From 27989ac66d6ce2d29d64aa09c6c1af4a4a52b230 Mon Sep 17 00:00:00 2001
From: cronn Bot <no-reply@cronn.de>
Date: Thu, 7 Nov 2024 07:46:55 +0000
Subject: [PATCH] Update v1.2.0

---
 .gitignore                                    |    6 +
 admin-portal/package.json                     |   63 +-
 admin-portal/src/app/layout.tsx               |    3 +
 .../java/de/eshg/base/contact/ContactApi.java |    5 +
 .../api/GetMergedContactsResponse.java        |   16 +
 .../de/eshg/base/feature/BaseFeature.java     |    1 +
 .../base/testhelper/BaseTestHelperApi.java    |    3 +
 .../de/eshg/base/user/api/UserRoleDto.java    |    2 +
 backend/base/build.gradle                     |    2 +-
 backend/base/gradle.lockfile                  |   31 +-
 backend/base/openApi.yaml                     |   51 +
 .../persistence/FacilityService.java          |   16 +-
 .../repository/FacilityRepository.java        |    6 +-
 .../eshg/base/contact/ContactController.java  |    6 +
 .../contact/persistence/ContactService.java   |    4 +
 .../repository/ContactRepository.java         |    3 +
 .../CitizenKeycloakTestProvisioning.java      |    9 +-
 .../base/keycloak/EmployeeKeycloakClient.java |   23 +-
 .../EmployeeKeycloakProvisioning.java         |    7 +-
 .../keycloak/InitialKeycloakProvisioning.java |    3 +
 .../base/keycloak/KeycloakProvisioning.java   |    7 +-
 .../keycloak/MasterKeycloakProvisioning.java  |    8 +-
 .../keycloak/RealmBoundKeycloakClient.java    |   18 +-
 .../testhelper/BaseTestHelperController.java  |    5 +
 .../testhelper/BaseTestHelperService.java     |   42 +-
 .../de/eshg/base/user/mapper/UserMapper.java  |    6 +
 .../resources/application-local.properties    |    1 +
 .../application-preview-features.properties   |    2 +-
 .../src/main/resources/application.properties |    1 -
 backend/build.gradle                          |   14 +-
 ...gMigrateAutoIncrementToSequenceChange.java |   26 +-
 .../eshg/MigrationIntegrationTestTraits.java  |    3 -
 backend/compliance-test/gradle.lockfile       |   31 +-
 backend/docker-compose.yaml                   |   16 +-
 .../file/common/PdfAConformanceValidator.java |   16 +-
 backend/inspection/build.gradle               |    2 +
 backend/inspection/gradle.lockfile            |   16 +-
 backend/inspection/openApi.yaml               |  402 +-
 .../checklist/ChecklistService.java           |    4 -
 .../checklist/MediaFileService.java           |    2 -
 .../inspection/facility/FacilityClient.java   |   32 +-
 .../facility/FacilityController.java          |    7 -
 .../inspection/facility/FacilityMapper.java   |  100 +-
 .../inspection/facility/FacilityService.java  |   57 +-
 .../GetPendingFacilitiesFilterOptionsDto.java |    3 +-
 .../api/InsPendingFacilityInspectionDto.java  |    3 +-
 ...InspPendingFacilitiesOverviewResponse.java |    3 +-
 .../facility/api/InspPendingFacilityDto.java  |    3 +-
 .../facility/persistence/Facility.java        |   11 +
 .../persistence/FacilityRepository.java       |    4 +
 .../inspection/feature/InspectionFeature.java |    3 +-
 .../inspection/importer/ImportInspection.java |   13 +
 .../importer/ImportInspectionFacility.java    |   13 +
 .../inspection/importer/ImportPersister.java  |  168 +
 .../importer/ImporterController.java          |   86 +
 .../inspection/importer/ImporterService.java  |   72 +
 .../importer/InspectionImporter.java          |  279 ++
 .../importer/InspectionImporterRowValues.java |   39 +
 .../importer/InspectionListColumn.java        |   62 +
 .../InspectionProcedureRowReader.java         |  176 +
 .../inspection/InspectionController.java      |   56 +-
 .../inspection/InspectionFinalizer.java       |    5 +-
 .../inspection/InspectionMapper.java          |   50 +-
 .../inspection/InspectionService.java         |    6 -
 .../inspection/inspection/ReviewService.java  |  328 ++
 .../api/FacilityDuplicateReviewDto.java       |   16 +
 .../api/FacilityForDuplicateReviewDto.java    |   28 +
 .../inspection/api/InspectionDto.java         |    4 +-
 .../api/InspectionDuplicateReviewDto.java     |   16 +
 .../api/InspectionForDuplicateReviewDto.java  |   20 +
 .../inspection/api/InspectionType.java        |    3 +-
 ...a => ResolveFacilityDuplicateRequest.java} |    7 +-
 .../ResolveInspectionDuplicateRequest.java    |   12 +
 .../inspection/persistence/Inspection.java    |   14 +
 .../persistence/InspectionRepository.java     |   48 +
 .../packlist/PacklistController.java          |   10 +-
 .../PacklistDefinitionController.java         |   14 +-
 .../report/InspectionReportService.java       |   12 +-
 .../application-preview-features.properties   |    2 +-
 ..._add_system_progress_entry_keydocument.xml |   14 +
 .../migrations/0046_cemetery_sequence.xml     |   11 +
 .../0047_inspection_import_merge.xml          |   39 +
 ...ject_and_message_text_to_mail_metadata.xml |   46 +
 .../0049_add_gdpr_validation_task.xml         |   43 +
 .../main/resources/migrations/changelog.xml   |    5 +
 .../import/InspectionImportTemplate.xlsx      |  Bin 0 -> 9547 bytes
 backend/keycloak/Dockerfile                   |    4 +-
 backend/keycloak/build.gradle                 |    2 +-
 backend/keycloak/gradle.lockfile              |  127 +-
 .../authenticator/AccessCodeForm.java         |    4 +-
 .../AbstractBaseModuleAggregationHelper.java  |   42 -
 .../aggregation/AbstractBaseModuleClient.java |   16 -
 .../client/BaseClientAutoConfiguration.java   |    6 +
 .../de/eshg/base/client/ContactClient.java    |   11 +
 .../lib/keycloak/EmployeePermissionRole.java  |   11 +-
 .../eshg/lib/keycloak/EmployeeTestUser.java   |    8 +
 .../eshg/lib/keycloak/ModuleMemberGroup.java  |    3 +-
 backend/lib-lsd-api/build.gradle              |    2 +-
 backend/lib-lsd-api/gradle.lockfile           |   37 +-
 .../testcontainers/LsdTestContainerUtil.java  |    6 +-
 .../procedure/api/GdprValidationTaskApi.java  |   25 +
 .../procedure/api/ProcedureMetricsApi.java    |    2 +-
 .../CreateManualProgressEntryRequest.java     |    2 -
 .../model/FindProceduresRequest.java          |   13 +
 .../model/FindProceduresResponse.java         |   13 +
 .../model/GetProgressEntryResponse.java       |    2 +-
 .../KeyDocumentAwareProgressEntryDto.java     |   19 +
 .../lib/procedure/model/MailMetaDataDto.java  |   18 +
 .../model/ManualProgressEntryDto.java         |   22 +-
 .../PatchManualProgressEntryRequest.java      |    2 -
 .../model/SystemProgressEntryDto.java         |   23 +-
 .../gdpr/AddGdprValidationTaskRequest.java    |   12 +
 .../model/gdpr/GdprValidationTaskTypeDto.java |   14 +
 backend/lib-procedures/build.gradle           |    1 +
 backend/lib-procedures/openApi.yaml           |   77 +-
 .../lib/procedure/domain/model/Cemetery.java  |    4 +-
 .../eshg/lib/procedure/domain/model/File.java |    2 +-
 .../lib/procedure/domain/model/FileAware.java |   10 -
 .../domain/model/GdprValidationTask.java      |   98 +
 .../model/GdprValidationTaskStatus.java       |   11 +
 .../domain/model/GdprValidationTaskType.java  |   11 +
 .../domain/model/InboxProgressEntry.java      |    5 -
 .../domain/model/KeyDocumentAware.java        |   17 +
 .../eshg/lib/procedure/domain/model/Mail.java |    6 +
 .../procedure/domain/model/MailMetaData.java  |   24 +
 .../domain/model/ManualProgressEntry.java     |   34 +-
 .../model/ProcessedInboxProgressEntry.java    |    7 +-
 .../procedure/domain/model/ProgressEntry.java |    4 +-
 .../domain/model/SystemProgressEntry.java     |   34 +-
 .../eshg/lib/procedure/domain/model/Task.java |    8 +
 .../domain/repository/CemeteryRepository.java |    3 +
 .../GdprValidationTaskRepository.java         |   18 +
 .../ManualProgressEntryRepository.java        |   28 -
 .../repository/ProcedureRepository.java       |   26 +-
 .../repository/ProgressEntryRepository.java   |   32 +
 .../de/eshg/lib/procedure/file/EmlParser.java |   56 +-
 .../lib/procedure/file/FileAwareResolver.java |   49 -
 .../eshg/lib/procedure/file/FileFactory.java  |   38 +-
 .../procedure/file/FileStorageService.java    |   17 -
 .../lib/procedure/file/FileUploadService.java |  116 -
 .../procedure/file/FileUploadValidator.java   |   69 -
 .../file/ImageMetaDataExtractor.java          |    4 +-
 .../procedure/file/MultipartFileParser.java   |   92 +
 .../eshg/lib/procedure/file/ParsedMail.java   |   92 -
 .../procedure/file/PdfMetaDataExtractor.java  |    4 +-
 .../gdpr/GdprValidationTaskController.java    |   87 +
 .../gdpr/GdprValidationTaskService.java       |   44 +
 .../housekeeping/archiving/ArchivingJob.java  |    2 +-
 .../cemetery/CemeteryHousekeeping.java        |   45 +
 .../CemeteryHousekeepingConfiguration.java    |   15 +
 .../CemeteryHousekeepingProperties.java       |   26 +
 .../inbox/InboxProcedureController.java       |   40 +-
 .../lib/procedure/mapping/FileMapper.java     |    2 +
 .../mapping/GdprValidationTaskMapper.java     |   35 +
 .../mapping/ProgressEntryMapper.java          |    6 +-
 .../procedures/ProcedureSearchService.java    |   34 +-
 .../ProgressEntryController.java              |   33 +-
 .../progressentry/ProgressEntryService.java   |  158 +-
 .../ProcedureLibraryAutoConfiguration.java    |   14 +-
 .../lib-procedure-test-helper.properties      |    6 +
 .../lib/procedure/util/CemeteryTestUtils.java |    6 +
 .../service/security/config/BaseUrls.java     |    2 +
 .../AbstractPublicSecurityConfiguration.java  |    4 +
 .../config/BasePublicSecurityConfig.java      |    7 +-
 .../InspectionPublicSecurityConfig.java       |    8 +
 .../SchoolEntryPublicSecurityConfig.java      |    2 +
 .../StatisticsPublicSecurityConfig.java       |   11 +-
 .../statistics/AbstractStatisticsService.java |    8 +-
 .../de/eshg/lib/xlsximport/ErrorHandler.java  |   13 +
 .../de/eshg/lib/xlsximport/ImportStatus.java  |    1 +
 .../eshg/lib/xlsximport/ImportValidator.java  |   16 +-
 .../java/de/eshg/lib/xlsximport/Importer.java |    7 +-
 .../de/eshg/lib/xlsximport/RowReader.java     |  136 +-
 .../de/eshg/lib/xlsximport/MultipartUtil.java |   11 +-
 .../lib/xlsximport/XlsxAssertionTraits.java   |   38 +-
 backend/local-service-directory/build.gradle  |    3 +-
 .../local-service-directory/gradle.lockfile   |   53 +-
 .../lsd/keycloak/KeycloakProvisioning.java    |    9 +-
 backend/measles-protection/openApi.yaml       |   77 +-
 .../AccessRestrictionService.java             |    2 -
 .../OrganisationPortalController.java         |   13 +-
 .../pdf/coverletter/CoverLetterService.java   |    4 +-
 .../src/main/resources/application.properties |    2 +
 ..._add_system_progress_entry_keydocument.xml |   14 +
 .../migrations/0029_cemetery_sequence.xml     |   11 +
 ...ject_and_message_text_to_mail_metadata.xml |   46 +
 .../0031_add_gdpr_validation_task.xml         |   43 +
 .../main/resources/migrations/changelog.xml   |    4 +
 .../templates/coverletters/end-of-letter.ftlx |    2 +-
 backend/medical-registry/build.gradle         |    1 +
 backend/medical-registry/openApi.yaml         |  436 +-
 .../MedicalRegistryController.java            |  107 +-
 .../MedicalRegistryService.java               |  259 +-
 .../de/eshg/medicalregistry/Validator.java    |   16 +
 .../eshg/medicalregistry/api/AddressDto.java  |   16 -
 .../api/CreateProcedureRequest.java           |   10 +-
 .../api/GetMedicalRegistryEntryOverview.java  |   19 +
 ...edicalRegistryProceduresFilterOptions.java |   37 +
 .../api/GetProcedureConfirmedResponse.java    |   21 +
 .../api/GetProcedureDraftResponse.java        |   22 +
 .../api/GetProcedureResponse.java             |   35 +-
 .../api/MedicalRegistryEntryDto.java          |   26 +
 .../api/PracticeAddressDto.java               |   17 +
 .../eshg/medicalregistry/api/PracticeDto.java |   30 +-
 .../api/ProfessionalAddressDto.java           |   19 +
 .../medicalregistry/api/ProfessionalDto.java  |   55 +-
 .../medicalregistry/api/TypeOfChangeDto.java  |   20 +
 .../business/model/DocumentData.java          |   32 +
 .../business/model/ProcedureDetailsData.java  |    8 -
 .../domain/model/TypeOfChange.java            |    1 -
 .../medicalregistry/mapper/AddressMapper.java |   33 +-
 .../medicalregistry/mapper/EntryMapper.java   |   42 +
 .../mapper/PracticeMapper.java                |    3 +-
 .../mapper/ProcedureMapper.java               |  110 +
 .../mapper/ProfessionalMapper.java            |   29 +-
 .../MedicalRegistryTestHelperController.java  |    6 +
 .../MedicalRegistryTestHelperService.java     |    6 +
 ...ion-health-department-frankfurt.properties |    1 +
 .../src/main/resources/application.properties |    3 -
 backend/school-entry/openApi.yaml             |  144 +-
 .../de/eshg/schoolentry/LabelService.java     |   60 +
 .../de/eshg/schoolentry/ProceduresHelper.java |    2 +-
 .../SchoolEntryCitizenController.java         |   57 +-
 .../SchoolEntryCitizenService.java            |    7 +-
 .../schoolentry/SchoolEntryController.java    |   41 +-
 .../eshg/schoolentry/SchoolEntryService.java  |  341 +-
 .../java/de/eshg/schoolentry/Validator.java   |   16 +
 .../api/DownloadInvitationsBulkRequest.java   |   12 +
 .../schoolentry/api/ProcedureDetailsDto.java  |    4 +-
 .../anamnesis/DaycareAndSchoolInfoDto.java    |    4 +-
 .../api/citizen/CitizenAnamnesisDto.java      |   16 +
 .../CitizenMigrationBackgroundDto.java        |    6 +-
 .../api/citizen/GetOpeningHoursResponse.java  |   11 +
 .../business/model/ImportAnamnesisData.java   |    5 +-
 .../ImportCustodianDataWithProcedure.java     |   11 +
 .../model/ImportPastProcedureData.java        |   11 +-
 .../model/ImportVaccinationStatusData.java    |   30 +-
 .../business/model/ProcedureDetailsData.java  |    4 +-
 .../model/ResolvedMergeProcedureData.java}    |   10 +-
 .../eshg/schoolentry/client/PersonClient.java |   28 +-
 .../config/SchoolEntryFeature.java            |    3 +-
 .../config/SchoolEntryProperties.java         |   32 +
 .../schoolentry/domain/model/Anamnesis.java   |    9 +
 .../domain/model/SchoolEntryProcedure.java    |   10 +
 .../SchoolEntryProcedureRepository.java       |   19 +
 .../SchoolEntryProcedureSpecification.java    |    9 +-
 .../importer/CitizenListRowReader.java        |   11 +-
 .../importer/CitizenOrSchoolListImporter.java |  136 +-
 .../schoolentry/importer/ImportService.java   |   23 +-
 .../eshg/schoolentry/importer/ImportType.java |   16 +-
 .../importer/PastProcedureListColumn.java     |   60 +
 .../importer/PastProcedureListImporter.java   |   81 +-
 .../importer/PastProcedureListRowReader.java  |  670 ++-
 .../PastProcedureListRowValueMapper.java      |    6 +-
 .../importer/PastProcedureListRowValues.java  |   44 +
 .../importer/SchoolEntryImporter.java         |  178 +-
 .../importer/SchoolListRowReader.java         |   14 +-
 .../importer/SchoolListRowValueMapper.java    |    3 +-
 .../schoolentry/mapper/AnamnesisMapper.java   |    7 +-
 .../schoolentry/mapper/ProcedureMapper.java   |    4 +-
 .../pdf/invitation/InvitationGenerator.java   |    4 +-
 .../medicalreport/MedicalReportGenerator.java |    4 +-
 .../SchoolInfoLetterExaminationMapper.java    |    4 +-
 .../SchoolInfoLetterGenerator.java            |    4 +-
 .../SchoolInfoLetterValidator.java            |    4 +-
 .../SchoolEntryStatisticsService.java         |   99 +-
 .../SchoolEntryProceduresPopulator.java       |    4 -
 .../schoolentry/util/ProgressEntryUtil.java   |   35 +-
 ...ion-health-department-frankfurt.properties |    2 +
 .../application-preview-features.properties   |    4 +-
 .../src/main/resources/application.properties |   12 +-
 ..._add_system_progress_entry_keydocument.xml |   14 +
 .../migrations/0052_cemetery_sequence.xml     |   11 +
 .../0053_add_was_in_daycare_flag.xml          |   23 +
 ...ject_and_message_text_to_mail_metadata.xml |   46 +
 .../0055_add_gdpr_validation_task.xml         |   43 +
 .../main/resources/migrations/changelog.xml   |    5 +
 backend/settings.gradle                       |   32 +-
 backend/spatz/build.gradle                    |    4 +-
 backend/spatz/gradle.lockfile                 |   32 +-
 backend/statistics/openApi.yaml               |  161 +-
 .../EvaluationTemplateController.java         |    3 +-
 .../statistics/EvaluationTemplateService.java |   39 +-
 .../statistics/FilterTemplateController.java  |    7 +-
 .../statistics/StatisticsApplication.java     |    3 +-
 .../aggregation/DataAggregationService.java   |   28 +-
 .../DataSourceAggregationService.java         |   29 +-
 .../aggregation/DataSourceController.java     |    2 +-
 .../aggregation/DataSourceValidator.java      |   12 +-
 .../aggregation/EvaluationController.java     |   13 +-
 .../aggregation/EvaluationService.java        |   32 +-
 .../aggregation/ReportController.java         |    8 +-
 .../aggregation/ReportExecution.java          |    5 +-
 .../aggregation/ReportSeriesController.java   |   34 +-
 .../aggregation/ReportSeriesExecution.java    |   48 +
 .../aggregation/ReportSeriesService.java      |  165 +-
 .../statistics/aggregation/ReportService.java |   29 +-
 .../aggregation/StatisticController.java      |   38 +-
 .../aggregation/StatisticExecution.java       |   47 +-
 .../aggregation/StatisticExecutorService.java |   80 +
 .../aggregation/StatisticService.java         |  187 +-
 .../AddStatisticWithDataSourcesRequest.java   |    1 +
 .../statistics/api/GetStatisticsRequest.java  |   33 +
 .../de/eshg/statistics/api/StatisticInfo.java |    3 +
 .../statistics/api/StatisticStateDto.java     |    3 +-
 .../{ => datasource}/AvailableDataSource.java |    2 +-
 .../BaseDataSourceAttribute.java              |    2 +-
 .../BusinessDataAttribute.java                |    6 +-
 .../BusinessDataSourceAttribute.java          |    2 +-
 .../api/{ => datasource}/DataSourceDto.java   |    2 +-
 .../GetAvailableDataSourcesResponse.java      |    2 +-
 ...luationTemplateWithDataSourcesRequest.java |    2 +-
 .../BaseDataAttributeWithName.java            |    4 +-
 .../BusinessDataAttributeWithName.java        |    5 +-
 .../EvaluationTemplateDto.java                |    1 +
 .../AbstractUpdateReportSeriesRequest.java    |   39 -
 .../ActivateAutoReportSeriesRequest.java      |   27 -
 .../DeactivateAutoReportSeriesRequest.java    |   20 -
 ...NameAndDescriptionReportSeriesRequest.java |   22 -
 .../api/report/UpdateReportSeriesRequest.java |   10 +
 .../config/OriginalDataAccessConfig.java      |   89 +
 .../datatransfer/EvaluationTemplateData.java  |    2 +-
 .../IncompleteDeletionException.java          |   12 +
 .../export/DataExportController.java          |    7 +-
 .../statistics/export/DataExportService.java  |   10 +-
 .../mapper/EvaluationTemplateMapper.java      |   14 +-
 .../statistics/mapper/StatisticMapper.java    |    9 +-
 .../persistence/entity/Statistic.java         |    5 +
 .../repository/StatisticRepository.java       |    4 +-
 .../StatisticsTestHelperController.java       |    9 +-
 .../src/main/resources/application.properties |    3 +
 backend/sti-protection/build.gradle           |    2 +
 backend/sti-protection/gradle.lockfile        |    7 +-
 backend/sti-protection/openApi.yaml           |  243 +-
 .../stiprotection/AppointmentService.java     |   76 +-
 .../OverdueProceduresNotifier.java            |   80 +
 .../StiProtectionApplication.java             |    2 +
 .../StiProtectionProcedureController.java     |    8 +-
 .../StiProtectionProcedureService.java        |    7 +-
 .../stiprotection/WaitingRoomController.java  |   54 +
 .../stiprotection/WaitingRoomService.java     |   78 +
 .../api/StiProtectionProcedureDto.java        |    4 +-
 .../SexWorkMedicalHistoryDto.java             |    6 +
 .../GetWaitingRoomProceduresResponse.java     |   17 +
 .../api/waitingroom/WaitingRoomDto.java       |   12 +
 .../waitingroom/WaitingRoomProcedureDto.java  |   23 +
 ...mProcedurePaginationAndSortParameters.java |   18 +
 .../api/waitingroom/WaitingRoomSortKey.java   |   15 +
 .../api/waitingroom/WaitingStatusDto.java     |   19 +
 .../StiProtectionEventMetadataService.java    |   68 +-
 ...tiProtectionNotificationConfiguration.java |   21 +
 .../mapper/StiProtectionProcedureMapper.java  |    4 +-
 .../medicalhistory/MedicalHistoryMapper.java  |   10 +
 .../mapper/waitingroom/WaitingRoomMapper.java |   31 +
 .../WaitingRoomProcedureMapper.java           |   23 +
 .../waitingroom/WaitingStatusMapper.java      |   46 +
 ...nonymousIdentificationDocumentService.java |    4 +-
 .../data/StiProtectionProcedureData.java      |    4 +-
 .../db/StiProtectionProcedure.java            |   32 +
 .../db/StiProtectionProcedureRepository.java  |   15 +-
 .../medicalhistory/SexWorkMedicalHistory.java |   51 +
 .../db/waitingroom/WaitingRoom.java           |   73 +
 .../waitingroom/WaitingRoomSpecification.java |   81 +
 .../db/waitingroom/WaitingStatus.java         |   16 +
 .../StiProtectionTestHelperController.java    |   13 +-
 .../src/main/resources/application.properties |    2 +
 ..._add_system_progress_entry_keydocument.xml |   14 +
 .../migrations/0009_add_calendar_event_id.xml |   16 +
 .../migrations/0010_cemetery_sequence.xml     |   11 +
 .../resources/migrations/0011_shedlock.xml    |   28 +
 .../migrations/0012_add_waiting_room.xml      |   29 +
 .../0013_extend_medical_histories.xml         |   33 +
 ...ject_and_message_text_to_mail_metadata.xml |   46 +
 .../0015_add_gdpr_validation_task.xml         |   43 +
 .../main/resources/migrations/changelog.xml   |    8 +
 .../eshg/testhelper/DatabaseResetHelper.java  |   90 +-
 .../testhelper/population/BasePopulator.java  |    7 -
 .../PopulateWithAccessTokenHelper.java        |    2 +-
 backend/travel-medicine/openApi.yaml          |  549 ++-
 .../TravelMedicineApplication.java            |    2 +
 .../certificate/CertificateService.java       |   13 +-
 .../citizenauth/CitizenAuthController.java    |   51 +-
 .../CitizenPublicController.java              |   32 +-
 .../citizenpublic/OpeningHoursProperties.java |   12 +
 .../api/GetOpeningHoursResponse.java          |   11 +
 .../document/DocumentDtoHelper.java           |  136 +
 .../document/TemplateToDocumentMapper.java    |  135 +
 .../api/DocumentAnamnesisQuestionDto.java     |   19 +
 .../document/api/DocumentConfirmationDto.java |   15 +
 .../document/api/DocumentContentDto.java      |   16 +
 .../document/api/DocumentSectionDto.java      |   17 +
 .../api/DocumentSectionElementDto.java        |   40 +
 .../api/DocumentSubElementMultiSelectDto.java |   14 +
 .../api/DocumentSubElementTextDto.java        |   15 +
 .../document/api/DocumentTextBlockDto.java    |   12 +
 .../InformationStatementFactory.java          |   29 +
 .../InformationStatementMapper.java           |   19 +-
 .../InformationStatementService.java          |  154 +
 .../api/InformationStatementDto.java          |    6 +-
 .../InformationStatementRepository.java       |    3 +-
 .../entity/InformationStatement.java          |    3 +-
 .../MedicalHistoryController.java             |    6 +-
 .../medicalhistory/MedicalHistoryFactory.java |   28 +
 .../medicalhistory/MedicalHistoryMapper.java  |   22 +-
 .../medicalhistory/MedicalHistoryService.java |  115 +
 .../medicalhistory/api/MedicalHistoryDto.java |    5 +-
 .../api/PatchMedicalHistoryRequest.java       |    6 +-
 .../persistence/MedicalHistoryRepository.java |    4 +-
 .../persistence/entity/MedicalHistory.java    |    2 +-
 .../medicalhistory/MedicalHistoryHelper.java  |  113 -
 .../medicalhistory/MedicalHistoryService.java |   62 -
 .../api/MedicalHistoryContentDto.java         |   18 -
 .../api/MedicalHistorySectionDto.java         |   19 -
 .../MedicalHistorySectionElementDataDto.java  |   22 -
 .../api/MedicalHistorySectionElementDto.java  |   18 -
 ...edicalHistorySubElementMultiSelectDto.java |   16 -
 .../api/MedicalHistorySubElementTextDto.java  |   17 -
 .../notification/NotificationService.java     |  143 +-
 .../notification/NotificationText.java        |  211 +-
 ...java => TemplateAnamnesisQuestionDto.java} |    5 +-
 .../template/api/TemplateConfirmationDto.java |   14 +
 .../api/TemplateSectionElementDto.java        |   42 +-
 .../api/TemplateSubElementMultiSelectDto.java |    3 +-
 .../api/TemplateSubElementTextDto.java        |    6 +-
 .../template/api/TemplateTextBlockDto.java    |   13 +
 .../MedicalHistoryTemplateMapper.java         |    2 +-
 .../CreateMedicalHistoryTemplateTask.java     |    2 +-
 .../testhelper/TestHelperUtil.java            |   68 +
 .../TestPopulateAdministrativeService.java    |   81 +-
 .../TestPopulateProcedureService.java         |  123 +-
 .../InformationStatementPopulationDto.java    |   15 +
 .../api/PostPopulateProcedureRequest.java     |    5 +-
 .../api/PostPopulateProcedureResponse.java    |    1 +
 .../AppointmentDetailsMapper.java             |    8 +-
 .../CitizenAccessCodeUserClient.java          |    4 +
 .../InformationStatementSummaryMapper.java    |   23 +
 .../vaccinationconsultation/PersonClient.java |  171 +-
 .../ProcedureAccessor.java                    |   18 +-
 .../ProcedureStepController.java              |    2 +-
 .../ProcedureStepService.java                 |   66 +-
 .../VaccinationConsultationController.java    |   52 +-
 .../VaccinationConsultationDetailsMapper.java |   17 +-
 .../VaccinationConsultationMapper.java        |    4 +-
 .../VaccinationConsultationService.java       |  235 +-
 .../api/GetAppointmentDetailsResponse.java    |    4 +-
 .../api/GetInformationStatementsResponse.java |   16 +
 .../api/GetMedicalHistoriesResponse.java      |    2 +-
 ...accinationConsultationDetailsResponse.java |    3 +-
 .../api/InformationStatementSummaryDto.java   |   17 +
 .../api/PatchAcceptDraftRequest.java          |   10 +
 .../persistence/entity/ProcedureStep.java     |    2 +-
 .../entity/VaccinationConsultation.java       |    4 +-
 .../VaccinationConsultationRepository.java    |    5 +-
 ...ion-health-department-frankfurt.properties |   40 +
 .../src/main/resources/application.properties |   44 +
 .../resources/initial_medical_history.json    |  119 +-
 .../0036_delete_templates_and_documents.xml   |   19 +
 ..._add_system_progress_entry_keydocument.xml |   14 +
 .../migrations/0038_cemetery_sequence.xml     |   11 +
 ...ject_and_message_text_to_mail_metadata.xml |   46 +
 .../0040_add_gdpr_validation_task.xml         |   43 +
 .../main/resources/migrations/changelog.xml   |    5 +
 .../default/de/booking_by_citizen.txt         |   11 +
 .../default/de/booking_by_employee.txt        |   11 +
 .../default/de/cancellation_by_citizen.txt    |    9 +
 .../default/de/cancellation_by_employee.txt   |    9 +
 .../default/de/new_citizen_procedure.txt      |   16 +
 .../default/de/new_follow_up_appointment.txt  |    9 +
 .../default/de/new_information_statement.txt  |    9 +
 .../default/de/rebooking_by_citizen.txt       |    9 +
 .../default/de/rebooking_by_employee.txt      |    9 +
 .../ga_frankfurt/de/booking_by_citizen.txt    |   11 +
 .../ga_frankfurt/de/booking_by_employee.txt   |   11 +
 .../de/cancellation_by_citizen.txt            |    9 +
 .../de/cancellation_by_employee.txt           |    9 +
 .../ga_frankfurt/de/new_citizen_procedure.txt |   16 +
 .../de/new_follow_up_appointment.txt          |    9 +
 .../de/new_information_statement.txt          |    9 +
 .../ga_frankfurt/de/rebooking_by_citizen.txt  |    9 +
 .../ga_frankfurt/de/rebooking_by_employee.txt |    9 +
 build.gradle                                  |   36 +-
 .../src/main/groovy/next-loading-files.gradle |   62 +
 buildSrc/src/main/groovy/node.gradle          |    2 +-
 citizen-portal/.gitignore                     |    4 +
 citizen-portal/build.gradle                   |    5 +
 citizen-portal/package.json                   |   72 +-
 .../masernschutz/meldeformular/page.tsx       |    2 +-
 citizen-portal/src/app/loading.template.tsx   |    8 +
 .../components/layout/AppLayout.tsx           |    4 +
 .../api/queries/schoolEntryCitizenApi.ts      |    9 +
 .../schoolEntry/locales/de/anamnesis.json     |    3 +-
 .../schoolEntry/locales/en/anamnesis.json     |    3 +-
 .../UpdateAppointmentForm.tsx                 |    6 +-
 .../citizenAnamnesis/CitizenAnamnesisForm.tsx |   30 +-
 .../steps/CitizenAnamnesisStepFour.tsx        |    1 -
 .../steps/CitizenAnamnesisStepTwo.tsx         |   45 +-
 .../pages/landingpage/LandingpageContent.tsx  |   33 +-
 .../api/mutations/citizenAuthApi.ts           |    4 +-
 .../api/queries/citizenPublicApi.ts           |   19 +
 .../appointment/AppointmentFormButtonBar.tsx  |   62 +-
 .../appointment/AppointmentFormContent.tsx    |   22 -
 .../appointment/AppointmentFormWrapper.tsx    |   57 -
 .../appointment/AppointmentStepper.tsx        |   90 +-
 .../OverviewAndAppointmentStepToggle.tsx      |   25 -
 .../appointmentFormValuesFactory.ts           |    3 +-
 .../appointment/steps/AppointmentTypeStep.tsx |   33 +-
 .../appointment/steps/VaccinationStep.tsx     |   31 +-
 .../AppointmentContent.tsx                    |    3 +-
 .../components/appointment/types.ts           |    2 +-
 .../components/landing/AppointmentSection.tsx |   38 +-
 .../components/landing/LandingpageContent.tsx |   48 +-
 .../MedicalHistorySidePanel.tsx               |    4 +-
 .../medicalHistory/MedicalHistoryStepper.tsx  |    4 +-
 ...umentElement.tsx => AnamnesisQuestion.tsx} |   28 +-
 .../document/ConfirmationElement.tsx          |   32 +
 .../document/DocumentMultiSelectElement.tsx   |   18 +-
 .../document/DocumentRadioButtonElement.tsx   |   18 +-
 .../components/document/DocumentSection.tsx   |   39 +-
 .../shared/components/document/TextBlock.tsx  |   14 +
 .../shared/contexts/StepContext.tsx           |  107 +-
 ...ppointmentDetailsAdditionalInformation.tsx |    4 +-
 .../AppointmentDetailsSidePanel.tsx           |    2 +-
 .../helpers/appointmentFormHelper.ts          |    2 +-
 .../locales/de/appointmentDetails.json        |    3 +-
 .../travelMedicine/locales/de/forms.json      |   17 +-
 .../travelMedicine/locales/de/landing.json    |    8 -
 .../locales/en/appointmentDetails.json        |    3 +-
 .../travelMedicine/locales/en/forms.json      |   17 +-
 .../travelMedicine/locales/en/landing.json    |    8 -
 citizen-portal/src/lib/i18n/client.ts         |    4 +-
 .../src/lib/shared/components/layout/page.tsx |    5 +-
 config/eslint.base.js                         |    8 +
 docs/dependency-management.adoc               |   49 +-
 docs/queries-and-mutations.adoc               |   22 +-
 employee-portal/.env                          |    1 +
 employee-portal/.gitignore                    |    7 +-
 employee-portal/build.gradle                  |   65 +-
 .../markdown/common/release-notes.md          |   22 +
 employee-portal/package.json                  |   88 +-
 .../(baseModule)/account/sessions/page.tsx    |    4 +-
 .../(baseModule)/inbox-procedures/page.tsx    |   20 +-
 .../app/(baseModule)/resources/[id]/page.tsx  |    2 +-
 .../inspection/teamview/page.tsx              |    2 -
 .../medical-registry/procedures/page.tsx      |   21 +
 .../procedures/[id]/anamnesis/page.tsx        |   18 +-
 .../development-screening/page.tsx            |   12 +-
 .../procedures/[id]/examinations/ear/page.tsx |    8 +-
 .../procedures/[id]/examinations/eye/page.tsx |    8 +-
 .../[id]/examinations/sopess/page.tsx         |    8 +-
 .../[fileStateId]/[personVersion]/page.tsx    |   22 +-
 .../procedures/[id]/vaccination/page.tsx      |    6 +-
 .../statistics/evaluation-templates/page.tsx  |   27 +
 .../statistics/statistics/page.tsx            |   24 +-
 .../procedures/[id]/@tabs/anamnesis/page.tsx  |   16 +-
 .../[id]/information-statements/page.tsx      |   23 +
 .../[fileStateId]/[personVersion]/page.tsx    |   22 +-
 .../travel-medicine/procedure/page.tsx        |   12 +-
 employee-portal/src/app/layout.tsx            |    3 +
 .../playground/appointment-picker/page.tsx    |    1 -
 .../src/app/playground/charts/page.tsx        |  970 ++++-
 .../src/app/playground/formPlus/page.tsx      |    2 +-
 employee-portal/src/env/server.js             |    1 +
 .../AuditlogAccessibleTableView.tsx           |   14 +-
 .../AuditlogCreatePasswordSidebar.tsx         |    9 +-
 .../AuditlogDeletePasswordButton.tsx          |    2 +-
 .../authorize/AuditLogAuthorizePage.tsx       |   16 +-
 .../authorize/AuditLogAuthorizeSidebar.tsx    |   30 +-
 .../authorize/UserAutoCompleteField.tsx       |    3 -
 .../calendar/sidebar/AddAbsenceSidebar.tsx    |   24 +-
 .../components/contacts/ContactDetails.tsx    |  240 +-
 .../components/contacts/ContactsOverview.tsx  |   82 +-
 .../components/contacts/ContactsTable.tsx     |   24 +-
 .../components/contacts/columns.tsx           |    1 -
 .../contacts/forms/ContactEntityForm.tsx      |   16 +-
 .../import/InstitutionContactImportForm.tsx   |   18 +-
 .../forms/import/PersonContactImportForm.tsx  |   18 +-
 .../merge/MergeInstitutionContactForm.tsx     |   20 +-
 .../forms/merge/MergePersonContactForm.tsx    |   20 +-
 .../modals/AddInstitutionContactSidebar.tsx   |   39 +-
 .../modals/AddPersonContactSidebar.tsx        |   39 +-
 .../contacts/modals/UpdateContactSidebar.tsx  |   51 +-
 .../dashboard/DashboardProceduresTable.tsx    |   16 +-
 .../overview/CreateGDPRProcedureSidebar.tsx   |  220 +-
 .../components/gdpr/overview/GDPRTable.tsx    |   92 +-
 .../LinkCentralFileSidebar.tsx                |   16 +-
 .../sidebars/EditMatterOfConcernSidebar.tsx   |    8 +-
 .../procedure/tiles/ProcedureDetailsTile.tsx  |    6 +-
 .../components/inventory/InventoryTable.tsx   |    6 +-
 .../components/inventory/columns.tsx          |    1 -
 .../inventory/modals/AddInventorySidebar.tsx  |   14 +-
 .../modals/InventoryRestockSidebar.tsx        |    8 +-
 .../modals/InventoryUpdateSidebar.tsx         |    8 +-
 .../layout/header/HeaderButtons.tsx           |   25 +-
 .../sideNavigation/NavigationListExpanded.tsx |    4 +
 .../layout/sideNavigation/SideNavigation.tsx  |    9 +-
 .../components/layout/sideNavigation/types.ts |    5 +
 .../sideNavigation/useNavigationItems.ts      |   11 +-
 .../ProcedureMetricsDisplay.tsx               |   19 +-
 .../taskMetrics/TaskMetricsDisplay.tsx        |   17 +-
 .../components/resources/ResourceDetail.tsx   |  244 +-
 .../components/resources/ResourcesTable.tsx   |   28 +-
 .../resources/sidebar/AddResourceSidebar.tsx  |   52 +-
 .../resources/sidebar/AddServiceSidebar.tsx   |  161 +-
 .../resources/sidebar/EventsViewSidebar.tsx   |  115 +-
 .../sidebar/UpdateResourceSidebar.tsx         |   25 +-
 .../baseModule/components/task/TasksTable.tsx |   14 +-
 .../baseModule/components/task/Teamview.tsx   |   32 +-
 .../users/SuggestNewUserSidebar.tsx           |  254 +-
 .../components/users/UserProfileDetails.tsx   |   28 +-
 .../users/UserProfileEditSidebar.tsx          |   41 -
 .../baseModule/components/users/UserTable.tsx |   28 +-
 ...ditForm.tsx => UserProfileEditSidebar.tsx} |   60 +-
 .../users/userSidebar/UserSidebarHeader.tsx   |   25 +-
 .../sideNavigationItemsResolver.tsx           |   62 +-
 .../lib/baseModule/sideNavigationItems.tsx    |    9 +-
 .../src/lib/baseModule/theme/theme.ts         |   14 +
 .../businessModules/chat/components/Chat.tsx  |    5 +-
 .../chat/components/ChatAvatar.tsx            |    2 +-
 .../chat/components/ChatBubble.tsx            |   87 -
 .../chat/components/ChatHeader.tsx            |  107 -
 .../components/ChatIllustrationBackground.tsx |    2 +-
 .../chat/components/OnlineStatus.tsx          |   16 +-
 .../chat/components/UsersAutocomplete.tsx     |   24 +-
 .../chat/components/chatPanel/ChatBubble.tsx  |   13 +-
 .../chat/components/chatPanel/ChatHeader.tsx  |   63 +
 .../components/chatPanel/ChatMessages.tsx     |   25 +-
 .../chat/components/chatPanel/ChatPanel.tsx   |    8 +-
 .../components/chatPanel/ChatPanelHeader.tsx  |   61 +-
 .../components/chatPanel/InputComponent.tsx   |   22 +-
 .../components/chatPanel/NewDirectChat.tsx    |   27 +-
 .../components/chatPanel/NewGroupChat.tsx     |    2 +-
 .../chat/components/chatPanel/UserList.tsx    |   93 +
 .../components/infoPanel/AddChatMember.tsx    |  105 +-
 .../components/infoPanel/AdminSettings.tsx    |   12 +-
 .../components/infoPanel/AssignAdminView.tsx  |   92 +-
 .../components/infoPanel/InfoPanelHeader.tsx  |   67 +-
 .../components/infoPanel/MemberInfoView.tsx   |    9 +-
 .../chat/components/infoPanel/RenameChat.tsx  |    5 +-
 .../chat/components/infoPanel/RoomAvatar.tsx  |   16 +-
 .../components/infoPanel/RoomInfoView.tsx     |   23 +-
 .../chat/shared/hooks/useChatLifecycle.tsx    |    5 +-
 .../chat/shared/hooks/useChatRoomList.tsx     |    6 +-
 .../shared/hooks/useGetSelfUserPresence.tsx   |    7 +-
 .../chat/shared/hooks/useNewMessages.tsx      |    1 +
 .../chat/shared/hooks/useRoomInfo.ts          |  106 +-
 .../chat/shared/hooks/useRoomMessages.tsx     |   77 +-
 .../chat/shared/sideNavigationItem.tsx        |   13 +-
 .../lib/businessModules/chat/shared/types.ts  |    7 +-
 .../lib/businessModules/chat/shared/utils.ts  |  108 +-
 .../businessModules/inspection/api/clients.ts |    6 +
 .../inspection/api/mutations/inspection.ts    |   18 +
 .../inspection/api/mutations/processImport.ts |   52 +
 .../inspection/api/queries/facility.ts        |   28 +-
 .../inspection/api/queries/inspection.ts      |   30 +
 .../inspection/api/queries/resources.ts       |   60 +-
 .../elements/ChecklistDefinitionElement.tsx   |   32 +-
 .../ChecklistDefinitionOverviewTable.tsx      |    6 +-
 .../facility/pending/DuplicateTileLine.tsx    |   56 +
 .../pending/FacilityDuplicateTile.tsx         |   86 +
 .../pending/InspectionDuplicateTile.tsx       |   92 +
 .../LineWithPossibleExclamationMark.tsx       |   30 +
 .../facility/pending/NewFacilityButton.tsx    |    4 +-
 .../pending/PendingFacilitiesOfflineTable.tsx |    9 +-
 .../pending/PendingFacilitiesTable.tsx        |   84 +-
 .../pending/PotentialDuplicatesWarning.tsx    |   61 +
 .../ReviewFacilityDuplicateSidebar.tsx        |  113 +
 .../ReviewInspectionDuplicateSidebar.tsx      |  122 +
 .../components/facility/pending/columns.tsx   |   41 +-
 .../search/FacilityWebSearchTable.tsx         |    6 +-
 .../components/icons/DuplicateIcon.tsx        |   33 +
 .../FinalizeInspectionModalContent.tsx        |    4 +-
 .../execution/InspectionTabExecution.tsx      |    5 +-
 .../checklist/form/ChecklistFileElement.tsx   |    2 +-
 .../form/ChecklistSectionElement.tsx          |    2 +-
 .../planning/InspectionTabPlanning.tsx        |   21 +-
 .../InspectionTabReportResult.tsx             |    1 +
 .../processImport/ProcessImportButton.tsx     |   33 +
 .../processImport/ProcessImportForm.tsx       |   90 +
 .../processImport/ProcessImportPending.tsx    |   27 +
 .../processImport/ProcessImportResult.tsx     |  154 +
 .../processImport/ProcessImportSidebar.tsx    |   55 +
 .../ChecklistDefinitionRepoOverviewTable.tsx  |    6 +-
 .../textBlock/EditTextBlockSidebar.tsx        |    4 +-
 .../components/textBlock/TextBlocksTable.tsx  |    2 +-
 .../inspection/shared/enums.ts                |    6 +
 .../shared/offline/RegisterServiceWorker.tsx  |   16 +-
 .../shared/offline/ServiceWorkerProvider.tsx  |    6 +
 .../offline/unregisterServiceWorker.tsx       |   15 +
 .../shared/offline/usePrecacheInspections.ts  |   37 +-
 .../inspection/shared/sideNavigationItem.tsx  |   44 +-
 .../api/mutations/procedures.ts               |    4 +-
 .../CreateAppointmentBlockGroupForm.tsx       |    9 +-
 .../createProceduresForm/NewPersonButton.tsx  |    8 +-
 .../AccessRestrictionLetterSidebar.tsx        |   18 +-
 .../procedureDetails/AddCustodianSidebar.tsx  |   10 +-
 .../AdditionalInfoSection.tsx                 |   68 +-
 .../procedureDetails/AddressDetails.tsx       |   34 +-
 .../procedureDetails/AffectedPerson.tsx       |   23 +-
 .../procedureDetails/AppointmentSidebar.tsx   |   40 +-
 .../procedureDetails/Custodians.tsx           |   26 +-
 .../EditAccessRestrictionSidebar.tsx          |    6 +-
 .../procedureDetails/EditFacilitySidebar.tsx  |    9 +-
 .../procedures/procedureDetails/Facility.tsx  |   59 +-
 .../procedureDetails/FacilityContact.tsx      |   57 +-
 .../procedureDetails/NewCustodianButton.tsx   |   26 +-
 .../procedureDetails/NewFacilityButton.tsx    |   26 +-
 .../procedureDetails/NewFacilitySidebar.tsx   |    4 +-
 .../procedures/procedureDetails/ProofTab.tsx  |  195 +-
 .../UpdateProcedureSection.tsx                |   14 +-
 .../proof/AccessRestrictionCard.tsx           |  143 +-
 .../proof/AppointmentCard.tsx                 |   95 +-
 .../procedureDetails/proof/ProofTabEntry.tsx  |   11 +-
 .../proceduresTable/ProceduresTable.tsx       |    9 +-
 .../shared/sideNavigationItem.tsx             |    6 +-
 .../medicalRegistry/api/clients.ts            |   22 +
 .../api/queries/apiQueryKeys.ts               |   12 +
 .../api/queries/medicalRegistryEntries.ts     |   42 +
 .../MedicalRegistryProcedureChip.tsx          |   35 +
 .../MedicalRegistryProceduresSearchBar.tsx    |   25 +
 .../MedicalRegistryProceduresTable.tsx        |  142 +
 .../medicalRegistry/shared/routes.ts          |    4 +
 .../shared/sideNavigationItem.tsx             |   36 +
 .../api/models/ProcedureDetails.ts            |    4 +
 .../CreateAppointmentBlockGroupForm.tsx       |    8 +-
 .../features/labels/CreateLabelSidebar.tsx    |    6 +-
 .../features/labels/UpdateLabelSidebar.tsx    |    9 +-
 .../procedures/anamnesis/AnamnesisForm.tsx    |   37 +-
 .../BirthDataAndChildInformationForm.tsx      |   17 +-
 .../anamnesis/IllnessAndAccidentInfoForm.tsx  |   24 +-
 .../importData/ImportDataSidebar.tsx          |    2 +-
 .../procedures/new/CreateProcedureSidebar.tsx |    9 +-
 .../procedureDetails/DeleteProcedureModal.tsx |   15 +-
 .../procedureDetails/PersonDetailsPanel.tsx   |    8 +-
 .../ProcedureActionsPanel.tsx                 |   13 +-
 .../procedureDetails/ProcedureDetails.tsx     |   11 +-
 .../procedureDetails/ReopenProcedureModal.tsx |   15 +-
 .../UpdateProcedureSidebar.tsx                |    7 +
 .../procedureDetails/WaitingRoomPanel.tsx     |   16 +-
 .../proceduresTable/ProcedureTableTitle.tsx   |   14 +-
 .../proceduresTable/ProceduresTable.tsx       |    6 +-
 .../sopessExamination/FormSectionTitle.tsx    |   11 +-
 .../features/waitingRoom/WaitingRoomTable.tsx |    5 +-
 .../schoolEntry/shared/sideNavigationItem.tsx |   22 +-
 .../api/models/evaluationDetails.ts           |   13 +
 .../api/models/evaluationTemplatesOverview.ts |   24 +
 .../statistics/api/models/pageRequest.ts      |   21 +
 .../api/models/statisticDetailsViewTypes.ts   |    1 +
 .../api/models/statisticOverview.ts           |   27 +
 .../statistics/api/models/statisticReports.ts |    1 +
 .../api/mutations/useAddAutoReportSeries.ts   |    5 +-
 .../statistics/api/mutations/useAddDiagram.ts |    6 +-
 .../api/mutations/useAddEvaluation.ts         |    4 +-
 .../api/mutations/useAddEvaluationTemplate.ts |   43 +
 .../api/mutations/useAddFilterTemplate.ts     |    2 +-
 .../api/mutations/useAddGeoShape.ts           |    6 +-
 .../statistics/api/mutations/useAddReport.ts  |   11 +-
 .../api/mutations/useAddStatistic.ts          |    2 +-
 .../mutations/useDeactivateReportSeries.ts    |    9 +-
 .../api/mutations/useDuplicateStatistic.ts    |    2 +-
 .../api/mutations/useEditStatisticName.ts     |    2 +-
 .../mutations/useGetFilterTemplateFilters.ts  |    2 +-
 .../api/mutations/useUpdateDataBasis.ts       |   10 +-
 .../api/mutations/useUpdateDiagram.ts         |    5 +-
 .../api/mutations/useUpdateEvaluation.ts      |    2 +-
 .../api/mutations/useUpdateReport.ts          |   12 +-
 .../queries/useGetDetailPageInformation.ts    |    1 +
 .../api/queries/useGetEvaluationDetails.ts    |   45 +
 .../useGetEvaluationTemplatesOverview.ts      |   74 +
 .../api/queries/useGetStatisticReports.ts     |    1 +
 .../api/queries/useGetStatistics.ts           |   10 +-
 .../queries/useGetStatisticsOverviewPage.ts   |    6 +-
 .../components/reports/ReportDetails.tsx      |    1 +
 .../components/reports/ReportsOverview.tsx    |   12 +-
 .../components/shared/CollapsableList.tsx     |   36 +
 .../EvaluationAccordion.tsx                   |    6 +-
 .../EvaluationAccordionDetails.tsx            |    2 +
 .../EvaluationChartDiagram.tsx                |  131 +-
 .../EvaluationDiagramBox.tsx                  |  201 +-
 .../EvaluationSortOrderSelect.tsx             |   12 -
 .../shared/charts/ChoroplethMap.tsx           |   13 +-
 .../components/shared/charts/Histogram.tsx    |   14 +-
 .../SaveStatisticStep/SaveStatisticStep.tsx   |   11 +-
 .../SaveAsEvaluationTemplateSidebar.tsx       |   49 +
 .../SaveEvaluationTemplateStep.tsx            |   81 +
 .../saveEvaluationTemplateStepFormModel.ts    |    9 +
 .../saveAsEvaluationTemplateFormModel.ts      |    9 +
 .../components/statistics/StateChip.tsx       |    2 +
 .../statistics/StatisticsOverview.tsx         |   15 +-
 .../components/statistics/StatisticsTable.tsx |  211 +-
 .../details/BusinessModuleInformationCard.tsx |    5 +
 .../ConfigureHistogramChartStep.tsx           |    9 +-
 .../details/DetailsInformationCard.tsx        |   46 +-
 .../statistics/details/StatisticDetails.tsx   |   22 +-
 .../details/reports/StatisticReports.tsx      |   56 +-
 .../EvaluationTemplatesOverview.tsx           |  189 +
 .../statistics/useStatisticRoleChecks.ts      |    2 +
 .../statistics/shared/routes.ts               |    3 +
 .../statistics/shared/sideNavigationItem.tsx  |   82 +-
 .../api/queries/medicalHistory.ts             |   33 +
 .../CreateAppointmentBlockGroupForm.tsx       |    9 +-
 .../StiProtectionProceduresTable.tsx          |    9 +-
 .../addNewProcedure/AppointmentForm.tsx       |    2 -
 .../details/AdditionalDataSection.tsx         |   37 +-
 .../details/AnonIdentityDocumentCard.tsx      |   46 +
 .../procedures/details/PersonDetails.tsx      |   79 +-
 .../procedures/details/ProcedureDetails.tsx   |   18 +-
 .../medicalHistory/BooleanSelectDate.tsx      |   85 +
 .../MedicalHistoryForm.config.ts              |  178 +-
 .../medicalHistory/MedicalHistoryForm.tsx     |  403 +-
 .../procedures/medicalHistory/helpers.ts      |  106 +
 .../sections/MedicalHistoryCommonFields.tsx   |  142 +
 .../sections/SexualOrientationAndContact.tsx  |   70 +
 .../shared/sideNavigationItem.tsx             |    9 +-
 .../api/mutations/appointmentTypes.ts         |    4 +-
 .../api/mutations/vaccinationConsultation.ts  |   28 +
 .../api/queries/vaccinationConsultation.ts    |   25 +-
 .../appointment/InitialAppointmentForm.tsx    |    6 +-
 .../personSidebar/personSidebarHelper.ts      |   10 +-
 .../InformationStatementTemplateEditor.tsx    |    9 +-
 ...ormationStatementTemplateOverviewTable.tsx |   11 +-
 .../MedicalHistoryTemplateOverviewTable.tsx   |    9 +-
 .../VaccinationConsultationsSearchTable.tsx   |    8 +-
 ...nationConsultationTabNavigationToolbar.tsx |   21 +-
 .../VaccinationConsultationsOverviewTable.tsx |   24 +-
 .../baseData/AbortProcedureModal.tsx          |   55 +
 .../baseData/AcceptProcedureForm.tsx          |  150 +
 .../baseData/AcceptProcedureSidebar.tsx       |  110 +
 .../baseData/CloseProcedurePanel.tsx          |   86 -
 .../baseData/DetailsGrid.tsx                  |    3 +-
 .../baseData/InformationStatementsTable.tsx   |   85 -
 .../baseData/InitialAppointmentTile.tsx       |    4 +-
 .../baseData/PatientDetails.tsx               |  234 ++
 .../baseData/PatientPanel.tsx                 |    5 +-
 .../baseData/ProcedureActionsPanel.tsx        |  169 +
 .../VaccinationConsultationDetails.tsx        |   47 +-
 .../sidebars/AddServiceAppointmentSidebar.tsx |   16 +-
 .../sidebars/AddServicePlanSidebar.tsx        |   12 +-
 .../sidebars/AssignServiceSidebar.tsx         |    9 +-
 .../sidebars/EditEarliestDateSidebar.tsx      |   12 +-
 .../EditServiceAppointmentSidebar.tsx         |   16 +-
 .../sidebars/OtherServiceAppliedSidebar.tsx   |   20 +-
 .../sidebars/ServiceAppliedSidebar.tsx        |   20 +-
 .../AddServiceAppointmentForm.tsx             |    4 +-
 .../EditServiceAppointmentForm.tsx            |    4 +-
 ...ccinationConsultationCertificatesTable.tsx |    4 +-
 .../InformationStatementSidebar.tsx           |    9 +-
 .../InformationStatementsColumns.tsx          |   10 +-
 .../InformationStatementsTable.tsx            |  130 +
 .../medicalHistory/ConfirmationElement.tsx    |   37 +
 .../medicalHistory/MedicalHistory.tsx         |  144 +-
 .../MedicalHistoryMultiSelectElement.tsx      |   18 +-
 .../MedicalHistoryRadioButtonElement.tsx      |   16 +-
 .../new/NewPerson.tsx                         |   18 +-
 .../shared/AppointmentRadioGroup.tsx          |   76 +-
 .../shared/LegacyAppointmentRadioGroup.tsx    |   76 +-
 .../shared/helpers.ts                         |   13 +-
 .../travelMedicine/shared/routes.ts           |    2 +
 .../shared/sideNavigationItem.tsx             |  130 +-
 .../sections/SectionButtonBar.tsx             |   53 +
 .../templateEditor/sections/SectionTitle.tsx  |    4 +-
 .../sections/TemplateSection.tsx              |   43 +-
 .../sections/TemplateSectionList.tsx          |   23 +-
 .../sections/dataElements/DataElementBox.tsx  |   30 +
 .../dataElements/DataElementHeading.tsx       |   15 +
 .../dataElements/SectionDataElementList.tsx   |   63 +-
 .../SectionElementComponentFactory.tsx        |  126 +
 .../dataElements/TemplateConfirmation.tsx     |   48 +
 .../dataElements/TemplateTextBlock.tsx        |   47 +
 .../AnamnesisQuestion.tsx}                    |   59 +-
 .../{ => anamnesisQuestion}/MainQuestion.tsx  |    6 +-
 .../subElements/SubMultiSelectElement.tsx     |    4 +-
 .../subElements/SubMultiSelectList.tsx        |    4 +-
 .../subElements/SubQuestion.tsx               |    2 +-
 .../lib/opendata/components/OpenDataTable.tsx |   15 +-
 .../opendata/components/openDataColumns.tsx   |    1 -
 employee-portal/src/lib/shared/api/config.ts  |    2 +
 .../shared/components/SearchableGroups.tsx    |    2 +-
 .../components/archiveView/ArchiveTable.tsx   |    9 +-
 .../shared/components/buttons/ActionsMenu.tsx |   18 +-
 .../components/buttons/IconTooltipButton.tsx  |   13 +-
 .../components/cards/SelectableCard.tsx       |    4 +-
 .../contentEditor/ContentEditor.tsx           |    6 +-
 .../components/detailsCard/DetailsCard.tsx    |   63 -
 .../components/detailsCard/LabeledValue.tsx   |   97 -
 .../components/detailsSection/DetailsCell.tsx |    5 +-
 .../detailsSection/DetailsSection.tsx         |    7 +-
 .../filterSettings/FilterTemplates.tsx        |    8 +-
 .../shared/components/form/FormGroupGrid.tsx  |   14 +-
 .../components/formFields/SliderField.tsx     |    2 +
 .../components/formFields/TextareaField.tsx   |    2 +
 .../formFields/ToggleButtonGroupField.tsx     |    8 +-
 .../components/layout/MainContentLayout.tsx   |    9 +-
 .../lib/shared/components/page/PageGrid.tsx   |    3 +-
 .../search/PersonSearchResults.tsx            |   21 +-
 .../procedures/inbox/InboxProceduresTable.tsx |   12 +-
 .../ProgressEntriesContext.tsx                |    9 +-
 .../procedures/progress-entries/constants.tsx |    2 +-
 .../modals/EntryDeletionRequestModal.tsx      |   16 +-
 .../modals/FileDeletionRequestModal.tsx       |   10 +-
 .../sidebars/CreateProgressEntrySidebar.tsx   |    6 +-
 .../DetailsContentWrapper.tsx                 |   98 +-
 .../ManualProgressEntryDetails.tsx            |  143 +-
 .../ProgressEntryDetailsSidebar.tsx           |    7 +-
 .../SystemProgressEntryDetails.tsx            |   52 +-
 .../procedures/progress-entries/types.ts      |   19 +-
 .../components/sidebar/SidebarContent.tsx     |    4 +-
 .../lib/shared/components/table/DataTable.tsx |   22 +-
 .../table/TableNavigationContext.tsx          |    8 +-
 .../lib/shared/components/table/TableRow.tsx  |   16 +-
 employee-portal/src/lib/shared/styles.ts      |   10 -
 .../common/unregisterBroadCastChannel.ts      |   10 +
 employee-portal/src/serviceWorker/sw/index.ts |   12 +
 .../src/serviceWorker/sw/requestHandlers.ts   |    4 +-
 lib-portal/package.json                       |   48 +-
 .../appointmentPicker/AppointmentCalendar.tsx |    5 +-
 .../AppointmentListForDate.tsx                |    1 -
 .../AppointmentPickerField.tsx                |    8 +-
 lib-portal/src/errorHandling/AlertContext.tsx |  122 +-
 lib-portal/src/errorHandling/errorMappers.tsx |   14 +-
 lib-portal/src/formatters/numbers.ts          |   38 +
 lib-portal/src/hooks/useUuid.ts               |    2 +-
 package.json                                  |   40 +-
 pnpm-lock.yaml                                | 3690 +++++++++--------
 pnpm-workspace.yaml                           |   70 +
 reverse-proxy/citizen-portal.conf             |   16 +
 reverse-proxy/employee-portal.conf            |   11 +
 settings.gradle                               |    2 +-
 927 files changed, 24660 insertions(+), 10371 deletions(-)
 create mode 100644 backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java
 rename backend/inspection/src/main/java/de/eshg/inspection/inspection/api/{GetInspectionsResponse.java => ResolveFacilityDuplicateRequest.java} (56%)
 create mode 100644 backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java
 create mode 100644 backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml
 create mode 100644 backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml
 create mode 100644 backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml
 create mode 100644 backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml
 create mode 100644 backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml
 create mode 100644 backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx
 delete mode 100644 backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java
 delete mode 100644 backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java
 create mode 100644 backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java
 delete mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java
 delete mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java
 delete mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java
 delete mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java
 create mode 100644 backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java
 create mode 100644 backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java
 create mode 100644 backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml
 create mode 100644 backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml
 create mode 100644 backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml
 create mode 100644 backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml
 delete mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java
 delete mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java
 create mode 100644 backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java
 create mode 100644 backend/opendata/src/main/resources/application-health-department-frankfurt.properties
 create mode 100644 backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java
 create mode 100644 backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java
 create mode 100644 backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java
 create mode 100644 backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java
 rename backend/school-entry/src/main/java/de/eshg/schoolentry/{client/ChildUpdate.java => business/model/ResolvedMergeProcedureData.java} (53%)
 create mode 100644 backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml
 create mode 100644 backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml
 create mode 100644 backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml
 create mode 100644 backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml
 create mode 100644 backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/AvailableDataSource.java (91%)
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/BaseDataSourceAttribute.java (83%)
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/BusinessDataAttribute.java (67%)
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/BusinessDataSourceAttribute.java (89%)
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/DataSourceDto.java (92%)
 rename backend/statistics/src/main/java/de/eshg/statistics/api/{ => datasource}/GetAvailableDataSourcesResponse.java (90%)
 delete mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
 delete mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
 delete mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
 delete mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java
 create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java
 create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml
 create mode 100644 backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{vaccinationconsultation => document/informationstatement}/InformationStatementMapper.java (53%)
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{vaccinationconsultation => document/informationstatement}/api/InformationStatementDto.java (72%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{vaccinationconsultation/persistence/entity => document/informationstatement/persistence}/InformationStatementRepository.java (62%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{vaccinationconsultation => document/informationstatement}/persistence/entity/InformationStatement.java (93%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/MedicalHistoryController.java (86%)
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/MedicalHistoryMapper.java (64%)
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/api/MedicalHistoryDto.java (77%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/api/PatchMedicalHistoryRequest.java (55%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/persistence/MedicalHistoryRepository.java (83%)
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/{ => document}/medicalhistory/persistence/entity/MedicalHistory.java (96%)
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java
 delete mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java
 rename backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/{TemplateSectionElementDataDto.java => TemplateAnamnesisQuestionDto.java} (84%)
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java
 create mode 100644 backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java
 create mode 100644 backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties
 create mode 100644 backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml
 create mode 100644 backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml
 create mode 100644 backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml
 create mode 100644 backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml
 create mode 100644 backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt
 create mode 100644 backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt
 create mode 100644 buildSrc/src/main/groovy/next-loading-files.gradle
 create mode 100644 citizen-portal/src/app/loading.template.tsx
 delete mode 100644 citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx
 delete mode 100644 citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx
 delete mode 100644 citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx
 rename citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/{DocumentElement.tsx => AnamnesisQuestion.tsx} (73%)
 create mode 100644 citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx
 create mode 100644 citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx
 create mode 100644 employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx
 create mode 100644 employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx
 create mode 100644 employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx
 delete mode 100644 employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx
 rename employee-portal/src/lib/baseModule/components/users/userSidebar/{UserProfileEditForm.tsx => UserProfileEditSidebar.tsx} (81%)
 delete mode 100644 employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx
 delete mode 100644 employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx
 create mode 100644 employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx
 create mode 100644 employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx
 create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts
 create mode 100644 employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx
 create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx
 delete mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx
 delete mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx
 rename employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/{baseData/sidebars => informationStatements}/InformationStatementSidebar.tsx (95%)
 rename employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/{baseData => informationStatements}/InformationStatementsColumns.tsx (85%)
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx
 create mode 100644 employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx
 rename employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/{SectionDataElement.tsx => anamnesisQuestion/AnamnesisQuestion.tsx} (56%)
 rename employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/{ => anamnesisQuestion}/MainQuestion.tsx (94%)
 rename employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/{ => anamnesisQuestion}/subElements/SubMultiSelectElement.tsx (90%)
 rename employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/{ => anamnesisQuestion}/subElements/SubMultiSelectList.tsx (87%)
 rename employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/{ => anamnesisQuestion}/subElements/SubQuestion.tsx (97%)
 delete mode 100644 employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
 delete mode 100644 employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
 delete mode 100644 employee-portal/src/lib/shared/styles.ts
 create mode 100644 employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts
 create mode 100644 lib-portal/src/formatters/numbers.ts

diff --git a/.gitignore b/.gitignore
index 583a654a9..e1e231029 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,12 @@
 # local env files
 .env*.local
 
+# helm files generated by helm-chartsnap.sh
+k8s/helmcharts/eshg/values.deploy-test.central.yaml
+k8s/helmcharts/eshg/values.deploy-test.frankfurt.yaml
+k8s/helmcharts/infrastructure-vshn/values.vshn.deploy-test.central.yaml
+k8s/helmcharts/infrastructure-vshn/values.vshn.deploy-test.frankfurt.yaml
+
 # IntelliJ
 *.iws
 *.iml
diff --git a/admin-portal/package.json b/admin-portal/package.json
index e88c0e18c..902021d14 100644
--- a/admin-portal/package.json
+++ b/admin-portal/package.json
@@ -4,37 +4,50 @@
   "type": "module",
   "private": true,
   "dependencies": {
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/admin-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
+    "@fontsource/poppins": "catalog:joy",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "@tanstack/react-table": "catalog:common",
     "asn1js": "3.0.5",
-    "i18next": "23.15.2",
-    "i18next-resources-to-backend": "1.2.1",
-    "next": "14.2.14",
+    "formik": "catalog:common",
+    "i18next": "catalog:i18next",
+    "i18next-resources-to-backend": "catalog:i18next",
+    "next": "catalog:next",
     "pkijs": "3.2.4",
-    "pvutils": "1.1.3",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-i18next": "15.0.2",
-    "valibot": "0.42.1",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-i18next": "catalog:i18next",
+    "remeda": "catalog:common",
+    "use-debounce": "catalog:common",
+    "valibot": "catalog:common",
     "zod": "3.23.8"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/admin-portal/src/app/layout.tsx b/admin-portal/src/app/layout.tsx
index 2b7b61eae..36474656e 100644
--- a/admin-portal/src/app/layout.tsx
+++ b/admin-portal/src/app/layout.tsx
@@ -107,6 +107,9 @@ export default function RootLayout({
       <body
         style={{ backgroundColor: "var(--joy-palette-neutral-100, #F0F4F8)" }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <NonceProvider initialNonce={nonce}>
           <ThemeProvider>
             <ApiProvider>
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java b/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
index a6bd35474..252c0ed65 100644
--- a/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/ContactApi.java
@@ -135,4 +135,9 @@ public interface ContactApi {
           @RequestParam("file")
           MultipartFile file)
       throws IOException;
+
+  @GetExchange("/{id}/merged-contacts")
+  @ApiResponse(responseCode = "200")
+  @Operation(summary = "Returns the IDs of contacts that were merged into the requested contact")
+  GetMergedContactsResponse getMergedContacts(@PathVariable("id") UUID id);
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java
new file mode 100644
index 000000000..108b56d80
--- /dev/null
+++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/GetMergedContactsResponse.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.base.contact.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetMergedContactsResponse(
+    @Schema(description = "List of contact IDs that have been merged into the requested contact")
+        @NotNull
+        List<UUID> contactIds) {}
diff --git a/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java b/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
index d6a00c52e..f0aa44f2b 100644
--- a/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
+++ b/backend/base-api/src/main/java/de/eshg/base/feature/BaseFeature.java
@@ -14,5 +14,6 @@ public enum BaseFeature {
   VERIFICATION_OF_EXTERNAL_DATA,
   OPEN_DATA,
   GDPR,
+  GDPR_ONLINE_PORTAL,
   CONTACT_MERGE
 }
diff --git a/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java b/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
index bf59d34f3..46d5c5c30 100644
--- a/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
+++ b/backend/base-api/src/main/java/de/eshg/base/testhelper/BaseTestHelperApi.java
@@ -72,6 +72,9 @@ public interface BaseTestHelperApi extends TestHelperApi, LoginProvider {
   @PostExchange("/enabled-new-features/{featureToEnable}")
   void enableNewFeature(@PathVariable("featureToEnable") BaseFeature featureToEnable);
 
+  @PostExchange("/disable-new-features/{featureToDisable}")
+  void disableNewFeature(@PathVariable("featureToDisable") BaseFeature featureToDisable);
+
   @PostExchange("/setup-admin")
   void createSetupAdmin(@Valid @RequestBody CreateSetupAdminRequest request);
 
diff --git a/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java b/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
index 3a34b29d0..d2d3bb62a 100644
--- a/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
+++ b/backend/base-api/src/main/java/de/eshg/base/user/api/UserRoleDto.java
@@ -31,6 +31,7 @@ public enum UserRoleDto {
   BASE_LABELS_WRITE,
   BASE_CONTACTS_READ,
   BASE_CONTACTS_WRITE,
+  BASE_GDPR_PROCEDURE_REVIEW,
   BASE_GDPR_PROCEDURE_READ,
   BASE_GDPR_PROCEDURE_WRITE,
   BASE_GLOBAL_CALENDARS_WRITE,
@@ -52,6 +53,7 @@ public enum UserRoleDto {
   INSPECTION_CENTRALREPOSITORY_WRITE,
   INSPECTION_CENTRALREPOSITORY_DELETE,
   INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS,
+  INSPECTION_IMPORT,
   TRAVEL_MEDICINE_ADMIN,
   MEASLES_PROTECTION_ADMIN,
   CHAT_MANAGEMENT_WRITE,
diff --git a/backend/base/build.gradle b/backend/base/build.gradle
index f6804de73..53233bba9 100644
--- a/backend/base/build.gradle
+++ b/backend/base/build.gradle
@@ -21,7 +21,7 @@ dependencies {
     implementation 'org.springframework.boot:spring-boot-starter-mail'
     implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
     implementation 'org.apache.commons:commons-collections4:latest.release'
-    implementation libs.bundles.keycloak.client
+    implementation libs.keycloak.client.admin.client
     implementation 'io.github.java-diff-utils:java-diff-utils:latest.release'
     implementation 'com.google.guava:guava:latest.release'
     implementation('org.mnode.ical4j:ical4j:latest.release') {
diff --git a/backend/base/gradle.lockfile b/backend/base/gradle.lockfile
index fe1c2be4d..b6454d34b 100644
--- a/backend/base/gradle.lockfile
+++ b/backend/base/gradle.lockfile
@@ -22,10 +22,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=productionRuntimeClas
 com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.mangstadt:vinnie:2.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -134,9 +131,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:fontbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -202,15 +199,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testCompileClasspath,testRuntimeClasspath
@@ -225,9 +223,8 @@ org.junit.platform:junit-platform-commons:1.10.5=productionRuntimeClasspath,runt
 org.junit.platform:junit-platform-engine:1.10.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.liquibase:liquibase-core:4.27.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.mnode.ical4j:ical4j:4.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/base/openApi.yaml b/backend/base/openApi.yaml
index 5e61eff45..53e7fb02d 100644
--- a/backend/base/openApi.yaml
+++ b/backend/base/openApi.yaml
@@ -818,6 +818,26 @@ paths:
       summary: Get base contact history step by revision id.
       tags:
       - Contact
+  /contacts/{id}/merged-contacts:
+    get:
+      operationId: getMergedContacts
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetMergedContactsResponse"
+          description: OK
+      summary: Returns the IDs of contacts that were merged into the requested contact
+      tags:
+      - Contact
   /department/info:
     get:
       operationId: getDepartmentInfo
@@ -2893,6 +2913,20 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/disable-new-features/{featureToDisable}:
+    post:
+      operationId: disableNewFeature
+      parameters:
+      - in: path
+        name: featureToDisable
+        required: true
+        schema:
+          $ref: "#/components/schemas/BaseFeature"
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/enabled-new-features/{featureToEnable}:
     post:
       operationId: enableNewFeature
@@ -4225,6 +4259,7 @@ components:
       - VERIFICATION_OF_EXTERNAL_DATA
       - OPEN_DATA
       - GDPR
+      - GDPR_ONLINE_PORTAL
       - CONTACT_MERGE
     BlockingEventsOfCalendar:
       type: object
@@ -6145,6 +6180,20 @@ components:
             $ref: "#/components/schemas/Label"
       required:
       - elements
+    GetMergedContactsResponse:
+      type: object
+      properties:
+        contactIds:
+          type: array
+          description: List of contact IDs that have been merged into the requested
+            contact
+          items:
+            type: string
+            format: uuid
+            description: List of contact IDs that have been merged into the requested
+              contact
+      required:
+      - contactIds
     GetPermissionsResponse:
       type: object
       properties:
@@ -8639,6 +8688,7 @@ components:
       - BASE_LABELS_WRITE
       - BASE_CONTACTS_READ
       - BASE_CONTACTS_WRITE
+      - BASE_GDPR_PROCEDURE_REVIEW
       - BASE_GDPR_PROCEDURE_READ
       - BASE_GDPR_PROCEDURE_WRITE
       - BASE_GLOBAL_CALENDARS_WRITE
@@ -8660,6 +8710,7 @@ components:
       - INSPECTION_CENTRALREPOSITORY_WRITE
       - INSPECTION_CENTRALREPOSITORY_DELETE
       - INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS
+      - INSPECTION_IMPORT
       - TRAVEL_MEDICINE_ADMIN
       - MEASLES_PROTECTION_ADMIN
       - CHAT_MANAGEMENT_WRITE
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
index c56ef3be8..a0ac85fc6 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/FacilityService.java
@@ -6,6 +6,7 @@
 package de.eshg.base.centralfile.persistence;
 
 import static de.eshg.base.centralfile.FacilityController.FACILITY_REFERENCE_NOT_FOUND;
+import static de.eshg.base.centralfile.persistence.entity.DataOrigin.EXTERNAL;
 import static de.eshg.base.util.SearchSpecificationUtil.getSimilarityThreshold;
 
 import de.cronn.commons.lang.StreamUtil;
@@ -34,10 +35,13 @@ import java.time.Instant;
 import java.util.*;
 import org.apache.commons.lang3.builder.DiffResult;
 import org.hibernate.Hibernate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 @Service
 public class FacilityService {
+  private static final Logger log = LoggerFactory.getLogger(FacilityService.class);
   public static final String MUTEX_FACILITY_WRITE = "FACILITY_WRITE";
   private final FacilityRepository facilityRepository;
   private final FuzzySearchHelper fuzzySearchHelper;
@@ -189,6 +193,14 @@ public class FacilityService {
 
   private Facility updateFileStateAndReferenceFacilityWhenLocked(
       Facility facilityFileState, Facility fileStateUpdate) {
+    if (facilityFileState.getDataOrigin() != EXTERNAL
+        && FacilityMatcher.isFacilityMatch(fileStateUpdate, facilityFileState)) {
+      log.debug(
+          "Recognized no-op update. Returning original facility file state (id={})",
+          facilityFileState.getId());
+      return facilityFileState;
+    }
+
     Facility referenceFacility =
         facilityRepository
             .findReferenceFacilityByFileStateExternalId(facilityFileState.getExternalId())
@@ -248,13 +260,13 @@ public class FacilityService {
 
   public Facility addFacilityFromExternalSource(Facility facilityFileState) {
     Facility referenceFacility = facilityFileState.cloneFromFileState();
-    referenceFacility.setDataOrigin(DataOrigin.EXTERNAL);
+    referenceFacility.setDataOrigin(EXTERNAL);
     referenceFacility.setDeleteAt(null);
     Facility savedReferenceFacility = facilityRepository.save(referenceFacility);
 
     facilityFileState.setReferenceFacility(savedReferenceFacility);
     facilityFileState.setReferenceVersion(savedReferenceFacility.getVersion());
-    facilityFileState.setDataOrigin(DataOrigin.EXTERNAL);
+    facilityFileState.setDataOrigin(EXTERNAL);
 
     return facilityRepository.save(facilityFileState);
   }
diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
index 1817d0784..74b4384cb 100644
--- a/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
+++ b/backend/base/src/main/java/de/eshg/base/centralfile/persistence/repository/FacilityRepository.java
@@ -5,6 +5,7 @@
 
 package de.eshg.base.centralfile.persistence.repository;
 
+import de.eshg.base.centralfile.persistence.entity.DataOrigin;
 import de.eshg.base.centralfile.persistence.entity.Facility;
 import java.time.Instant;
 import java.util.Collection;
@@ -19,12 +20,13 @@ public interface FacilityRepository
     extends JpaRepository<Facility, UUID>, JpaSpecificationExecutor<Facility> {
 
   default List<Facility> findReferenceFacilityByName(String name) {
-    return findByNameEqualsAndReferenceFacilityIsNull(name);
+    return findByNameEqualsAndDataOriginNotAndReferenceFacilityIsNull(name, DataOrigin.EXTERNAL);
   }
 
   Optional<Facility> findByExternalId(UUID externalId);
 
-  List<Facility> findByNameEqualsAndReferenceFacilityIsNull(String name);
+  List<Facility> findByNameEqualsAndDataOriginNotAndReferenceFacilityIsNull(
+      String name, DataOrigin excludedDataOrigin);
 
   @Query(
       "select f.externalId from Facility f join f.referenceFacility ref where ref.id = :referenceId ")
diff --git a/backend/base/src/main/java/de/eshg/base/contact/ContactController.java b/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
index 4ac26abac..603f243fb 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/ContactController.java
@@ -269,6 +269,12 @@ public class ContactController implements ContactApi {
         matches.getTotalElements());
   }
 
+  @Override
+  @Transactional(readOnly = true)
+  public GetMergedContactsResponse getMergedContacts(UUID id) {
+    return new GetMergedContactsResponse(contactService.findAllMergeSources(id));
+  }
+
   private static void validateVCardFile(MultipartFile file) {
     ParseVCardUtils.validateFileExistsAndHasCorrectType(file);
   }
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
index c8bd1ad60..72fca536f 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/ContactService.java
@@ -96,6 +96,10 @@ public class ContactService {
     return contactRepository.findAllById(ids);
   }
 
+  public List<UUID> findAllMergeSources(UUID id) {
+    return contactRepository.findAllByMergedInto(id);
+  }
+
   public List<RevisionEntryWithChange<Contact>> getContactHistory(
       UUID id, UUID userId, Instant before) {
     AuditReader reader = AuditReaderFactory.get(entityManager);
diff --git a/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java b/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
index e413cd2c8..a8c285b63 100644
--- a/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
+++ b/backend/base/src/main/java/de/eshg/base/contact/persistence/repository/ContactRepository.java
@@ -29,4 +29,7 @@ public interface ContactRepository
 
   @Query("select count(*) from Contact c where c.category = :category")
   long countByCategory(@Param("category") InstitutionContactCategory category);
+
+  @Query("select id from Contact where mergedInto.id = :mergedInto")
+  List<UUID> findAllByMergedInto(@Param("mergedInto") UUID mergedInto);
 }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
index 9f907f068..9855496ac 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakTestProvisioning.java
@@ -23,7 +23,8 @@ import org.springframework.stereotype.Component;
 @Component
 @DependsOn(CitizenKeycloakProvisioning.BEAN_NAME)
 @ConditionalOnTestUserProvisioningEnabled
-public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning {
+public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning
+    implements AutoCloseable {
   public static final String MUK_TEST_REALM_NAME = "muk-test";
   public static final String BUND_ID_TEST_REALM_NAME = "bund-id-test";
   public static final String KEY_PROVIDER_TYPE = "org.keycloak.keys.KeyProvider";
@@ -74,6 +75,12 @@ public class CitizenKeycloakTestProvisioning extends KeycloakTestProvisioning {
     }
   }
 
+  @Override
+  public void close() {
+    this.mukKeycloakClient.close();
+    this.bundIdKeycloakClient.close();
+  }
+
   private void createOrUpdateIdpTestRealmKeys(
       KeycloakProperties.IdpTestRealm idpTestRealm,
       KeycloakProperties.IdentityProvider idpConfig,
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
index 0b2848c3b..701276274 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakClient.java
@@ -23,9 +23,12 @@ import de.eshg.keycloak.api.user.model.KeycloakApiGroupMemberDto;
 import de.eshg.keycloak.api.user.model.KeycloakApiUserDto;
 import de.eshg.rest.service.error.AlreadyExistsException;
 import de.eshg.rest.service.security.CurrentUserHelper;
+import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.core.UriBuilder;
 import java.net.URI;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import org.keycloak.admin.client.resource.RoleScopeResource;
 import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.representations.idm.RoleRepresentation;
@@ -53,11 +56,25 @@ public class EmployeeKeycloakClient extends RealmBoundKeycloakClient {
   }
 
   public List<KeycloakApiUserDto> getUsersById(List<UUID> userIds, boolean ignoreUnknownId) {
-    return keycloakUserApi
-        .getUsersBulk(realmName, new BulkGetUsersRequest(userIds, ignoreUnknownId))
-        .users();
+    try {
+      return keycloakUserApi
+          .getUsersBulk(realmName, new BulkGetUsersRequest(userIds, ignoreUnknownId))
+          .users();
+    } catch (NotFoundException notFound) {
+      KeycloakError keycloakError = notFound.getResponse().readEntity(KeycloakError.class);
+      Pattern pattern = Pattern.compile("^User with id '(?<userId>[-a-zA-Z0-9]+)' not found$");
+      Matcher matcher = pattern.matcher(keycloakError.error());
+      if (matcher.matches()) {
+        throw new de.eshg.rest.service.error.NotFoundException(
+            "User with id '%s' not found".formatted(matcher.group("userId")));
+      }
+
+      throw new IllegalStateException("Unexpected exception for bulk get user by id", notFound);
+    }
   }
 
+  record KeycloakError(String error) {}
+
   public List<KeycloakApiGroupMemberDto> getGroupMembers(List<String> groupNames) {
     return keycloakUserApi
         .getGroupMembers(realmName, new GetGroupMembersRequest(groupNames))
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
index f1be19fff..3458cd799 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/EmployeeKeycloakProvisioning.java
@@ -13,6 +13,7 @@ import static de.eshg.base.keycloak.RealmBoundKeycloakClient.REALM_MANAGEMENT_CL
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SECURITY_ADMIN_CONSOLE_CLIENT_ID;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_ID_PREFIX;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_NAME_PREFIX;
+import static de.eshg.base.keycloak.RealmBoundKeycloakClient.getClientRepresentationAttributes;
 
 import com.google.common.annotations.VisibleForTesting;
 import de.cronn.commons.lang.StreamUtil;
@@ -245,11 +246,7 @@ public class EmployeeKeycloakProvisioning extends KeycloakProvisioning<EmployeeK
     clientRepresentation.setFrontchannelLogout(false);
     clientRepresentation.setRedirectUris(List.of());
     clientRepresentation.setWebOrigins(List.of());
-    // default attributes, set for diffing
-    clientRepresentation.setAttributes(
-        Map.of(
-            "backchannel.logout.revoke.offline.tokens", FALSE,
-            "backchannel.logout.session.required", TRUE));
+    clientRepresentation.setAttributes(getClientRepresentationAttributes(Map.of()));
     clientRepresentation.setDefaultClientScopes(List.of(ESHG_CLIENT_SCOPE_NAME));
     clientRepresentation.setOptionalClientScopes(List.of());
 
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
index 65cdce3e2..73168920e 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/InitialKeycloakProvisioning.java
@@ -6,9 +6,11 @@
 package de.eshg.base.keycloak;
 
 import static de.eshg.base.keycloak.InitialKeycloakProvisioning.BEAN_NAME;
+import static de.eshg.base.keycloak.KeycloakProvisioning.FALSE;
 
 import jakarta.ws.rs.NotAuthorizedException;
 import java.util.List;
+import java.util.Map;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.slf4j.Logger;
@@ -67,6 +69,7 @@ public class InitialKeycloakProvisioning {
     client.setName("GA-Lotse Base Module Client");
     client.setDescription(
         "Used by the GA-Lotse base module for provisioning and managing this keycloak instance.");
+    client.setAttributes(Map.of("realm_client", FALSE));
 
     return client;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
index 5f9acf228..be70e16a5 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/KeycloakProvisioning.java
@@ -8,6 +8,7 @@ package de.eshg.base.keycloak;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.BASIC_CLIENT_SCOPE;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_ID_PREFIX;
 import static de.eshg.base.keycloak.RealmBoundKeycloakClient.SYSTEM_CLIENT_NAME_PREFIX;
+import static de.eshg.base.keycloak.RealmBoundKeycloakClient.getClientRepresentationAttributes;
 
 import com.google.common.annotations.VisibleForTesting;
 import de.eshg.lib.keycloak.*;
@@ -239,8 +240,10 @@ public abstract class KeycloakProvisioning<T extends RealmBoundKeycloakClient> {
     clientRepresentation.setWebOrigins(List.of("+"));
 
     clientRepresentation.setAttributes(
-        Map.of(
-            "post.logout.redirect.uris", redirectUriBuilder.replacePath("/logout").toUriString()));
+        getClientRepresentationAttributes(
+            Map.of(
+                "post.logout.redirect.uris",
+                redirectUriBuilder.replacePath("/logout").toUriString())));
     setEshgClientScopes(clientRepresentation);
     return clientRepresentation;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
index 9e49419e4..9336e4bcf 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/MasterKeycloakProvisioning.java
@@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
 
 @Component(BEAN_NAME)
 @DependsOn(InitialKeycloakProvisioning.BEAN_NAME)
-public class MasterKeycloakProvisioning {
+public class MasterKeycloakProvisioning implements AutoCloseable {
   public static final String BEAN_NAME = "masterKeycloakProvisioning";
 
   private static final Logger log = LoggerFactory.getLogger(MasterKeycloakProvisioning.class);
@@ -71,10 +71,14 @@ public class MasterKeycloakProvisioning {
     if (this.keycloakProperties.setupAdmin().enabled()) {
       initializeSetupAdmin(
           keycloakProperties.setupAdmin().username(), keycloakProperties.setupAdmin().email());
-      // TODO(ISSUE-3525): Delete default admin user here.
     }
   }
 
+  @Override
+  public void close() {
+    this.keycloakClient.close();
+  }
+
   @VisibleForTesting
   public RealmBoundKeycloakClient getKeycloakClient() {
     return keycloakClient;
diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
index cc0e6f03b..db4468b37 100644
--- a/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
+++ b/backend/base/src/main/java/de/eshg/base/keycloak/RealmBoundKeycloakClient.java
@@ -5,6 +5,8 @@
 
 package de.eshg.base.keycloak;
 
+import static de.eshg.base.keycloak.KeycloakProvisioning.FALSE;
+import static de.eshg.base.keycloak.KeycloakProvisioning.TRUE;
 import static de.eshg.base.keycloak.KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID;
 import static de.eshg.base.keycloak.differ.KeycloakDiffer.toJson;
 import static java.util.function.Function.identity;
@@ -70,6 +72,8 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
   private static final String PROFILE_CLIENT_SCOPE = "profile";
   private static final String OFFLINE_ACCESS_CLIENT_SCOPE = "offline_access";
   private static final String MICROPROFILE_JWT_CLIENT_SCOPE = "microprofile-jwt";
+  private static final String ORGANIZATION_CLIENT_SCOPE = "organization";
+  private static final String SAML_ORGANIZATION_CLIENT_SCOPE = "saml_organization";
   private static final String EMAIL_CLIENT_SCOPE = "email";
   public static final String ACCOUNT_CLIENT_ID = "account";
   public static final String ACCOUNT_CONSOLE_CLIENT_ID = "account-console";
@@ -502,7 +506,9 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
         PROFILE_CLIENT_SCOPE,
         OFFLINE_ACCESS_CLIENT_SCOPE,
         MICROPROFILE_JWT_CLIENT_SCOPE,
-        EMAIL_CLIENT_SCOPE);
+        EMAIL_CLIENT_SCOPE,
+        ORGANIZATION_CLIENT_SCOPE,
+        SAML_ORGANIZATION_CLIENT_SCOPE);
   }
 
   List<String> getIgnoredClientIds() {
@@ -1037,6 +1043,16 @@ public class RealmBoundKeycloakClient implements AutoCloseable {
     }
   }
 
+  public static Map<String, String> getClientRepresentationAttributes(
+      Map<String, String> configuredAttributes) {
+    Map<String, String> attributes = new LinkedHashMap<>();
+    attributes.put("backchannel.logout.revoke.offline.tokens", FALSE);
+    attributes.put("backchannel.logout.session.required", TRUE);
+    attributes.put("realm_client", FALSE);
+    attributes.putAll(configuredAttributes);
+    return attributes;
+  }
+
   private String getDefaultRoleName() {
     return "default-roles-" + realmName;
   }
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
index 1415faf90..87dd78597 100644
--- a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperController.java
@@ -130,6 +130,11 @@ public class BaseTestHelperController extends TestHelperController
     baseFeatureToggle.enableNewFeature(featureToEnable);
   }
 
+  @Override
+  public void disableNewFeature(BaseFeature featureToDisable) {
+    baseFeatureToggle.disableNewFeature(featureToDisable);
+  }
+
   @Override
   public void createSetupAdmin(CreateSetupAdminRequest request) {
     masterKeycloakProvisioning.initializeSetupAdmin(request.username(), request.emailAddress());
diff --git a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
index d63fb23be..08244e11c 100644
--- a/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
+++ b/backend/base/src/main/java/de/eshg/base/testhelper/BaseTestHelperService.java
@@ -33,6 +33,8 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.interception.TestRequestInterceptor;
 import de.eshg.testhelper.population.BasePopulator;
 import de.eshg.testhelper.population.ListWithTotalNumber;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
 import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
@@ -43,9 +45,7 @@ import java.util.Map;
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
-import org.apache.http.impl.client.HttpClientBuilder;
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
-import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine;
 import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.admin.client.Keycloak;
@@ -181,17 +181,18 @@ public class BaseTestHelperService extends DefaultTestHelperService {
 
   public AccessToken loginUncached(UsernamePassword usernamePassword, String userAgent) {
     environmentConfig.assertIsNotProduction();
-    try (Keycloak keycloak =
-        KeycloakBuilder.builder()
-            .serverUrl(keycloakProperties.internal().url())
-            .grantType(OAuth2Constants.PASSWORD)
-            .realm(getRealmName(usernamePassword.realm()))
-            .scope(OAuth2Constants.SCOPE_OPENID)
-            .clientId(KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID)
-            .username(usernamePassword.username())
-            .password(usernamePassword.password())
-            .resteasyClient(getResteasyClient(userAgent))
-            .build()) {
+    try (ResteasyClient resteasyClient = getResteasyClient(userAgent);
+        Keycloak keycloak =
+            KeycloakBuilder.builder()
+                .serverUrl(keycloakProperties.internal().url())
+                .grantType(OAuth2Constants.PASSWORD)
+                .realm(getRealmName(usernamePassword.realm()))
+                .scope(OAuth2Constants.SCOPE_OPENID)
+                .clientId(KeycloakTestProvisioning.TEST_HELPER_CLIENT_ID)
+                .username(usernamePassword.username())
+                .password(usernamePassword.password())
+                .resteasyClient(resteasyClient)
+                .build()) {
       AccessTokenResponse accessTokenResponse = keycloak.tokenManager().getAccessToken();
       Assert.isTrue(
           accessTokenResponse.getTokenType().equals("Bearer"),
@@ -208,11 +209,7 @@ public class BaseTestHelperService extends DefaultTestHelperService {
   }
 
   private static ResteasyClient getResteasyClient(String userAgent) {
-    return new ResteasyClientBuilderImpl()
-        .httpEngine(
-            new ApacheHttpClient43Engine(
-                HttpClientBuilder.create().setUserAgent(userAgent).build()))
-        .build();
+    return new ResteasyClientBuilderImpl().register(new HttpUserAgentFilter(userAgent)).build();
   }
 
   private String getRealmName(Realm realm) {
@@ -309,4 +306,13 @@ public class BaseTestHelperService extends DefaultTestHelperService {
         healthDepartmentContactPopulator.populate(numberOfEntitiesToPopulate);
     return new SearchContactsResponse(result.entities(), result.totalNumberOfElements());
   }
+
+  private record HttpUserAgentFilter(String userAgent) implements ClientRequestFilter {
+    @Override
+    public void filter(ClientRequestContext requestContext) {
+      if (userAgent != null) {
+        requestContext.getHeaders().putSingle("User-Agent", userAgent);
+      }
+    }
+  }
 }
diff --git a/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java b/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
index bced46952..cca9d6650 100644
--- a/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
+++ b/backend/base/src/main/java/de/eshg/base/user/mapper/UserMapper.java
@@ -5,6 +5,8 @@
 
 package de.eshg.base.user.mapper;
 
+import static de.eshg.lib.keycloak.EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW;
+
 import de.eshg.base.SalutationDto;
 import de.eshg.base.calendar.api.DetailedEventWithoutCalendarId;
 import de.eshg.base.keycloak.EmployeeUserAttribute;
@@ -128,6 +130,7 @@ public class UserMapper {
       case BASE_LABELS_WRITE -> EmployeePermissionRole.BASE_LABELS_WRITE;
       case BASE_CONTACTS_READ -> EmployeePermissionRole.BASE_CONTACTS_READ;
       case BASE_CONTACTS_WRITE -> EmployeePermissionRole.BASE_CONTACTS_WRITE;
+      case BASE_GDPR_PROCEDURE_REVIEW -> BASE_GDPR_PROCEDURE_REVIEW;
       case BASE_GDPR_PROCEDURE_READ -> EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ;
       case BASE_GDPR_PROCEDURE_WRITE -> EmployeePermissionRole.BASE_GDPR_PROCEDURE_WRITE;
       case BASE_GLOBAL_CALENDARS_WRITE -> EmployeePermissionRole.BASE_GLOBAL_CALENDARS_WRITE;
@@ -157,6 +160,7 @@ public class UserMapper {
           EmployeePermissionRole.INSPECTION_CENTRALREPOSITORY_DELETE;
       case INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS ->
           EmployeePermissionRole.INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS;
+      case INSPECTION_IMPORT -> EmployeePermissionRole.INSPECTION_IMPORT;
       case TRAVEL_MEDICINE_ADMIN -> EmployeePermissionRole.TRAVEL_MEDICINE_ADMIN;
       case MEASLES_PROTECTION_ADMIN -> EmployeePermissionRole.MEASLES_PROTECTION_ADMIN;
       case CHAT_MANAGEMENT_WRITE -> EmployeePermissionRole.CHAT_MANAGEMENT_WRITE;
@@ -208,6 +212,7 @@ public class UserMapper {
       case BASE_LABELS_WRITE -> UserRoleDto.BASE_LABELS_WRITE;
       case BASE_CONTACTS_READ -> UserRoleDto.BASE_CONTACTS_READ;
       case BASE_CONTACTS_WRITE -> UserRoleDto.BASE_CONTACTS_WRITE;
+      case BASE_GDPR_PROCEDURE_REVIEW -> UserRoleDto.BASE_GDPR_PROCEDURE_REVIEW;
       case BASE_GDPR_PROCEDURE_READ -> UserRoleDto.BASE_GDPR_PROCEDURE_READ;
       case BASE_GDPR_PROCEDURE_WRITE -> UserRoleDto.BASE_GDPR_PROCEDURE_WRITE;
       case BASE_MAIL_SEND -> UserRoleDto.BASE_MAIL_SEND;
@@ -240,6 +245,7 @@ public class UserMapper {
       case INSPECTION_CENTRALREPOSITORY_DELETE -> UserRoleDto.INSPECTION_CENTRALREPOSITORY_DELETE;
       case INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS ->
           UserRoleDto.INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS;
+      case INSPECTION_IMPORT -> UserRoleDto.INSPECTION_IMPORT;
       case TRAVEL_MEDICINE_ADMIN -> UserRoleDto.TRAVEL_MEDICINE_ADMIN;
       case MEASLES_PROTECTION_ADMIN -> UserRoleDto.MEASLES_PROTECTION_ADMIN;
       case CHAT_MANAGEMENT_WRITE -> UserRoleDto.CHAT_MANAGEMENT_WRITE;
diff --git a/backend/base/src/main/resources/application-local.properties b/backend/base/src/main/resources/application-local.properties
index e66635d9c..1dc82c0a0 100644
--- a/backend/base/src/main/resources/application-local.properties
+++ b/backend/base/src/main/resources/application-local.properties
@@ -1,5 +1,6 @@
 spring.config.import=classpath:application-health-department-frankfurt.properties
 
+eshg.mail.noreply=noreply@stadt-frankfurt.de
 eshg.keycloak.admin-client.client-secret=admin
 eshg.keycloak.employee-realm.auth-client-secret=jPKtsvmKqRqsscNnN7NMVFhmf3b9NH
 eshg.keycloak.citizen-realm.auth-client-secret=tstj3RgLtqF4Kbh3hVNRRXTwxLkhmq
diff --git a/backend/base/src/main/resources/application-preview-features.properties b/backend/base/src/main/resources/application-preview-features.properties
index c1014e48a..6f012d6d3 100644
--- a/backend/base/src/main/resources/application-preview-features.properties
+++ b/backend/base/src/main/resources/application-preview-features.properties
@@ -1 +1 @@
-de.eshg.base.feature-toggle.enabled-new-features=TASK_METRICS, STI_PROTECTION, CHAT_USERNAME, INBOX, GDPR, CONTACT_MERGE
+de.eshg.base.feature-toggle.enabled-new-features=TASK_METRICS, STI_PROTECTION, CHAT_USERNAME, INBOX, GDPR, GDPR_ONLINE_PORTAL, CONTACT_MERGE, OPEN_DATA
diff --git a/backend/base/src/main/resources/application.properties b/backend/base/src/main/resources/application.properties
index 476fd6357..f49e72ee7 100644
--- a/backend/base/src/main/resources/application.properties
+++ b/backend/base/src/main/resources/application.properties
@@ -26,7 +26,6 @@ spring.mail.properties.mail.smtp.timeout=3000
 #Socket write timeout, implemented by java.util.concurrent.ScheduledExecutorService
 #Overhead of one thread per connection
 spring.mail.properties.mail.smtp.writetimeout=5000
-eshg.mail.noreply=noreply@stadt-frankfurt.de
 eshg.employee-portal.reverse-proxy.url=http://localhost:4000
 eshg.citizen-portal.reverse-proxy.url=http://localhost:4001
 eshg.keycloak.employee-realm.name=eshg
diff --git a/backend/build.gradle b/backend/build.gradle
index 28ad66d7a..3d59880dd 100644
--- a/backend/build.gradle
+++ b/backend/build.gradle
@@ -266,18 +266,6 @@ subprojects {
         testImplementation 'org.springframework.boot:spring-boot-starter-test'
         testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
     }
-
-    // Workaround for bug in gradle
-    // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/376
-    // https://github.com/gradle/gradle/issues/27947
-    dependencyManagement {
-        applyMavenExclusions(false)
-        dependencies {
-            libs.bundles.keycloak.client.get().each {
-                dependency it.toString()
-            }
-        }
-    }
 }
 
 tasks.register("composeUp") {
@@ -287,7 +275,7 @@ tasks.register("composeUp") {
 // Temporary workaround to solve the race condition between tests in different modules
 // that both use the same 'base' module instance and reset the database in between.
 // We currently work on to get rid of this shared dependency.
-// Please see [internal gitlab link]
+// Please see https://gitlab.com/ga-ffm/ga-lotse/eshg-backend/-/merge_requests/377
 
 def testLimitService = project.gradle.sharedServices
         .registerIfAbsent("testLimitService", TaskExecutionLimitService.class, {spec -> spec.maxParallelUsages = 1})
diff --git a/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java b/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
index 40bbfa290..806a559d3 100644
--- a/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
+++ b/backend/business-module-persistence-commons/src/main/java/de/eshg/liquibase/EshgMigrateAutoIncrementToSequenceChange.java
@@ -8,6 +8,8 @@ package de.eshg.liquibase;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
 import liquibase.change.AbstractChange;
 import liquibase.change.ChangeMetaData;
 import liquibase.change.DatabaseChange;
@@ -31,7 +33,7 @@ public class EshgMigrateAutoIncrementToSequenceChange extends AbstractChange {
   private static final String CREATE_SEQUENCE_SQL =
       """
     create sequence %s
-        start with %d
+        start with 1
         increment by %d
         no minvalue
         no maxvalue
@@ -88,12 +90,22 @@ public class EshgMigrateAutoIncrementToSequenceChange extends AbstractChange {
 
       long nextValue = selectNextValue(jdbcConnection, oldSequenceName);
 
-      return new SqlStatement[] {
-        new RawSqlStatement(
-            CREATE_SEQUENCE_SQL.formatted(newSequenceName, nextValue, ALLOCATION_SIZE)),
-        new RawSqlStatement(
-            "alter table %s alter column id drop identity".formatted(getTableName()))
-      };
+      List<SqlStatement> sqlStatements = new ArrayList<>();
+
+      sqlStatements.add(
+          new RawSqlStatement(CREATE_SEQUENCE_SQL.formatted(newSequenceName, ALLOCATION_SIZE)));
+
+      if (nextValue > 1) {
+        sqlStatements.add(
+            new RawSqlStatement(
+                "alter sequence %s restart with %d".formatted(newSequenceName, nextValue)));
+      }
+
+      sqlStatements.add(
+          new RawSqlStatement(
+              "alter table %s alter column id drop identity".formatted(getTableName())));
+
+      return sqlStatements.toArray(SqlStatement[]::new);
     } catch (DatabaseException | SQLException e) {
       throw new RuntimeException(e);
     }
diff --git a/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
index 0ef4c84fd..ef622770d 100644
--- a/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
+++ b/backend/business-module-persistence-commons/src/testFixtures/java/de/eshg/MigrationIntegrationTestTraits.java
@@ -8,12 +8,10 @@ package de.eshg;
 import de.cronn.postgres.snapshot.util.PostgresDump;
 import de.cronn.postgres.snapshot.util.PostgresDumpFormat;
 import de.cronn.postgres.snapshot.util.PostgresDumpOption;
-import de.cronn.postgres.snapshot.util.Schema;
 import de.eshg.testhelper.api.TestHelperDatabaseConnectionDetailsResponse;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.List;
 
 public interface MigrationIntegrationTestTraits {
 
@@ -36,7 +34,6 @@ public interface MigrationIntegrationTestTraits {
         databaseConnectionDetails.username(),
         databaseConnectionDetails.password(),
         PostgresDumpFormat.PLAIN_TEXT,
-        List.of(Schema.include("public")),
         PostgresDumpOption.CREATE,
         PostgresDumpOption.INSERTS,
         PostgresDumpOption.NO_OWNER,
diff --git a/backend/compliance-test/gradle.lockfile b/backend/compliance-test/gradle.lockfile
index 583a2ec32..dcd5b35eb 100644
--- a/backend/compliance-test/gradle.lockfile
+++ b/backend/compliance-test/gradle.lockfile
@@ -25,10 +25,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testRuntimeClasspath
 com.github.docker-java:docker-java-transport:3.3.6=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=testRuntimeClasspath
 com.github.mangstadt:vinnie:2.0.2=testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=testCompileClasspath,testRuntimeClasspath
 com.github.virtuald:curvesapi:1.08=testCompileClasspath,testRuntimeClasspath
@@ -204,9 +201,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=testRuntimeClasspath
 org.apache.httpcomponents.core5:httpcore5:5.2.5=testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:fontbox:3.0.3=testRuntimeClasspath
@@ -284,15 +281,16 @@ org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.java-websocket:Java-WebSocket:1.5.7=testRuntimeClasspath
 org.javassist:javassist:3.28.0-GA=testCompileClasspath,testRuntimeClasspath
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testRuntimeClasspath
 org.jsoup:jsoup:1.18.1=testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
@@ -303,9 +301,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=testRuntimeClasspath
 org.liquibase:liquibase-core:4.27.0=testRuntimeClasspath
 org.mnode.ical4j:ical4j:4.0.4=testRuntimeClasspath
diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml
index ca86d27f0..2214a2815 100644
--- a/backend/docker-compose.yaml
+++ b/backend/docker-compose.yaml
@@ -116,8 +116,8 @@ services:
         limits:
           memory: 1G
     environment:
-      KEYCLOAK_ADMIN: admin
-      KEYCLOAK_ADMIN_PASSWORD: admin
+      KC_BOOTSTRAP_ADMIN_USERNAME: admin
+      KC_BOOTSTRAP_ADMIN_PASSWORD: admin
       KC_DB: dev-file
       KC_PROXY_HEADERS: xforwarded
       KC_HOSTNAME: http://localhost:4003
@@ -146,8 +146,6 @@ services:
 
   inspection:
     image: ga-lotse/inspection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://inspection-db/inspection
@@ -176,8 +174,6 @@ services:
 
   school-entry:
     image: ga-lotse/school-entry
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://school-entry-db/schoolentry
@@ -229,8 +225,6 @@ services:
 
   travel-medicine:
     image: ga-lotse/travel-medicine
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - DOCKER_HOSTNAME=${DOCKER_HOSTNAME:-localhost}
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
@@ -287,8 +281,6 @@ services:
 
   measles-protection:
     image: ga-lotse/measles-protection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://measles-protection-db/measles_protection
@@ -456,8 +448,6 @@ services:
 
   sti-protection:
     image: ga-lotse/sti-protection
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://sti-protection-db/sti_protection
@@ -511,8 +501,6 @@ services:
 
   medical-registry:
     image: ga-lotse/medical-registry
-    env_file:
-      - 'lib-procedures/src/test/resources/archiving-test.env'
     environment:
       - spring.profiles.active=${ACTIVE_SPRING_PROFILES:-local,test-helper}
       - spring.datasource.url=jdbc:postgresql://medical-registry-db/medical_registry
diff --git a/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
index 09529169e..f44964a94 100644
--- a/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
+++ b/backend/file-commons/src/main/java/de/eshg/file/common/PdfAConformanceValidator.java
@@ -9,6 +9,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.function.Function;
 import org.verapdf.core.EncryptedPdfException;
 import org.verapdf.core.ModelParsingException;
 import org.verapdf.core.ValidationException;
@@ -28,25 +29,30 @@ public class PdfAConformanceValidator {
   }
 
   public static void validate(byte[] fileContent) {
+    validate(fileContent, message -> new BadRequestException(ErrorCode.NONCONFORM_PDF, message));
+  }
+
+  public static void validate(
+      byte[] fileContent, Function<String, RuntimeException> exceptionCreator) {
     try (VeraPDFFoundry foundry = Foundries.defaultInstance();
         PDFAParser parser = foundry.createParser(new ByteArrayInputStream(fileContent));
         PDFAValidator validator = foundry.createValidator(parser.getFlavour(), false)) {
 
       ValidationResult result = validator.validate(parser);
       if (!result.isCompliant()) {
-        throw createPdfAConformanceException();
+        throw createPdfAConformanceException(exceptionCreator);
       }
 
     } catch (IOException
         | ValidationException
         | ModelParsingException
         | EncryptedPdfException exception) {
-      throw createPdfAConformanceException();
+      throw createPdfAConformanceException(exceptionCreator);
     }
   }
 
-  private static BadRequestException createPdfAConformanceException() {
-    return new BadRequestException(
-        ErrorCode.NONCONFORM_PDF, "Uploaded pdf did not pass conformance level check");
+  private static RuntimeException createPdfAConformanceException(
+      Function<String, RuntimeException> exceptionCreator) {
+    return exceptionCreator.apply("Uploaded pdf did not pass conformance level check");
   }
 }
diff --git a/backend/inspection/build.gradle b/backend/inspection/build.gradle
index 1915dc5cd..a43c68c62 100644
--- a/backend/inspection/build.gradle
+++ b/backend/inspection/build.gradle
@@ -12,6 +12,7 @@ dependencies {
     implementation project(':lib-document-generator')
     implementation project(':lib-editor')
     implementation project(':lib-scheduling')
+    implementation project(':lib-xlsx-import')
     implementation project(':business-module-persistence-commons')
     implementation project(':file-commons')
 
@@ -27,6 +28,7 @@ dependencies {
     testImplementation testFixtures(project(':lib-service-directory-admin-api'))
     testImplementation testFixtures(project(':business-module-persistence-commons'))
     testImplementation testFixtures(project(':lib-document-generator'))
+    testImplementation testFixtures(project(':lib-xlsx-import'))
 }
 
 dockerCompose {
diff --git a/backend/inspection/gradle.lockfile b/backend/inspection/gradle.lockfile
index f450f2d9a..b8d67f21e 100644
--- a/backend/inspection/gradle.lockfile
+++ b/backend/inspection/gradle.lockfile
@@ -22,6 +22,7 @@ com.github.docker-java:docker-java-transport:3.3.6=productionRuntimeClasspath,ru
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.github.virtuald:curvesapi:1.08=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -80,8 +81,9 @@ com.tngtech.archunit:archunit-junit5:1.3.0=productionRuntimeClasspath,runtimeCla
 com.tngtech.archunit:archunit:1.3.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 com.zaxxer:HikariCP:5.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-io:commons-io:2.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+commons-io:commons-io:2.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-logging:commons-logging:1.3.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath
@@ -119,6 +121,7 @@ io.swagger.core.v3:swagger-models-jakarta:2.2.22=compileClasspath,productionRunt
 jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+jakarta.mail:jakarta.mail-api:2.1.3=testRuntimeClasspath
 jakarta.persistence:jakarta.persistence-api:3.1.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.servlet:jakarta.servlet-api:6.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 jakarta.transaction:jakarta.transaction-api:2.0.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -140,9 +143,10 @@ net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,te
 net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.ttddyy:datasource-proxy:1.10=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.antlr:antlr4-runtime:4.13.0=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.commons:commons-compress:1.27.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-collections4:4.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-compress:1.26.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.commons:commons-math3:3.6.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-text:1.12.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -155,6 +159,9 @@ org.apache.pdfbox:pdfbox-io:3.0.3=productionRuntimeClasspath,runtimeClasspath,te
 org.apache.pdfbox:pdfbox-tools:3.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.pdfbox:pdfbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.pdfbox:xmpbox:3.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml-lite:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi-ooxml:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.poi:poi:5.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tika:tika-bom:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-core:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.tika:tika-parser-pdf-module:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -163,6 +170,7 @@ org.apache.tomcat.embed:tomcat-embed-core:10.1.31=compileClasspath,productionRun
 org.apache.tomcat.embed:tomcat-embed-el:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-websocket:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat:tomcat-annotations-api:10.1.31=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+org.apache.xmlbeans:xmlbeans:5.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-anim:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-awt-util:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.xmlgraphics:batik-bridge:1.17=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
diff --git a/backend/inspection/openApi.yaml b/backend/inspection/openApi.yaml
index 30ab76095..0881cda4d 100644
--- a/backend/inspection/openApi.yaml
+++ b/backend/inspection/openApi.yaml
@@ -873,6 +873,11 @@ paths:
         schema:
           type: string
           format: date-time
+      - in: query
+        name: hasDuplicates
+        required: false
+        schema:
+          type: boolean
       - in: query
         name: pageNumber
         required: false
@@ -1189,25 +1194,6 @@ paths:
       tags:
       - WebSearch
   /facilities/{id}:
-    get:
-      operationId: getFacility
-      parameters:
-      - in: path
-        name: id
-        required: true
-        schema:
-          type: string
-          format: uuid
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/InspFacility"
-          description: OK
-      summary: Get a facility
-      tags:
-      - Facility
     put:
       operationId: updateFacility
       parameters:
@@ -1380,6 +1366,59 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
+  /import:
+    post:
+      operationId: importProcesses
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                file:
+                  type: string
+                  format: binary
+              required:
+              - file
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: object
+          description: OK
+      summary: Start import processes
+      tags:
+      - Importer
+  /import/templates/inspection-import-template:
+    get:
+      operationId: getInspectionImportTemplate
+      responses:
+        "200":
+          content:
+            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
+              schema:
+                type: string
+                format: binary
+          description: OK
+      summary: Get the XLSX inspection import template
+      tags:
+      - Importer
   /inbox-procedures:
     get:
       description: |
@@ -1543,19 +1582,6 @@ paths:
       summary: Update status of inbox procedure
       tags:
       - InboxProcedure
-  /inspections:
-    get:
-      operationId: getInspections
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/GetInspectionsResponse"
-          description: OK
-      summary: "Get list of all inspections, sorted by title"
-      tags:
-      - Inspection
   /inspections/geo/reversegeocode:
     get:
       operationId: getReverseGeoCode
@@ -1722,6 +1748,26 @@ paths:
         inspection
       tags:
       - Inspection
+  /inspections/{id}/facility-duplicates:
+    get:
+      operationId: getFacilityDuplicates
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/FacilityDuplicateReview"
+          description: OK
+      summary: Get facility duplicates of an inspection
+      tags:
+      - Inspection
   /inspections/{id}/finalize:
     post:
       operationId: finalizeInspection
@@ -1755,6 +1801,26 @@ paths:
       summary: Finalize an inspection
       tags:
       - Inspection
+  /inspections/{id}/inspection-duplicates:
+    get:
+      operationId: getInspectionDuplicates
+      parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/InspectionDuplicateReview"
+          description: OK
+      summary: Get inspection duplicates of an inspection
+      tags:
+      - Inspection
   /inspections/{id}/inventory:
     put:
       description: "To delete an inventory entry, set it's count to 0."
@@ -1803,6 +1869,53 @@ paths:
         inspection
       tags:
       - Inspection
+  /inspections/{id}/resolve-facility-duplicate:
+    post:
+      operationId: resolveFacilityDuplicate
+      parameters:
+      - description: The id of the inspection
+        in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/ResolveFacilityDuplicateRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Resolves a facility duplicate for an inspection by choosing a facility
+      tags:
+      - Inspection
+  /inspections/{id}/resolve-inspection-duplicate:
+    post:
+      operationId: resolveInspectionDuplicate
+      parameters:
+      - description: The id of the inspection
+        in: path
+        name: id
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/ResolveInspectionDuplicateRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Resolves an inspection duplicate for an inspection by choosing whether
+        to keep or discard an inspection
+      tags:
+      - Inspection
   /inspections/{id}/resource:
     post:
       description: Adds a resource to an inspection. You can not update a resource.
@@ -3467,6 +3580,17 @@ components:
       required:
       - dataOrigin
       - name
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AddPacklistDefinitionRevisionRequest:
       type: object
       properties:
@@ -4671,12 +4795,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateNewChecklistDefinitionRequest:
@@ -5218,6 +5338,18 @@ components:
             type: string
       required:
       - name
+    FacilityDuplicateReview:
+      type: object
+      properties:
+        existingFacilities:
+          type: array
+          items:
+            $ref: "#/components/schemas/FacilityForDuplicateReview"
+        importedFacility:
+          $ref: "#/components/schemas/FacilityForDuplicateReview"
+      required:
+      - existingFacilities
+      - importedFacility
     FacilityFileState:
       type: object
       properties:
@@ -5274,6 +5406,42 @@ components:
       - name
       - phoneNumbers
       - referenceVersion
+    FacilityForDuplicateReview:
+      type: object
+      properties:
+        addressAddition:
+          type: string
+        city:
+          type: string
+        emailAddresses:
+          type: array
+          items:
+            type: string
+        houseNo:
+          type: string
+        name:
+          type: string
+        objectType:
+          $ref: "#/components/schemas/ObjectTypeRef"
+        phoneNumbers:
+          type: array
+          items:
+            type: string
+        postalCode:
+          type: string
+        referenceId:
+          type: string
+          format: uuid
+        street:
+          type: string
+      required:
+      - city
+      - emailAddresses
+      - name
+      - phoneNumbers
+      - postalCode
+      - referenceId
+      - street
     FacilityType:
       type: string
       enum:
@@ -5306,6 +5474,12 @@ components:
       enum:
       - REVIEW
       - DOCUMENT_INSPECTION
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -5581,15 +5755,6 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/InspectionIncident"
-    GetInspectionsResponse:
-      type: object
-      properties:
-        inspections:
-          type: array
-          items:
-            $ref: "#/components/schemas/Inspection"
-      required:
-      - inspections
     GetManualProgressEntryHistoryResponse:
       type: object
       properties:
@@ -5738,7 +5903,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -5988,6 +6155,40 @@ components:
       required:
       - changedAt
       - changedBy
+    ImportStatistics:
+      type: object
+      properties:
+        created:
+          type: integer
+          format: int32
+          minimum: 0
+        duplicated:
+          type: integer
+          format: int32
+          minimum: 0
+        failed:
+          type: integer
+          format: int32
+          minimum: 0
+        mergeFailed:
+          type: integer
+          format: int32
+          minimum: 0
+        merged:
+          type: integer
+          format: int32
+          minimum: 0
+        total:
+          type: integer
+          format: int32
+          minimum: 0
+      required:
+      - created
+      - duplicated
+      - failed
+      - mergeFailed
+      - merged
+      - total
     InboxProcedure:
       type: object
       properties:
@@ -6175,6 +6376,9 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/InspPendingFacility"
+        numberOfPossibleDuplicates:
+          type: integer
+          format: int64
         totalNumberOfElements:
           type: integer
           format: int64
@@ -6183,6 +6387,7 @@ components:
           format: int32
       required:
       - elements
+      - numberOfPossibleDuplicates
       - totalNumberOfElements
       - totalPages
     InspPendingFacility:
@@ -6211,6 +6416,8 @@ components:
         plannedFrom:
           type: string
           format: date-time
+        possibleFacilityDuplicate:
+          type: boolean
         postalCode:
           type: string
         street:
@@ -6220,6 +6427,7 @@ components:
       - city
       - id
       - name
+      - possibleFacilityDuplicate
       - postalCode
       - street
     InspPendingFacilityInspection:
@@ -6233,6 +6441,8 @@ components:
           format: int32
         phase:
           $ref: "#/components/schemas/InspectionPhase"
+        possibleInspectionDuplicate:
+          type: boolean
         status:
           $ref: "#/components/schemas/ProcedureStatus"
         type:
@@ -6241,6 +6451,7 @@ components:
       - id
       - numberOfIncidents
       - phase
+      - possibleInspectionDuplicate
       - status
       - type
     InspPendingFacilityKind:
@@ -6300,6 +6511,10 @@ components:
           $ref: "#/components/schemas/InspectionPhase"
         plannedAppointment:
           $ref: "#/components/schemas/InspectionAppointment"
+        possibleFacilityDuplicate:
+          type: boolean
+        possibleInspectionDuplicate:
+          type: boolean
         reportId:
           type: string
           format: uuid
@@ -6333,6 +6548,8 @@ components:
       - facility
       - inventories
       - phase
+      - possibleFacilityDuplicate
+      - possibleInspectionDuplicate
       - resources
       - result
       - selectedChecklistDefinitionVersions
@@ -6423,12 +6640,23 @@ components:
       - name
       - version
       - versionId
+    InspectionDuplicateReview:
+      type: object
+      properties:
+        existingInspections:
+          type: array
+          items:
+            $ref: "#/components/schemas/InspectionForDuplicateReview"
+        importedInspection:
+          $ref: "#/components/schemas/InspectionForDuplicateReview"
+      required:
+      - existingInspections
+      - importedInspection
     InspectionFeature:
       type: string
       enum:
-      - PACKLISTS
-      - CHECKLIST_AUDIOS
       - OFFLINE
+      - IMPORT
     InspectionFollowupInfo:
       type: object
       properties:
@@ -6440,6 +6668,31 @@ components:
           format: uuid
         followupType:
           $ref: "#/components/schemas/FollowupType"
+    InspectionForDuplicateReview:
+      type: object
+      properties:
+        executedTime:
+          type: string
+          format: date-time
+        externalId:
+          type: string
+          format: uuid
+        numberOfIncidents:
+          type: integer
+          format: int32
+        result:
+          $ref: "#/components/schemas/InspectionResult"
+        title:
+          type: string
+        type:
+          $ref: "#/components/schemas/InspectionType"
+      required:
+      - executedTime
+      - externalId
+      - numberOfIncidents
+      - result
+      - title
+      - type
     InspectionIncident:
       type: object
       properties:
@@ -6580,6 +6833,7 @@ components:
       - INITIAL
       - COMPLAINT
       - DOCUMENT_INSPECTION
+      - IMPORT
     InterceptionType:
       type: string
       enum:
@@ -6596,6 +6850,20 @@ components:
       - PROTECTIVE_EQUIPMENT
       - TEST_KIT
       - MISC
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -6641,13 +6909,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -6681,13 +6955,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -6940,15 +7211,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -7375,6 +7640,21 @@ components:
       - fileSize
       - reportDate
       - reportId
+    ResolveFacilityDuplicateRequest:
+      type: object
+      properties:
+        chosenReferenceId:
+          type: string
+          format: uuid
+      required:
+      - chosenReferenceId
+    ResolveInspectionDuplicateRequest:
+      type: object
+      properties:
+        keepInspection:
+          type: boolean
+      required:
+      - keepInspection
     ResourceType:
       type: string
       description: The list of possible types under which Resources can be categorized.
@@ -7445,6 +7725,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -7456,6 +7741,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java b/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
index 993281f44..87b4ca782 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/checklist/ChecklistService.java
@@ -16,7 +16,6 @@ import de.eshg.inspection.checklist.persistence.element.ChecklistElement;
 import de.eshg.inspection.checklistdefinition.persistence.ChecklistDefinitionVersion;
 import de.eshg.inspection.incident.persistence.InspectionIncidentRepository;
 import de.eshg.inspection.inspection.persistence.Inspection;
-import de.eshg.rest.service.error.BadRequestException;
 import java.util.List;
 import org.springframework.stereotype.Service;
 
@@ -53,9 +52,6 @@ public class ChecklistService {
   }
 
   public GetChecklistsResponse getChecklists(Inspection inspection) {
-    if (inspection.getChecklists() == null || inspection.getChecklists().isEmpty()) {
-      throw new BadRequestException("wrong state", "This inspection doesn't have a checklist yet.");
-    }
     return ChecklistDtoMapper.dtoFrom(inspection.getChecklists());
   }
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java b/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
index 1d5ad0af2..1fd6444f6 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/checklist/MediaFileService.java
@@ -20,7 +20,6 @@ import de.eshg.inspection.checklist.persistence.element.ChecklistImageElement;
 import de.eshg.inspection.checklistdefinition.api.ChecklistElementType;
 import de.eshg.inspection.common.persistence.MediaFile;
 import de.eshg.inspection.common.persistence.MediaFileRepository;
-import de.eshg.inspection.feature.InspectionFeature;
 import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.inspection.api.InspectionPhase;
@@ -90,7 +89,6 @@ public class MediaFileService {
         return List.of(MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG);
       }
       case UpdateChecklistAudioDto ignored -> {
-        inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.CHECKLIST_AUDIOS);
         return List.of(CustomMediaTypes.MEDIA_TYPE_WAV, CustomMediaTypes.MEDIA_TYPE_MP3);
       }
       default ->
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
index cd50e7927..dd839f456 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityClient.java
@@ -8,19 +8,25 @@ package de.eshg.inspection.facility;
 import static de.eshg.inspection.facility.FacilityMapper.mapBaseFacilityAddRequest;
 
 import de.eshg.base.centralfile.FacilityApi;
+import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
 import de.eshg.base.centralfile.api.GetFileStateIdsResponse;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStatesRequest;
+import de.eshg.base.centralfile.api.facility.GetReferenceFacilityResponse;
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
+import de.eshg.base.centralfile.api.facility.SearchReferenceFacilitiesResponse;
 import de.eshg.base.centralfile.api.person.SyncFileStateRequest;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.error.ErrorResponse;
+import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.UUID;
 import java.util.function.Supplier;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.HttpClientErrorException;
 
@@ -47,6 +53,10 @@ public class FacilityClient {
     return doAndForwardErrorCodes(() -> facilityApi.getFacilityFileState(id));
   }
 
+  public GetReferenceFacilityResponse getReferenceFacility(UUID id) {
+    return doAndForwardErrorCodes(() -> facilityApi.getReferenceFacility(id));
+  }
+
   public List<UUID> getFacilityFileStateIdsWithSameReferenceFacility(UUID id) {
     return doAndForwardErrorCodes(
         () -> facilityApi.getFacilityFileStateIdsWithSameReferenceFacility(id).fileStateIds());
@@ -81,17 +91,33 @@ public class FacilityClient {
         });
   }
 
+  public void markFacilityFileStateForDeletion(Collection<UUID> fileStateIds) {
+    doAndForwardErrorCodes(
+        () -> {
+          facilityApi.markFacilityFileStateForDeletion(
+              new DeleteFileStatesRequest(new LinkedHashSet<>(fileStateIds)));
+          return null;
+        });
+  }
+
+  public SearchReferenceFacilitiesResponse searchReferenceFacilities(String name) {
+    return doAndForwardErrorCodes(() -> facilityApi.searchReferenceFacilities(name));
+  }
+
   private <T> T doAndForwardErrorCodes(Supplier<T> action) {
     try {
       return action.get();
     } catch (HttpClientErrorException e) {
-      // We want to forward error codes 1:1 to the frontend.
+      if (e.getStatusCode().isSameCodeAs(HttpStatus.UNAUTHORIZED)) {
+        throw new BadRequestException(ErrorCode.UNAUTHORIZED, "Unauthorized base module call");
+      }
       ErrorResponse errorResponse = e.getResponseBodyAs(ErrorResponse.class);
-      if (errorResponse == null) {
+      if (errorResponse != null) {
+        throw new BadRequestException(errorResponse.errorCode(), errorResponse.message());
+      } else {
         throw new BadRequestException(
             ErrorCode.UNEXPECTED_ERROR, "Could not read error from base module");
       }
-      throw new BadRequestException(errorResponse.errorCode(), errorResponse.message());
     }
   }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
index 8eb87f3d1..cd801092c 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityController.java
@@ -44,13 +44,6 @@ public class FacilityController {
     this.facilityService = facilityService;
   }
 
-  @GetMapping(path = "/{id}")
-  @Operation(summary = "Get a facility")
-  @Transactional(readOnly = true)
-  public InspFacilityDto getFacility(@PathVariable("id") UUID externalId) {
-    return facilityService.getFacility(externalId);
-  }
-
   @PostMapping
   @Operation(summary = "Add a new facility")
   @Transactional
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
index 9499dcd1f..ae48fd802 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityMapper.java
@@ -11,11 +11,14 @@ import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.GetReferenceFacilityResponse;
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
 import de.eshg.inspection.facility.api.*;
 import de.eshg.inspection.facility.persistence.Facility;
 import de.eshg.inspection.facility.persistence.PendingFacilityView;
+import de.eshg.inspection.inspection.api.FacilityForDuplicateReviewDto;
 import de.eshg.inspection.objecttype.ObjectTypeMapper;
+import de.eshg.inspection.objecttype.api.ObjectTypeDto;
 import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.mapping.ProcedureMapper;
@@ -23,12 +26,9 @@ import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.rest.service.error.NotFoundException;
 import jakarta.validation.constraints.NotNull;
 import java.time.Instant;
-import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 public class FacilityMapper {
@@ -92,21 +92,6 @@ public class FacilityMapper {
     return facility;
   }
 
-  public static Map<UUID, InspFacilityDto> facilitiesFrom(
-      List<Facility> facilities, List<AddFacilityFileStateResponse> baseFacilities) {
-    Map<UUID, AddFacilityFileStateResponse> baseFacilityMap =
-        baseFacilities.stream()
-            .collect(Collectors.toMap(AddFacilityFileStateResponse::id, Function.identity()));
-
-    return facilities.stream()
-        .map(
-            facility ->
-                FacilityMapper.fromAddFacilityResponse(
-                    facility, baseFacilityMap.get(facility.getCentralFileStateId())))
-        .distinct()
-        .collect(Collectors.toMap(InspFacilityDto::id, Function.identity()));
-  }
-
   private static InsPendingFacilityInspectionDto createInspPendingFacilityInspectionDto(
       PendingFacilityView view) {
     if (view.inspection() == null) return null;
@@ -115,7 +100,8 @@ public class FacilityMapper {
         ProcedureMapper.toInterfaceType(view.inspection().getProcedureStatus()),
         view.inspection().getType(),
         view.inspection().getPhase(),
-        view.inspection().getIncidents().size());
+        view.inspection().getIncidents().size(),
+        !view.inspection().getPossibleDuplicates().isEmpty());
   }
 
   static InspPendingFacilityDto createInspPendingFacilityDto(
@@ -140,7 +126,8 @@ public class FacilityMapper {
             domesticAddress.city(),
             plannedFrom,
             objecttype,
-            inspection);
+            inspection,
+            view.facility().hasPossibleDuplicates());
       }
       case PostboxAddressDto postboxAddress -> {
         return new InspPendingFacilityDto(
@@ -155,7 +142,8 @@ public class FacilityMapper {
             postboxAddress.city(),
             plannedFrom,
             objecttype,
-            inspection);
+            inspection,
+            view.facility().hasPossibleDuplicates());
       }
       default ->
           throw new NotFoundException(
@@ -197,4 +185,74 @@ public class FacilityMapper {
             baseFacility.contactAddress(),
             baseFacility.differentBillingAddress()));
   }
+
+  public static FacilityForDuplicateReviewDto mapToFacilityForDuplicateReviewDto(
+      UUID referenceId, GetFacilityFileStateResponse baseFacility, ObjectTypeDto objectType) {
+    switch (baseFacility.contactAddress()) {
+      case DomesticAddressDto domesticAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            referenceId,
+            new ObjectTypeRefDto(objectType.id(), objectType.name()),
+            baseFacility.name(),
+            domesticAddress.street(),
+            domesticAddress.houseNumber(),
+            domesticAddress.addressAddition(),
+            domesticAddress.postalCode(),
+            domesticAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      case PostboxAddressDto postboxAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            referenceId,
+            new ObjectTypeRefDto(objectType.id(), objectType.name()),
+            baseFacility.name(),
+            "Postfach",
+            postboxAddress.postbox(),
+            null,
+            postboxAddress.postalCode(),
+            postboxAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      default ->
+          throw new NotFoundException(
+              "invalid address of unknown type: " + baseFacility.contactAddress());
+    }
+  }
+
+  public static FacilityForDuplicateReviewDto mapToFacilityForDuplicateReviewDto(
+      GetReferenceFacilityResponse baseFacility) {
+    switch (baseFacility.contactAddress()) {
+      case DomesticAddressDto domesticAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            baseFacility.id(),
+            null,
+            baseFacility.name(),
+            domesticAddress.street(),
+            domesticAddress.houseNumber(),
+            domesticAddress.addressAddition(),
+            domesticAddress.postalCode(),
+            domesticAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      case PostboxAddressDto postboxAddress -> {
+        return new FacilityForDuplicateReviewDto(
+            baseFacility.id(),
+            null,
+            baseFacility.name(),
+            "Postfach",
+            postboxAddress.postbox(),
+            null,
+            postboxAddress.postalCode(),
+            postboxAddress.city(),
+            baseFacility.emailAddresses(),
+            baseFacility.phoneNumbers());
+      }
+      default ->
+          throw new NotFoundException(
+              "invalid address of unknown type: " + baseFacility.contactAddress());
+    }
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
index 9138f3a8f..099518c59 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/FacilityService.java
@@ -13,6 +13,8 @@ import static org.springframework.util.CollectionUtils.isEmpty;
 import de.eshg.base.address.AddressDto;
 import de.eshg.base.centralfile.api.DataOriginDto;
 import de.eshg.base.centralfile.api.facility.*;
+import de.eshg.domain.model.BaseEntity_;
+import de.eshg.domain.model.SequencedBaseEntity_;
 import de.eshg.inspection.facility.api.GetPendingFacilitiesFilterOptionsDto;
 import de.eshg.inspection.facility.api.GetPendingFacilitiesPaginationOptionsDto;
 import de.eshg.inspection.facility.api.InspAddFacilityRequest;
@@ -31,6 +33,8 @@ import de.eshg.inspection.facility.persistence.PendingFacilityView;
 import de.eshg.inspection.facility.websearch.WebSearchService;
 import de.eshg.inspection.facility.websearch.persistence.WebSearchEntry;
 import de.eshg.inspection.facility.websearch.persistence.WebSearchEntryStatus;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionFinalizer;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.inspection.api.InspectionPhase;
@@ -40,6 +44,7 @@ import de.eshg.inspection.inspection.persistence.InspectionAppointment;
 import de.eshg.inspection.inspection.persistence.InspectionAppointment_;
 import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility;
 import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility_;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
 import de.eshg.inspection.inspection.persistence.Inspection_;
 import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
 import de.eshg.inspection.objecttype.persistence.ObjectType;
@@ -58,6 +63,7 @@ import jakarta.persistence.criteria.Join;
 import jakarta.persistence.criteria.JoinType;
 import jakarta.persistence.criteria.Predicate;
 import jakarta.persistence.criteria.Root;
+import jakarta.validation.constraints.NotNull;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -90,6 +96,8 @@ public class FacilityService {
   private final Clock clock;
   private final EntityManager entityManager;
   private final InspectionFinalizer inspectionFinalizer;
+  private final InspectionRepository inspectionRepository;
+  private final InspectionFeatureToggle inspectionFeatureToggle;
 
   public FacilityService(
       FacilityRepository facilityRepository,
@@ -98,7 +106,9 @@ public class FacilityService {
       WebSearchService webSearchService,
       Clock clock,
       EntityManager entityManager,
-      InspectionFinalizer inspectionFinalizer) {
+      InspectionFinalizer inspectionFinalizer,
+      InspectionRepository inspectionRepository,
+      InspectionFeatureToggle inspectionFeatureToggle) {
     this.facilityRepository = facilityRepository;
     this.facilityClient = facilityClient;
     this.inspectionService = inspectionService;
@@ -106,13 +116,8 @@ public class FacilityService {
     this.clock = clock;
     this.entityManager = entityManager;
     this.inspectionFinalizer = inspectionFinalizer;
-  }
-
-  public InspFacilityDto getFacility(UUID externalId) {
-    Facility facility = loadFacility(externalId);
-    GetFacilityFileStateResponse baseResponse =
-        facilityClient.getFacilityFileState(facility.getCentralFileStateId());
-    return FacilityMapper.fromGetFacilityResponse(facility, baseResponse);
+    this.inspectionRepository = inspectionRepository;
+    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   public InspAddFacilityResponse addFacility(InspAddFacilityRequest request) {
@@ -242,9 +247,22 @@ public class FacilityService {
   public InspPendingFacilitiesOverviewResponse getPendingFacilities(
       GetPendingFacilitiesFilterOptionsDto params,
       GetPendingFacilitiesPaginationOptionsDto pagination) {
+    if (params.hasDuplicates() != null) {
+      inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    }
+
     // early validate page request params
     PageRequest pageRequest = pagination.getPageRequest();
 
+    List<Long> facilityIdsWithFacilityDuplicate = facilityRepository.getFacilityIdsWithDuplicates();
+
+    List<Long> inspectionIdsWithInspectionDuplicate =
+        inspectionRepository.getInspectionIdsWithDuplicates();
+
+    long numberOfDuplicates =
+        (long) facilityIdsWithFacilityDuplicate.size()
+            + (long) inspectionIdsWithInspectionDuplicate.size();
+
     List<PendingFacilityView> candidates =
         findPendingFacilities(
             params.objectTypeId(),
@@ -252,7 +270,10 @@ public class FacilityService {
             params.type(),
             params.phase(),
             params.isBefore(),
-            params.isAfter());
+            params.isAfter(),
+            params.hasDuplicates(),
+            facilityIdsWithFacilityDuplicate,
+            inspectionIdsWithInspectionDuplicate);
 
     // fetch centralfile data in a bulk query
     Map<UUID, AddFacilityFileStateResponse> centralFileData = fetchCentralFileData(candidates);
@@ -266,7 +287,9 @@ public class FacilityService {
     List<InspPendingFacilityDto> result = sortAndPageEntries(filteredEntries, pageRequest);
 
     int totalPages = (int) Math.ceil((double) filteredEntries.size() / pageRequest.getPageSize());
-    return new InspPendingFacilitiesOverviewResponse(totalPages, filteredEntries.size(), result);
+
+    return new InspPendingFacilitiesOverviewResponse(
+        totalPages, filteredEntries.size(), result, numberOfDuplicates);
   }
 
   private List<PendingFacilityView> findPendingFacilities(
@@ -275,7 +298,10 @@ public class FacilityService {
       @Nullable Set<InspectionType> type,
       @Nullable Set<InspectionPhase> phase,
       @Nullable Instant isBefore,
-      @Nullable Instant isAfter) {
+      @Nullable Instant isAfter,
+      @Nullable Boolean hasDuplicates,
+      @NotNull List<Long> facilityIds,
+      @NotNull List<Long> inspectionIds) {
     Set<ProcedureStatus> procedureStatus = FacilityMapper.toDomainType(status);
 
     CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -350,6 +376,15 @@ public class FacilityService {
                       LocalDate.ofInstant(isAfter, ZoneOffset.UTC)))));
     }
 
+    if (hasDuplicates != null) {
+      Predicate hasDuplicatesPredicate =
+          cb.or(
+              inspectionRoot.get(SequencedBaseEntity_.id).in(inspectionIds),
+              facilityJoin.get(BaseEntity_.id).in(facilityIds));
+
+      predicates.add(hasDuplicates ? hasDuplicatesPredicate : cb.not(hasDuplicatesPredicate));
+    }
+
     cq.select(cb.construct(PendingFacilityView.class, facilityJoin, irfJoin, inspectionRoot));
     cq.where(cb.and(predicates.toArray(Predicate[]::new)));
 
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
index 1e4a8cc28..6b91bae3e 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/GetPendingFacilitiesFilterOptionsDto.java
@@ -25,4 +25,5 @@ public record GetPendingFacilitiesFilterOptionsDto(
     Set<InspectionType> type,
     Set<InspectionPhase> phase,
     Instant isBefore,
-    Instant isAfter) {}
+    Instant isAfter,
+    Boolean hasDuplicates) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
index adf17d9b7..5ddba64d8 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InsPendingFacilityInspectionDto.java
@@ -18,4 +18,5 @@ public record InsPendingFacilityInspectionDto(
     @NotNull ProcedureStatusDto status,
     @NotNull InspectionType type,
     @NotNull InspectionPhase phase,
-    @NotNull int numberOfIncidents) {}
+    @NotNull int numberOfIncidents,
+    @NotNull boolean possibleInspectionDuplicate) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
index 9e20bd7b3..6d29015e9 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilitiesOverviewResponse.java
@@ -15,5 +15,6 @@ import java.util.List;
 public record InspPendingFacilitiesOverviewResponse(
     @NotNull int totalPages,
     @NotNull long totalNumberOfElements,
-    @NotNull @Valid List<InspPendingFacilityDto> elements)
+    @NotNull @Valid List<InspPendingFacilityDto> elements,
+    @NotNull long numberOfPossibleDuplicates)
     implements PagedResponse<InspPendingFacilityDto> {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
index a6551c827..0d1f3b6b6 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/api/InspPendingFacilityDto.java
@@ -26,4 +26,5 @@ public record InspPendingFacilityDto(
     @NotBlank String city,
     Instant plannedFrom,
     @Valid ObjectTypeRefDto objecttype,
-    @Valid InsPendingFacilityInspectionDto inspection) {}
+    @Valid InsPendingFacilityInspectionDto inspection,
+    @NotNull boolean possibleFacilityDuplicate) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
index 1683b3af9..c8351943e 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/Facility.java
@@ -48,6 +48,9 @@ public class Facility extends BaseEntityWithExternalId {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private ObjectType objectType;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  private boolean possibleDuplicates;
+
   public Facility() {}
 
   public Facility(ObjectType objectType, UUID centralFileStateId) {
@@ -102,4 +105,12 @@ public class Facility extends BaseEntityWithExternalId {
   public void setLastInspected(Instant lastInspected) {
     this.lastInspected = lastInspected;
   }
+
+  public boolean hasPossibleDuplicates() {
+    return possibleDuplicates;
+  }
+
+  public void setPossibleDuplicates(boolean possibleDuplicates) {
+    this.possibleDuplicates = possibleDuplicates;
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
index 27f627a21..57c70872c 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/facility/persistence/FacilityRepository.java
@@ -10,6 +10,7 @@ import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface FacilityRepository
     extends JpaRepository<Facility, Long>, JpaSpecificationExecutor<Facility> {
@@ -19,4 +20,7 @@ public interface FacilityRepository
   Optional<Facility> findByCentralFileStateId(UUID centralFileStatId);
 
   List<Facility> findAllByCentralFileStateIdIn(List<UUID> centralFileStateIds);
+
+  @Query("select id from Facility where possibleDuplicates = true")
+  List<Long> getFacilityIdsWithDuplicates();
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java b/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
index 5f85021d3..c0aa3f768 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/feature/InspectionFeature.java
@@ -6,7 +6,6 @@
 package de.eshg.inspection.feature;
 
 public enum InspectionFeature {
-  PACKLISTS,
-  CHECKLIST_AUDIOS,
   OFFLINE,
+  IMPORT,
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java
new file mode 100644
index 000000000..5352aed41
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspection.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.inspection.inspection.api.InspectionResult;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+
+record ImportInspection(
+    @NotNull Instant lastInspected, @NotNull InspectionResult result, String incidents) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java
new file mode 100644
index 000000000..a9deb3668
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportInspectionFacility.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import jakarta.validation.constraints.NotNull;
+
+record ImportInspectionFacility(
+    String importId, ObjectType objectType, @NotNull FacilityDetailsDto facilityDetailsDto) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java
new file mode 100644
index 000000000..e62501a8a
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImportPersister.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static java.time.temporal.ChronoUnit.HOURS;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.inspection.facility.FacilityClient;
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.facility.persistence.FacilityRepository;
+import de.eshg.inspection.incident.persistence.InspectionIncident;
+import de.eshg.inspection.inspection.api.InspectionPhase;
+import de.eshg.inspection.inspection.api.InspectionType;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.inspection.inspection.persistence.InspectionAppointment;
+import de.eshg.inspection.inspection.persistence.InspectionRelatedFacility;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
+import de.eshg.inspection.inspection.persistence.InspectionTask;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import de.eshg.inspection.objecttype.persistence.ObjectTypeRepository;
+import de.eshg.inspection.report.InspectionReportService;
+import de.eshg.inspection.report.mapper.ChecklistReportMapper;
+import de.eshg.inspection.report.persistence.Report;
+import de.eshg.inspection.report.persistence.element.ReportElementText;
+import de.eshg.lib.auditlog.AuditLogger;
+import de.eshg.lib.procedure.domain.model.FacilityType;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.TaskStatus;
+import de.eshg.rest.service.security.CurrentUserHelper;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ImportPersister {
+  private final InspectionRepository inspectionRepository;
+  private final FacilityRepository facilityRepository;
+  private final ObjectTypeRepository objectTypeRepository;
+  private final FacilityClient facilityClient;
+  private final AuditLogger auditLogger;
+  private final Clock clock;
+
+  ImportPersister(
+      InspectionRepository inspectionRepository,
+      FacilityRepository facilityRepository,
+      ObjectTypeRepository objectTypeRepository,
+      FacilityClient facilityClient,
+      AuditLogger auditLogger,
+      Clock clock) {
+    this.inspectionRepository = inspectionRepository;
+    this.facilityRepository = facilityRepository;
+    this.objectTypeRepository = objectTypeRepository;
+    this.facilityClient = facilityClient;
+    this.auditLogger = auditLogger;
+    this.clock = clock;
+  }
+
+  Set<UUID> fetchExistingProcedureIds(List<UUID> procedureIds) {
+    if (procedureIds.isEmpty()) {
+      return Set.of();
+    } else {
+      List<UUID> list = inspectionRepository.collectExistingProceduresByExternalIds(procedureIds);
+      return new HashSet<>(list);
+    }
+  }
+
+  Optional<ObjectType> findObjectType(String objectTypeName) {
+    return objectTypeRepository.findByName(objectTypeName);
+  }
+
+  UUID addBaseFacility(ImportInspectionFacility importFacility, UUID facilityReferenceId) {
+    AddFacilityFileStateResponse response =
+        facilityClient.addFacilityFileState(
+            new AddFacilityFileStateRequest(
+                facilityReferenceId, importFacility.facilityDetailsDto(), DataOriginDto.IMPORT));
+    return response.id();
+  }
+
+  UUID getReferenceFacilityId(UUID centralFileStateId) {
+    return facilityClient.getReferenceFacility(centralFileStateId).id();
+  }
+
+  Facility addInspectionFacility(ImportInspectionFacility importFacility, UUID centralFileStateId) {
+    Facility facility = new Facility(importFacility.objectType(), centralFileStateId);
+    return facilityRepository.save(facility);
+  }
+
+  Inspection addInspection(
+      ImportInspection importInspection,
+      String facilityName,
+      Facility facility,
+      UUID centralFileStateId) {
+    UUID currentUserId = CurrentUserHelper.getCurrentUserId();
+    Integer standardDuration = facility.getObjectType().getStandardDuration();
+    Instant appointmentStart = importInspection.lastInspected();
+    Instant appointmentEnd = appointmentStart.plus(standardDuration, HOURS);
+    Clock clockStart = Clock.fixed(appointmentStart, clock.getZone());
+    Clock clockEnd = Clock.fixed(appointmentEnd, clock.getZone());
+
+    Inspection inspection = new Inspection();
+    inspection.setProcedureType(ProcedureType.INSPECTION);
+    inspection.setType(InspectionType.REGULAR); // TODO: change to IMPORTED later
+    inspection.setPhase(InspectionPhase.CLOSED);
+    inspection.setModifiedBy(currentUserId);
+    inspection.setResult(importInspection.result());
+    inspection.updateProcedureStatus(ProcedureStatus.CLOSED, clockEnd, auditLogger);
+
+    InspectionRelatedFacility inspectionRelatedFacility = new InspectionRelatedFacility();
+    inspectionRelatedFacility.setCentralFileStateId(centralFileStateId);
+    inspectionRelatedFacility.setFacilityType(FacilityType.INSPECTION);
+    inspectionRelatedFacility.setProcedure(inspection);
+    inspectionRelatedFacility.setFacility(facility);
+    inspection.addRelatedFacility(inspectionRelatedFacility);
+
+    InspectionTask task1 = inspection.createPlanningTask(currentUserId, clockStart);
+    InspectionTask task2 = inspection.createExecutionTask(clockStart);
+    InspectionTask task3 = inspection.createReportTask(clockStart);
+    task1.setTaskStatus(TaskStatus.CLOSED);
+    task2.setTaskStatus(TaskStatus.CLOSED);
+    task3.setTaskStatus(TaskStatus.CLOSED);
+
+    InspectionAppointment plannedAppointment = new InspectionAppointment();
+    plannedAppointment.setAppointmentStart(appointmentStart);
+    plannedAppointment.setAppointmentEnd(appointmentEnd);
+    inspection.setPlannedAppointment(plannedAppointment);
+
+    InspectionAppointment executionAppointment = new InspectionAppointment();
+    executionAppointment.setAppointmentStart(appointmentStart);
+    executionAppointment.setAppointmentEnd(appointmentEnd);
+    inspection.setExecutionAppointment(executionAppointment);
+
+    if (!isBlank(importInspection.incidents())) {
+      InspectionIncident inspectionIncident = new InspectionIncident();
+      inspectionIncident.setIncidentExternalId(UUID.randomUUID());
+      inspectionIncident.setTitle("Importiertes Vorkommnis");
+      inspectionIncident.setDescription(importInspection.incidents());
+      inspectionIncident.setManualPosition(0);
+      inspection.addIncident(inspectionIncident);
+    }
+
+    Report report = new Report();
+    ChecklistReportMapper.addTopLevelTitle(report, facilityName);
+    InspectionReportService.addDateOfInspection(report, inspection, clock);
+    ReportElementText hint = new ReportElementText();
+    hint.setText("Dieser Vorgang wurde importiert.");
+    hint.setEditable(false);
+    hint.setMoveable(false);
+    hint.setDeletable(false);
+    report.getReportElements().add(hint);
+    InspectionReportService.adjustPositions(report);
+    report.setInspection(inspection);
+    inspection.setReport(report);
+
+    return inspectionRepository.save(inspection);
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java
new file mode 100644
index 000000000..45604ce58
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.file.common.CustomMediaTypes;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import de.eshg.lib.xlsximport.util.FileResponseUtil;
+import de.eshg.rest.service.security.config.BaseUrls;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.io.IOException;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+@RequestMapping(path = ImporterController.BASE_URL)
+@Tag(name = "Importer")
+public class ImporterController {
+  public static final String BASE_URL = BaseUrls.Inspection.INSPECTION_IMPORT_CONTROLLER;
+
+  private static final DateTimeFormatter FILE_TIMESTAMP =
+      DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
+
+  private final ImporterService importerService;
+  private final InspectionFeatureToggle featureToggle;
+  private final Resource importTemplate;
+  private final Clock clock;
+
+  public ImporterController(
+      ImporterService inspectionImporterService,
+      InspectionFeatureToggle featureToggle,
+      @Value("classpath:templates/import/InspectionImportTemplate.xlsx") Resource importTemplate,
+      Clock clock) {
+    this.importerService = inspectionImporterService;
+    this.featureToggle = featureToggle;
+    this.importTemplate = importTemplate;
+    this.clock = clock;
+  }
+
+  @ApiResponse(
+      responseCode = "200",
+      content = @Content(mediaType = MediaType.ALL_VALUE, schema = @Schema(type = "object")))
+  @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+  @Operation(summary = "Start import processes")
+  @Transactional
+  public ResponseEntity<MultiValueMap<String, Object>> importProcesses(
+      @RequestPart("file") MultipartFile file) throws IOException {
+    featureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    ImportResult result = importerService.importProcesses(file);
+    return FileResponseUtil.mapImportResultToMultipartResponse(result, filename());
+  }
+
+  @GetMapping(
+      path = "/templates/inspection-import-template",
+      produces = CustomMediaTypes.APPLICATION_XLSX_VALUE)
+  @Operation(summary = "Get the XLSX inspection import template")
+  public ResponseEntity<Resource> getInspectionImportTemplate() {
+    featureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return FileResponseUtil.getTemplateFileResponse(importTemplate);
+  }
+
+  private String filename() {
+    return "ImportResult_%s.xlsx".formatted(LocalDateTime.now(clock).format(FILE_TIMESTAMP));
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java
new file mode 100644
index 000000000..d3f4dea7d
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/ImporterService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.lib.xlsximport.ImportValidator.validateFileExistsAndHasCorrectType;
+import static de.eshg.lib.xlsximport.ImportValidator.validateHeaderExists;
+import static de.eshg.lib.xlsximport.ImportValidator.validateSheet;
+
+import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.ImportValidator;
+import de.eshg.lib.xlsximport.XlsxNormalizer;
+import de.eshg.lib.xlsximport.model.ImportResult;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Clock;
+import java.util.List;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class ImporterService {
+  private final Clock clock;
+  private final ImportPersister importPersister;
+
+  public ImporterService(Clock clock, ImportPersister importPersister) {
+    this.clock = clock;
+    this.importPersister = importPersister;
+  }
+
+  /**
+   * Imports inspection processes from an Excel file.
+   *
+   * @param file The multipart file containing the Excel data to be imported.
+   * @return An ImportResultDto object that contains the results of the import operation.
+   */
+  public ImportResult importProcesses(MultipartFile file) throws IOException {
+    validateFileExistsAndHasCorrectType(file);
+
+    try (InputStream inputStream = file.getInputStream();
+        XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+      validateSheet(workbook);
+
+      XSSFSheet sheet = workbook.getSheetAt(0);
+      validateHeaderExists(sheet);
+
+      return importProcesses(sheet);
+    }
+  }
+
+  private ImportResult importProcesses(XSSFSheet sheet) throws IOException {
+    try (XlsxNormalizer xlsxNormalizer = new XlsxNormalizer()) {
+      XSSFSheet normalizedSheet = xlsxNormalizer.normalize(sheet);
+
+      List<InspectionListColumn> actualColumns =
+          ImportValidator.validateHeaderFormat(InspectionListColumn.values(), normalizedSheet);
+
+      InspectionImporter importer =
+          new InspectionImporter(
+              normalizedSheet,
+              new InspectionProcedureRowReader(
+                  normalizedSheet, actualColumns, importPersister, clock),
+              new FeedbackColumnAccessor(actualColumns),
+              importPersister);
+      return importer.process();
+    }
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java
new file mode 100644
index 000000000..f543c986b
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporter.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.lib.xlsximport.ImportStatus.BATCH_ERROR;
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_WITHIN_LIST;
+import static de.eshg.lib.xlsximport.ImportStatus.ERROR_INPUT_DATA;
+import static de.eshg.lib.xlsximport.ImportStatus.EXCEPTION;
+import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_PREVIOUSLY;
+import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_SUCCESSFULLY;
+import static de.eshg.lib.xlsximport.ImportStatus.INVALID_PROCEDURE_ID;
+import static java.util.Comparator.naturalOrder;
+import static java.util.Comparator.nullsLast;
+
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.Importer;
+import de.eshg.lib.xlsximport.RowReader;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InspectionImporter extends Importer<InspectionImporterRowValues, InspectionListColumn> {
+
+  private static final Logger log = LoggerFactory.getLogger(InspectionImporter.class);
+
+  private final ImportPersister importPersister;
+
+  /**
+   * This map groups all rows having the same importId. It maps to a TreeSet which is sorted by the
+   * lastInspected column.
+   */
+  private final Map<String, TreeSet<InspectionImporterRowValues>> rowsWithImportIds =
+      new LinkedHashMap<>();
+
+  /**
+   * This map groups all rows not having an importId, but having <i>exactly</i> the same facility
+   * data. Each facility maps to a TreeSet which is sorted by the lastInspected column. Note that if
+   * two rows have no importId, but have <i>almost</i> the same facility data, but not
+   * <i>exactly</i> the same, then this will be treated as totally different facilities! Currently,
+   * there is no similarity search!
+   */
+  private final Map<ImportInspectionFacility, TreeSet<InspectionImporterRowValues>>
+      rowsWithoutImportIds = new LinkedHashMap<>();
+
+  InspectionImporter(
+      XSSFSheet sheet,
+      RowReader<InspectionImporterRowValues, InspectionListColumn> rowReader,
+      FeedbackColumnAccessor feedbackColumnAccessor,
+      ImportPersister importPersister) {
+    super(sheet, rowReader, feedbackColumnAccessor);
+    this.importPersister = importPersister;
+  }
+
+  @Override
+  protected void readRowsAndEvaluateActions() {
+    Collection<InspectionImporterRowValues> values = readRows().values();
+    Set<UUID> existingProcedureIds = fetchExistingProcedureIds(values);
+
+    for (InspectionImporterRowValues rowValues : values) {
+      Row row = rowValues.getRow();
+      if (rowValues.getStatus() == DUPLICATE_WITHIN_LIST || containsMatchingRow(rowValues)) {
+        writeStatus(row, DUPLICATE_WITHIN_LIST);
+        rowValues.setStatus(DUPLICATE_WITHIN_LIST);
+        stats.countDuplicated();
+      } else {
+        if (rowValues.getProcedureId() != null) {
+          if (existingProcedureIds.contains(rowValues.getProcedureId())) {
+            writeStatus(row, IMPORTED_PREVIOUSLY);
+            rowValues.setStatus(IMPORTED_PREVIOUSLY);
+            stats.countPreviouslyImported();
+          } else {
+            writeStatus(row, INVALID_PROCEDURE_ID);
+            rowValues.setStatus(INVALID_PROCEDURE_ID);
+            stats.countFailed();
+          }
+        } else if (!rowValues.isValid()) {
+          writeStatus(row, ERROR_INPUT_DATA);
+          rowValues.setStatus(ERROR_INPUT_DATA);
+          stats.countFailed();
+        }
+        // Note that we even add _invalid_ rows to the result maps.
+        // This is intentional; in the next step createProceduresAndWriteResults() we'll
+        // check if any batch of rows having the same (facility) importId has any error;
+        // then we'll mark the _all_ rows of the same batch as error.
+        if (rowValues.hasImportId()) {
+          rowsWithImportIds
+              .computeIfAbsent(
+                  rowValues.getFacility().importId(), _id -> createSetSortedByLastInspected())
+              .add(rowValues);
+        } else {
+          rowsWithoutImportIds
+              .computeIfAbsent(rowValues.getFacility(), _f -> createSetSortedByLastInspected())
+              .add(rowValues);
+        }
+        // Add to importableRows() for duplicate check in containsMatchingRow()
+        validRows.importableRows().add(rowValues);
+      }
+    }
+
+    // Clear validRows() list to save memory. We don't need it in the following steps.
+    validRows.importableRows().clear();
+  }
+
+  @Override
+  protected void createProceduresAndWriteResults() {
+    handleRowsWithImportIds();
+    handleRowsWithoutImportIds();
+  }
+
+  @Override
+  protected void mergeProceduresAndWriteResults() {}
+
+  private void handleRowsWithImportIds() {
+    rowsWithImportIds.values().forEach(this::importBatchWithSameImportId);
+  }
+
+  private void handleRowsWithoutImportIds() {
+    rowsWithoutImportIds.forEach(this::importBatchWithExactlySameFacility);
+  }
+
+  /**
+   * Import a batch of rows having the same facility importId. The rows in the batch are sorted by
+   * "begangen am" (lastInspected) date.
+   */
+  private void importBatchWithSameImportId(TreeSet<InspectionImporterRowValues> batch) {
+    boolean batchHasError = false;
+    UUID facilityReferenceId = null;
+    Facility facility = null;
+
+    for (InspectionImporterRowValues rowValues : batch) {
+      if (hasError(rowValues)) {
+        // mark remaining rows of the same batch as BATCH_ERROR
+        batchHasError = true;
+      } else if (batchHasError) {
+        markAsBatchError(rowValues);
+      } else {
+        ImportInspectionFacility importFacility = rowValues.getFacility();
+        try {
+          UUID centralFileStateId =
+              importPersister.addBaseFacility(importFacility, facilityReferenceId);
+          if (facilityReferenceId == null) {
+            // ensure that the subsequent facility file states get the same referenceId
+            facilityReferenceId = importPersister.getReferenceFacilityId(centralFileStateId);
+            // add an inspection facility for the first file state,
+            // re-use this inspection facility for the subsequent inspections
+            facility = importPersister.addInspectionFacility(importFacility, centralFileStateId);
+          }
+          importInspection(rowValues, facility, centralFileStateId);
+        } catch (Exception ex) {
+          log.error("error importing row #{}", rowValues.getRow().getRowNum(), ex);
+          markWithException(rowValues);
+          batchHasError = true;
+        }
+      }
+    }
+  }
+
+  /**
+   * Import a batch of rows having exactly the same facility data (but no importId). The rows in the
+   * batch are sorted by "begangen am" (lastInspected) date.
+   */
+  private void importBatchWithExactlySameFacility(
+      ImportInspectionFacility importFacility, TreeSet<InspectionImporterRowValues> batch) {
+    Facility facility;
+    UUID facilityReferenceId;
+    // try to import facility
+    try {
+      UUID centralFileStateId = importPersister.addBaseFacility(importFacility, null);
+      facility = importPersister.addInspectionFacility(importFacility, centralFileStateId);
+      facilityReferenceId = importPersister.getReferenceFacilityId(centralFileStateId);
+    } catch (Exception ex) {
+      log.error("error importing row #{}", batch.first().getRow().getRowNum(), ex);
+      markWithException(batch.first());
+      // since we could not add the facility, mark the remaining inspection rows
+      // of the same batch as BATCH_ERROR
+      for (InspectionImporterRowValues rowValues : batch.tailSet(batch.first(), false)) {
+        markAsBatchError(rowValues);
+      }
+      return;
+    }
+
+    // try to import inspections for this facility
+    boolean batchHasError = false;
+    for (InspectionImporterRowValues rowValues : batch) {
+      if (hasError(rowValues)) {
+        // mark remaining rows of the same batch as BATCH_ERROR
+        batchHasError = true;
+      } else if (batchHasError) {
+        markAsBatchError(rowValues);
+      } else {
+        try {
+          UUID centralFileStateId =
+              importPersister.addBaseFacility(importFacility, facilityReferenceId);
+          importInspection(rowValues, facility, centralFileStateId);
+        } catch (Exception ex) {
+          log.error("error importing row #{}", rowValues.getRow().getRowNum(), ex);
+          markWithException(rowValues);
+          batchHasError = true;
+        }
+      }
+    }
+  }
+
+  private void importInspection(
+      InspectionImporterRowValues rowValues, Facility facility, UUID centralFileStateId) {
+    ImportInspection importInspection = rowValues.getInspection();
+    String facilityName = rowValues.getFacility().facilityDetailsDto().name();
+    Inspection inspection =
+        importPersister.addInspection(importInspection, facilityName, facility, centralFileStateId);
+    UUID procedureId = inspection.getExternalId();
+    writeStatusAndProcedureId(rowValues.getRow(), IMPORTED_SUCCESSFULLY, procedureId);
+    rowValues.setStatus(IMPORTED_SUCCESSFULLY);
+    rowValues.setProcedureId(procedureId);
+    stats.countCreated();
+  }
+
+  private Set<UUID> fetchExistingProcedureIds(Collection<InspectionImporterRowValues> values) {
+    List<UUID> procedureIds =
+        values.stream()
+            .map(InspectionImporterRowValues::getProcedureId)
+            .filter(Objects::nonNull)
+            .toList();
+    return importPersister.fetchExistingProcedureIds(procedureIds);
+  }
+
+  private boolean containsMatchingRow(InspectionImporterRowValues rowValues) {
+    return validRows.importableRows().stream().anyMatch(row -> row.isDuplicateRow(rowValues));
+  }
+
+  private static boolean hasError(InspectionImporterRowValues rowValues) {
+    if (!rowValues.isValid()) return true;
+    return switch (rowValues.getStatus()) {
+      case null -> false;
+      case ERROR_INPUT_DATA,
+              INVALID_PROCEDURE_ID,
+              IMPORTED_PREVIOUSLY,
+              DUPLICATE_WITHIN_LIST,
+              DUPLICATE_IN_ASSET,
+              EXCEPTION,
+              BATCH_ERROR,
+              MERGE_FAILED ->
+          true;
+      case IMPORTED_SUCCESSFULLY, MERGED_SUCCESSFULLY -> false;
+    };
+  }
+
+  private void markAsBatchError(InspectionImporterRowValues rowValues) {
+    writeStatus(rowValues.getRow(), BATCH_ERROR);
+    rowValues.setStatus(BATCH_ERROR);
+    stats.countFailed();
+  }
+
+  private void markWithException(InspectionImporterRowValues rowValues) {
+    writeStatus(rowValues.getRow(), EXCEPTION);
+    rowValues.setStatus(EXCEPTION);
+    stats.countFailed();
+  }
+
+  private static TreeSet<InspectionImporterRowValues> createSetSortedByLastInspected() {
+    return new TreeSet<>(
+        Comparator.comparing(
+            key -> key.getInspection().lastInspected(), nullsLast(naturalOrder())));
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java
new file mode 100644
index 000000000..0bcf1bd18
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionImporterRowValues.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.lib.xlsximport.RowValues;
+
+class InspectionImporterRowValues extends RowValues {
+  private ImportInspectionFacility facility;
+  private ImportInspection inspection;
+
+  public ImportInspectionFacility getFacility() {
+    return facility;
+  }
+
+  public void setFacility(ImportInspectionFacility facility) {
+    this.facility = facility;
+  }
+
+  public ImportInspection getInspection() {
+    return inspection;
+  }
+
+  public void setInspection(ImportInspection inspection) {
+    this.inspection = inspection;
+  }
+
+  public boolean hasImportId() {
+    return !isBlank(facility.importId());
+  }
+
+  public boolean isDuplicateRow(InspectionImporterRowValues other) {
+    return facility.equals(other.facility) && inspection.equals(other.inspection);
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java
new file mode 100644
index 000000000..381825981
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionListColumn.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import de.eshg.lib.xlsximport.XlsxColumn;
+
+enum InspectionListColumn implements XlsxColumn {
+  ID("ID", Necessity.OPTIONAL),
+  OBJECTTYPE("Objekttyp", Necessity.REQUIRED),
+  FACILITY_NAME("Name", Necessity.REQUIRED),
+  FACILITY_ZIPCODE("PLZ", Necessity.REQUIRED),
+  FACILITY_CITY("Ort", Necessity.REQUIRED),
+  FACILITY_STREET("Straße", Necessity.REQUIRED),
+  FACILITY_HOUSENUMBER("Hausnummer", Necessity.REQUIRED),
+  FACILITY_EMAIL("Email", Necessity.OPTIONAL),
+  FACILITY_PHONENUMBER("Telefon", Necessity.OPTIONAL),
+  CONTACT_SALUTATION("Kontakt Anrede", Necessity.OPTIONAL),
+  CONTACT_TITLE("Kontakt Titel", Necessity.OPTIONAL),
+  CONTACT_ROLE("Kontakt Rolle", Necessity.OPTIONAL),
+  CONTACT_FIRSTNAME("Kontakt Vorname", Necessity.OPTIONAL),
+  CONTACT_LASTNAME("Kontakt Name", Necessity.OPTIONAL),
+  CONTACT_EMAIL("Kontakt Email", Necessity.OPTIONAL),
+  CONTACT_PHONENUMBER("Kontakt Telefon", Necessity.OPTIONAL),
+  INSPECTED_AT("begangen am", Necessity.REQUIRED),
+  INSPECTION_RESULT("Ergebnis", Necessity.REQUIRED),
+  INSPECTION_INCIDENTS("Vorkommnisse", Necessity.OPTIONAL),
+  STATUS(STATUS_COLUMN_HEADER, Necessity.ADD_IF_MISSING, STATUS_COLUMN_HEADER_WIDTH),
+  PROCEDURE_ID(PROCEDURE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
+  ;
+
+  private final String header;
+  private final Necessity necessity;
+  private final int columnWidth;
+
+  InspectionListColumn(String header, Necessity necessity) {
+    this(header, necessity, 0);
+  }
+
+  InspectionListColumn(String header, Necessity necessity, int columnWidth) {
+    this.header = header;
+    this.necessity = necessity;
+    this.columnWidth = columnWidth;
+  }
+
+  @Override
+  public String getHeader() {
+    return header;
+  }
+
+  @Override
+  public Necessity getNecessity() {
+    return necessity;
+  }
+
+  @Override
+  public int getColumnWidth() {
+    return columnWidth;
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java
new file mode 100644
index 000000000..eb93fdf4a
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.importer;
+
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_EMAIL;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_FIRSTNAME;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_LASTNAME;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_PHONENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_ROLE;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_SALUTATION;
+import static de.eshg.inspection.importer.InspectionListColumn.CONTACT_TITLE;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_CITY;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_EMAIL;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_HOUSENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_NAME;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_PHONENUMBER;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_STREET;
+import static de.eshg.inspection.importer.InspectionListColumn.FACILITY_ZIPCODE;
+import static de.eshg.inspection.importer.InspectionListColumn.ID;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTED_AT;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTION_INCIDENTS;
+import static de.eshg.inspection.importer.InspectionListColumn.INSPECTION_RESULT;
+import static de.eshg.inspection.importer.InspectionListColumn.OBJECTTYPE;
+import static de.eshg.inspection.importer.InspectionListColumn.PROCEDURE_ID;
+import static de.eshg.inspection.importer.InspectionListColumn.STATUS;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+import de.eshg.base.GenderDto;
+import de.eshg.base.SalutationDto;
+import de.eshg.base.address.DomesticAddressDto;
+import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto;
+import de.eshg.base.centralfile.api.facility.FacilityDetailsDto;
+import de.eshg.inspection.inspection.api.InspectionResult;
+import de.eshg.inspection.objecttype.persistence.ObjectType;
+import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
+import de.eshg.lib.xlsximport.RowReader;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+
+class InspectionProcedureRowReader
+    extends RowReader<InspectionImporterRowValues, InspectionListColumn> {
+
+  private final ImportPersister importPersister;
+  private final Clock clock;
+
+  public InspectionProcedureRowReader(
+      Sheet sheet,
+      List<InspectionListColumn> actualColumns,
+      ImportPersister importPersister,
+      Clock clock) {
+    super(sheet, actualColumns);
+    this.importPersister = importPersister;
+    this.clock = clock;
+  }
+
+  @Override
+  protected InspectionImporterRowValues read(ColumnAccessor<InspectionListColumn> col) {
+    InspectionImporterRowValues result = new InspectionImporterRowValues();
+    ErrorHandler errorHandler = createErrorHandler(result);
+
+    result.setFacility(readFacilityData(col, errorHandler));
+    result.setInspection(readInspectionData(col, errorHandler));
+    result.setStatus(readStatus(col, STATUS, errorHandler));
+    result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
+
+    return result;
+  }
+
+  private ImportInspectionFacility readFacilityData(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    String importId = cellAsString(col, ID, true, true, errorHandler);
+
+    String objectTypeName = cellAsString(col, OBJECTTYPE, errorHandler);
+    Optional<ObjectType> objectType = importPersister.findObjectType(objectTypeName);
+    if (objectType.isEmpty()) {
+      errorHandler.handleError(col.get(OBJECTTYPE), "Unbekannter Objekttyp");
+    }
+
+    FacilityDetailsDto facilityDetailsDto = readFacilityDetails(col, errorHandler);
+
+    return new ImportInspectionFacility(importId, objectType.orElse(null), facilityDetailsDto);
+  }
+
+  private FacilityDetailsDto readFacilityDetails(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    String name = cellAsString(col, FACILITY_NAME, errorHandler);
+    String zipCode = cellAsString(col, FACILITY_ZIPCODE, false, true, errorHandler);
+    String city = cellAsString(col, FACILITY_CITY, errorHandler);
+    String street = cellAsString(col, FACILITY_STREET, errorHandler);
+    String housenumber = cellAsString(col, FACILITY_HOUSENUMBER, false, true, errorHandler);
+    String email = cellAsString(col, FACILITY_EMAIL, true, false, errorHandler);
+    String phonenumber = cellAsString(col, FACILITY_PHONENUMBER, true, true, errorHandler);
+
+    DomesticAddressDto contactAddress =
+        new DomesticAddressDto(CountryCode.DE, city, zipCode, null, street, housenumber, null);
+
+    FacilityContactPersonDto contactPerson = readFacilityContactPerson(col, errorHandler);
+
+    return new FacilityDetailsDto(
+        name,
+        isBlank(email) ? List.of() : List.of(email),
+        isBlank(phonenumber) ? List.of() : List.of(phonenumber),
+        contactPerson == null ? List.of() : List.of(contactPerson),
+        contactAddress,
+        null);
+  }
+
+  private FacilityContactPersonDto readFacilityContactPerson(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    SalutationDto salutation = cellAsSalutation(col, CONTACT_SALUTATION, errorHandler);
+    String title = cellAsString(col, CONTACT_TITLE, true, false, errorHandler);
+    String role = cellAsString(col, CONTACT_ROLE, true, false, errorHandler);
+    String firstName = cellAsString(col, CONTACT_FIRSTNAME, true, false, errorHandler);
+    String lastName = cellAsString(col, CONTACT_LASTNAME, true, false, errorHandler);
+    String email = cellAsString(col, CONTACT_EMAIL, true, false, errorHandler);
+    String phonenumber = cellAsString(col, CONTACT_PHONENUMBER, true, false, errorHandler);
+
+    if (salutation == null
+        && isBlank(title)
+        && isBlank(role)
+        && isBlank(firstName)
+        && isBlank(lastName)
+        && isBlank(email)
+        && isBlank(phonenumber)) {
+      return null;
+    }
+
+    return new FacilityContactPersonDto(
+        email, phonenumber, role, lastName, firstName, title, salutation, GenderDto.NOT_SPECIFIED);
+  }
+
+  private ImportInspection readInspectionData(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    LocalDateTime inspectedAt = cellAsDateTime(col, INSPECTED_AT, errorHandler);
+    InspectionResult inspectionResult = cellAsInspectionResult(col, errorHandler);
+    String incidents = cellAsString(col, INSPECTION_INCIDENTS, true, true, errorHandler);
+    return new ImportInspection(toInstant(inspectedAt), inspectionResult, incidents);
+  }
+
+  private InspectionResult cellAsInspectionResult(
+      ColumnAccessor<InspectionListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(INSPECTION_RESULT);
+    String result = cellAsString(col, INSPECTION_RESULT, false, false, errorHandler);
+    return switch (result) {
+      case "Erfolgreich" -> InspectionResult.SUCCESSFUL;
+      case "Erfolgreich mit Beanstandungen" -> InspectionResult.SUCCESSFUL_WITH_INCIDENTS;
+      case "Negativ" -> InspectionResult.FAILED;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: \"Erfolgreich\", \"Erfolgreich mit Beanstandungen\", \"Negativ\"; Tatsächlich: \"%s\")"
+                .formatted(result));
+        yield null;
+      }
+    };
+  }
+
+  private Instant toInstant(LocalDateTime dateTime) {
+    if (dateTime == null) return null;
+    if (dateTime.getHour() == 0 && dateTime.getMinute() == 0 && dateTime.getSecond() == 0) {
+      // if the time is 0:00h then there is no time set in the excel-sheet
+      dateTime = dateTime.plusHours(10);
+    }
+    return dateTime.atZone(clock.getZone()).toInstant();
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
index 6d8dc49f3..60194e97d 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionController.java
@@ -8,6 +8,8 @@ package de.eshg.inspection.inspection;
 import static de.eshg.rest.service.error.ErrorCode.INSUFFICIENT_USER_RIGHTS;
 
 import de.eshg.base.centralfile.api.facility.PutFacilityRequest;
+import de.eshg.inspection.feature.InspectionFeature;
+import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.api.*;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.rest.service.error.BadRequestException;
@@ -42,9 +44,17 @@ public class InspectionController {
   public static final String BASE_URL = BaseUrls.Inspection.INSPECTION_CONTROLLER;
 
   private final InspectionService inspectionService;
+  private final ReviewService reviewService;
 
-  public InspectionController(InspectionService inspectionService) {
+  private final InspectionFeatureToggle inspectionFeatureToggle;
+
+  public InspectionController(
+      InspectionService inspectionService,
+      ReviewService reviewService,
+      InspectionFeatureToggle inspectionFeatureToggle) {
     this.inspectionService = inspectionService;
+    this.reviewService = reviewService;
+    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @PostMapping(path = "/{id}")
@@ -56,13 +66,6 @@ public class InspectionController {
     return inspectionService.startInspection(procedureId, request);
   }
 
-  @GetMapping
-  @Operation(summary = "Get list of all inspections, sorted by title")
-  @Transactional(readOnly = true)
-  public GetInspectionsResponse getInspections() {
-    return new GetInspectionsResponse(inspectionService.loadAllInspections());
-  }
-
   @GetMapping(path = "/{id}")
   @Operation(summary = "Get details of an inspection")
   @Transactional(readOnly = true)
@@ -182,6 +185,43 @@ associated reference facility
     return inspectionService.getAvailablePLDs(externalId);
   }
 
+  @PostMapping(path = "/{id}/resolve-inspection-duplicate")
+  @Operation(
+      summary =
+          "Resolves an inspection duplicate for an inspection by choosing whether to keep or discard an inspection")
+  @Transactional
+  public void resolveInspectionDuplicate(
+      @Parameter(description = "The id of the inspection") @PathVariable("id") UUID id,
+      @RequestBody @Valid ResolveInspectionDuplicateRequest request) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    reviewService.resolveInspectionDuplicate(id, request.keepInspection());
+  }
+
+  @PostMapping(path = "/{id}/resolve-facility-duplicate")
+  @Operation(summary = "Resolves a facility duplicate for an inspection by choosing a facility")
+  public void resolveFacilityDuplicate(
+      @Parameter(description = "The id of the inspection") @PathVariable("id") UUID id,
+      @RequestBody @Valid ResolveFacilityDuplicateRequest request) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    reviewService.resolveFacilityDuplicate(id, request.chosenReferenceId());
+  }
+
+  @GetMapping(path = "/{id}/inspection-duplicates")
+  @Operation(summary = "Get inspection duplicates of an inspection")
+  @Transactional(readOnly = true)
+  public InspectionDuplicateReviewDto getInspectionDuplicates(@PathVariable("id") UUID externalId) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return reviewService.reviewInspectionDuplicates(externalId);
+  }
+
+  @GetMapping(path = "/{id}/facility-duplicates")
+  @Operation(summary = "Get facility duplicates of an inspection")
+  @Transactional(readOnly = true)
+  public FacilityDuplicateReviewDto getFacilityDuplicates(@PathVariable("id") UUID externalId) {
+    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.IMPORT);
+    return reviewService.reviewFacilityDuplicates(externalId);
+  }
+
   private static void validateAssignmentRole(UUID assigneeId) {
     if (assigneeId != null
         && !assigneeId.equals(CurrentUserHelper.getCurrentUserId())
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
index b33221639..daacf05ca 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionFinalizer.java
@@ -32,7 +32,6 @@ import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
@@ -200,9 +199,7 @@ public class InspectionFinalizer {
     pdfMetaData.setCreatedDate(reportDate.toInstant());
     pdfMetaData.setDescription(reportData.inspection().title());
     String filename = reportData.reportInfo().filename();
-    Pdf pdf =
-        FileFactory.createPdfWithMetaData(
-            filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    Pdf pdf = FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
 
     report.setReportFile(pdf);
   }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
index 28b288f72..b6194afb7 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionMapper.java
@@ -9,7 +9,6 @@ import static java.util.Optional.ofNullable;
 
 import de.eshg.base.calendar.CalendarEventApi;
 import de.eshg.base.calendar.api.GetBusinessCaseEventResponse;
-import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.inventory.InventoryApi;
 import de.eshg.base.resource.ResourceApi;
@@ -34,6 +33,7 @@ import de.eshg.inspection.inspection.api.InspectionCLDVersionDto;
 import de.eshg.inspection.inspection.api.InspectionDto;
 import de.eshg.inspection.inspection.api.InspectionDto.ReportInfoDto;
 import de.eshg.inspection.inspection.api.InspectionFollowupInfoDto;
+import de.eshg.inspection.inspection.api.InspectionForDuplicateReviewDto;
 import de.eshg.inspection.inspection.api.InspectionInventoryDto;
 import de.eshg.inspection.inspection.api.InspectionPLDRevisionDto;
 import de.eshg.inspection.inspection.api.InspectionResourceDto;
@@ -57,7 +57,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
 import java.util.stream.Stream;
@@ -118,7 +117,7 @@ public class InspectionMapper {
     return mapToInspectionTitle(facilityDto.baseFacility().name());
   }
 
-  InspectionDto mapToDto(Inspection inspection) {
+  public InspectionDto mapToDto(Inspection inspection) {
     InspFacilityDto facilityDto =
         mapToDto(inspection.getCentralFileStateId(), inspection.getFacility());
     return mapToDto(inspection, facilityDto);
@@ -183,7 +182,9 @@ public class InspectionMapper {
         mapFollowupInfoToDto(inspection),
         mapIncidents(inspection),
         assignee,
-        lockedByUser);
+        lockedByUser,
+        inspection.getFacility().hasPossibleDuplicates(),
+        !inspection.getPossibleDuplicates().isEmpty());
   }
 
   private List<InspectionInventoryDto> mapInventories(Inspection inspection) {
@@ -252,25 +253,6 @@ public class InspectionMapper {
                     Comparator.nullsLast(Integer::compareTo)));
   }
 
-  List<InspectionDto> mapToDtos(List<Inspection> inspections) {
-    if (inspections.isEmpty()) return Collections.emptyList();
-
-    List<UUID> centralFileStateIds =
-        inspections.stream().map(Inspection::getCentralFileStateId).toList();
-    List<Facility> facilities = inspections.stream().map(Inspection::getFacility).toList();
-    List<AddFacilityFileStateResponse> baseFacilities =
-        facilityClient.getFacilityFileStates(centralFileStateIds);
-    Map<UUID, InspFacilityDto> facilityMap =
-        FacilityMapper.facilitiesFrom(facilities, baseFacilities);
-
-    return inspections.stream()
-        .map(
-            inspection ->
-                mapToDto(inspection, facilityMap.get(inspection.getFacility().getExternalId())))
-        .sorted(Comparator.comparing(InspectionDto::title))
-        .toList();
-  }
-
   private InspectionAppointmentDto mapAppointment(
       InspectionAppointment appointment, Optional<InspectionTask> task) {
     if (appointment == null) return null;
@@ -392,4 +374,26 @@ public class InspectionMapper {
     return new InspectionAnnouncementDto(
         inspectionAnnouncement.getDate(), inspectionAnnouncement.getType());
   }
+
+  public static InspectionForDuplicateReviewDto mapToDtoForDuplicateReview(
+      Inspection inspection, String title) {
+
+    Instant executedTime =
+        Optional.ofNullable(inspection.getExecutionAppointment())
+            .map(InspectionAppointment::getAppointmentStart)
+            .orElse(
+                Optional.ofNullable(inspection.getPlannedAppointment())
+                    .map(InspectionAppointment::getAppointmentStart)
+                    .orElse( // This case should never happen but just in case we handle it without
+                        // throwing an exception
+                        Instant.ofEpochSecond(0)));
+
+    return new InspectionForDuplicateReviewDto(
+        inspection.getExternalId(),
+        title,
+        inspection.getType(),
+        inspection.getResult(),
+        executedTime,
+        inspection.getIncidents().size());
+  }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
index 8ca08e463..26ff6595b 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/InspectionService.java
@@ -253,11 +253,6 @@ public class InspectionService {
     return inspectionMapper.mapToDto(inspection);
   }
 
-  public List<InspectionDto> loadAllInspections() {
-    List<Inspection> inspections = inspectionRepository.findAll();
-    return inspectionMapper.mapToDtos(inspections);
-  }
-
   public Inspection findNewestOpenInspectionForFacility(Facility facility) {
     return inspectionRepository.findNewestOpenInspectionForFacility(facility);
   }
@@ -459,7 +454,6 @@ public class InspectionService {
     if (StringUtils.isNotBlank(progressEntryText)) {
       ManualProgressEntry manualProgressEntry = new ManualProgressEntry();
       manualProgressEntry.setManualProgressEntryType(ManualProgressEntryType.NOTE);
-      manualProgressEntry.setSubject("Anmerkung bei Begehungserstellung");
       manualProgressEntry.setNote(progressEntryText);
       inspection.addProgressEntry(manualProgressEntry);
     }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java
new file mode 100644
index 000000000..6eff52ce1
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/ReviewService.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection;
+
+import static java.util.stream.Collectors.toMap;
+
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.SearchReferenceFacilitiesResponse;
+import de.eshg.domain.model.SequencedBaseEntity;
+import de.eshg.inspection.facility.FacilityClient;
+import de.eshg.inspection.facility.FacilityMapper;
+import de.eshg.inspection.facility.persistence.Facility;
+import de.eshg.inspection.facility.persistence.FacilityRepository;
+import de.eshg.inspection.inspection.api.FacilityDuplicateReviewDto;
+import de.eshg.inspection.inspection.api.FacilityForDuplicateReviewDto;
+import de.eshg.inspection.inspection.api.InspectionDto;
+import de.eshg.inspection.inspection.api.InspectionDuplicateReviewDto;
+import de.eshg.inspection.inspection.persistence.Inspection;
+import de.eshg.inspection.inspection.persistence.InspectionRepository;
+import de.eshg.lib.procedure.procedures.ProcedureDeletionService;
+import de.eshg.persistence.TransactionHelper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ReviewService {
+
+  private static final Logger log = LoggerFactory.getLogger(ReviewService.class);
+
+  private final InspectionService inspectionService;
+  private final FacilityClient facilityClient;
+  private final FacilityRepository facilityRepository;
+  private final InspectionRepository inspectionRepository;
+  private final TransactionHelper transactionHelper;
+  private final ProcedureDeletionService<Inspection> inspectionDeletionService;
+
+  public ReviewService(
+      InspectionService inspectionService,
+      FacilityClient facilityClient,
+      FacilityRepository facilityRepository,
+      InspectionRepository inspectionRepository,
+      TransactionHelper transactionHelper,
+      ProcedureDeletionService<Inspection> inspectionDeletionService) {
+    this.inspectionService = inspectionService;
+    this.facilityClient = facilityClient;
+    this.facilityRepository = facilityRepository;
+    this.inspectionRepository = inspectionRepository;
+    this.transactionHelper = transactionHelper;
+    this.inspectionDeletionService = inspectionDeletionService;
+  }
+
+  public InspectionDuplicateReviewDto reviewInspectionDuplicates(UUID inspectionId) {
+    Inspection inspection = inspectionService.loadInspection(inspectionId);
+
+    GetFacilityFileStateResponse facilityFileState =
+        facilityClient.getFacilityFileState(inspection.getFacility().getCentralFileStateId());
+
+    String title = facilityFileState.name();
+
+    return new InspectionDuplicateReviewDto(
+        InspectionMapper.mapToDtoForDuplicateReview(inspection, title),
+        inspection.getPossibleDuplicates().stream()
+            .map(i -> InspectionMapper.mapToDtoForDuplicateReview(i, title))
+            .toList());
+  }
+
+  public FacilityDuplicateReviewDto reviewFacilityDuplicates(UUID inspectionId) {
+    InspectionDto inspection = inspectionService.loadInspectionDTO(inspectionId);
+
+    UUID referenceIdOfImportedFacility =
+        facilityClient.getReferenceFacility(inspection.facility().baseFacility().id()).id();
+
+    FacilityForDuplicateReviewDto inspectionFacility =
+        FacilityMapper.mapToFacilityForDuplicateReviewDto(
+            referenceIdOfImportedFacility,
+            inspection.facility().baseFacility(),
+            inspection.facility().objectType());
+
+    String facilityName = inspection.facility().baseFacility().name();
+    SearchReferenceFacilitiesResponse searchResponse =
+        facilityClient.searchReferenceFacilities(facilityName);
+
+    return new FacilityDuplicateReviewDto(
+        inspectionFacility,
+        searchResponse.facilities().stream()
+            .filter(f -> !f.id().equals(referenceIdOfImportedFacility))
+            .map(FacilityMapper::mapToFacilityForDuplicateReviewDto)
+            .sorted( // We sort to keep the order deterministic for the tests
+                Comparator.comparing(FacilityForDuplicateReviewDto::name)
+                    .thenComparing(FacilityForDuplicateReviewDto::street)
+                    .thenComparing(FacilityForDuplicateReviewDto::houseNo)
+                    .thenComparing(FacilityForDuplicateReviewDto::addressAddition)
+                    .thenComparing(FacilityForDuplicateReviewDto::postalCode)
+                    .thenComparing(FacilityForDuplicateReviewDto::city)
+                    .thenComparing(f -> String.join(",", f.emailAddresses()))
+                    .thenComparing(f -> String.join(",", f.phoneNumbers()))
+                    .thenComparing(f -> f.objectType().name())
+                    .thenComparing(FacilityForDuplicateReviewDto::referenceId)
+                    .thenComparing(f -> f.objectType().id()))
+            .toList());
+  }
+
+  public void resolveInspectionDuplicate(UUID inspectionId, boolean keepInspection) {
+    Inspection inspection = inspectionService.loadInspectionForUpdate(inspectionId);
+
+    if (inspection.getPossibleDuplicates().isEmpty()) {
+      throw new BadRequestException(
+          ErrorCode.BAD_REQUEST,
+          String.format(
+              "Inspection with external id %s is not marked as duplicate.",
+              inspection.getExternalId().toString()));
+    }
+
+    if (keepInspection) {
+      inspection.getPossibleDuplicates().clear();
+    } else {
+      inspectionRepository.deleteInspectionFromDuplicatesLists(inspection.getId());
+      inspectionDeletionService.delete(inspectionId);
+    }
+  }
+
+  public void resolveFacilityDuplicate(UUID inspectionId, UUID chosenReferenceId) {
+    AtomicBoolean deleteFacility = new AtomicBoolean(false);
+    Set<UUID> centralFileStatesToDelete = new HashSet<>();
+    Set<Long> inspectionIdsToUpdate = new HashSet<>();
+
+    transactionHelper.executeInTransaction(
+        () -> {
+          Inspection importedInspection = inspectionService.loadInspectionForUpdate(inspectionId);
+          Facility inspectionFacility = importedInspection.getFacility();
+          UUID referenceIdOfImportedInspection =
+              facilityClient.getReferenceFacility(importedInspection.getCentralFileStateId()).id();
+
+          if (!inspectionFacility.hasPossibleDuplicates()) {
+            throw new BadRequestException(
+                ErrorCode.BAD_REQUEST,
+                String.format(
+                    "Facility of inspection with external id %s is not marked as duplicate.",
+                    importedInspection.getExternalId().toString()));
+          }
+
+          // If the chosen facility is the one created in the import, we use that one.
+          if (referenceIdOfImportedInspection.equals(chosenReferenceId)) {
+            inspectionFacility.setPossibleDuplicates(false);
+            return;
+          }
+
+          // Otherwise determine all centralFileStateIds for that chosenReferenceId and find all
+          // associated inspections and their facilities.
+          List<UUID> centralFileStateIdsOfChosenFacility =
+              facilityClient.getFacilityFileStateIdsAssociatedWithReferenceFacility(
+                  chosenReferenceId);
+          List<Inspection> allInspectionsOfReferenceFacility =
+              inspectionRepository.findByCentralFileStateIds(centralFileStateIdsOfChosenFacility);
+          Collection<Facility> facilities =
+              allInspectionsOfReferenceFacility.stream()
+                  .map(Inspection::getFacility)
+                  .collect(toMap(Facility::getId, v -> v, (v, w) -> v))
+                  .values();
+
+          // determine the "target inspection facility" (the one that remains)
+          Facility targetFacility =
+              switch (facilities.size()) {
+                case 0 -> inspectionFacility;
+                case 1 -> facilities.iterator().next();
+                default -> {
+                  Facility first = facilities.iterator().next();
+                  log.error(
+                      "Found {} inspection facilities for these centralFileStateIds: {}; using the first one with id {}",
+                      facilities.size(),
+                      centralFileStateIdsOfChosenFacility,
+                      first.getId());
+                  yield first;
+                }
+              };
+
+          if (targetFacility == inspectionFacility) {
+            inspectionFacility.setPossibleDuplicates(false);
+
+            UUID previousFileStateId = inspectionFacility.getCentralFileStateId();
+            centralFileStatesToDelete.add(previousFileStateId);
+            inspectionFacility.setCentralFileStateId(
+                createNewFileState(chosenReferenceId, previousFileStateId));
+          }
+
+          List<Inspection> allInspectionsOfImportedInspection =
+              inspectionRepository.findAllInspectionsForFacility(inspectionFacility);
+
+          // We memorize the ids of the inspections we update here, so we can validate later that
+          // they no longer have the central file state ids we want to delete
+          inspectionIdsToUpdate.addAll(
+              allInspectionsOfImportedInspection.stream()
+                  .map(SequencedBaseEntity::getId)
+                  .collect(Collectors.toSet()));
+
+          for (Inspection inspectionToBeUpdated : allInspectionsOfImportedInspection) {
+            // We record the old central file state IDs here because we will overwrite them, but we
+            // won't set the deleteFacility flag before the end of the transaction in case it fails
+            // and rolls back
+            UUID previousFileStateId = inspectionToBeUpdated.getCentralFileStateId();
+            centralFileStatesToDelete.add(previousFileStateId);
+
+            // Replace the current centralFileState of the inspection with a new centralFileState
+            // that has the same data, but with the 'chosenReferenceId' as new reference id:
+            inspectionToBeUpdated
+                .getRelatedFacility()
+                .setCentralFileStateId(createNewFileState(chosenReferenceId, previousFileStateId));
+
+            // Change the reference of the inspection to the new facility
+            if (targetFacility != inspectionFacility) {
+              inspectionToBeUpdated.getRelatedFacility().setFacility(targetFacility);
+            } else {
+              // If the targetFacility is the original facility, its central file state id also has
+              // to be updated
+              inspectionFacility.setCentralFileStateId(
+                  inspectionToBeUpdated.getCentralFileStateId());
+            }
+          }
+
+          if (targetFacility != inspectionFacility) {
+            facilityRepository.delete(inspectionFacility);
+            centralFileStatesToDelete.add(inspectionFacility.getCentralFileStateId());
+          }
+
+          for (Inspection inspectionToBeUpdated : allInspectionsOfImportedInspection) {
+            checkForInspectionDuplicates(inspectionToBeUpdated);
+          }
+
+          deleteFacility.set(true);
+        });
+
+    if (deleteFacility.get() && !centralFileStatesToDelete.isEmpty()) {
+      // We read the central file state ids used for this facility in order to double-check if it
+      // is safe to delete the obsolete central file states
+      Set<UUID> centralFileStateIdsInDatabase =
+          transactionHelper.executeInReadOnlyTransaction(
+              () -> {
+                Inspection inspection = inspectionService.loadInspection(inspectionId);
+                List<Inspection> allInspectionsOfFacility =
+                    inspectionRepository.findAllById(inspectionIdsToUpdate);
+                Set<UUID> centralFileStateIds = new HashSet<>();
+                centralFileStateIds.add(inspection.getFacility().getCentralFileStateId());
+                centralFileStateIds.add(inspection.getCentralFileStateId());
+                centralFileStateIds.addAll(
+                    allInspectionsOfFacility.stream()
+                        .map(Inspection::getCentralFileStateId)
+                        .collect(Collectors.toSet()));
+                return centralFileStateIds;
+              });
+
+      if (!Collections.disjoint(centralFileStatesToDelete, centralFileStateIdsInDatabase)) {
+        throw new IllegalStateException(
+            "Facility to be deleted was not successfully removed from all inspections");
+      } else {
+        facilityClient.markFacilityFileStateForDeletion(centralFileStatesToDelete);
+      }
+    }
+  }
+
+  private UUID createNewFileState(UUID chosenReferenceId, UUID previousFileStateId) {
+    GetFacilityFileStateResponse previousFileState =
+        facilityClient.getFacilityFileState(previousFileStateId);
+
+    AddFacilityFileStateResponse newFileState =
+        facilityClient.addFacilityFileState(
+            new AddFacilityFileStateRequest(
+                chosenReferenceId,
+                previousFileState.name(),
+                previousFileState.emailAddresses(),
+                previousFileState.phoneNumbers(),
+                previousFileState.contactPersons(),
+                previousFileState.contactAddress(),
+                previousFileState.differentBillingAddress(),
+                previousFileState.dataOrigin(),
+                null));
+    return newFileState.id();
+  }
+
+  private void checkForInspectionDuplicates(Inspection inspection) {
+    UUID centralFileStateId = inspection.getFacility().getCentralFileStateId();
+
+    List<UUID> centralFileStateIds =
+        facilityClient.getFacilityFileStateIdsWithSameReferenceFacility(centralFileStateId);
+
+    Instant startTime =
+        Optional.ofNullable(inspection.getExecutionAppointment())
+            .orElse(inspection.getPlannedAppointment())
+            .getAppointmentStart()
+            .truncatedTo(ChronoUnit.DAYS);
+    Instant endTime = startTime.plus(1, ChronoUnit.DAYS);
+
+    List<Inspection> possibleInspectionDuplicates =
+        inspectionRepository.findByCentralFileStateIdsAndAppointment(
+            centralFileStateIds, startTime, endTime, inspection.getId());
+
+    if (!possibleInspectionDuplicates.isEmpty()) {
+      Collection<Inspection> newUniqueValues =
+          Stream.concat(
+                  inspection.getPossibleDuplicates().stream(),
+                  possibleInspectionDuplicates.stream())
+              .collect(toMap(Inspection::getId, v -> v, (v, w) -> v))
+              .values();
+      inspection.getPossibleDuplicates().clear();
+      inspection.getPossibleDuplicates().addAll(newUniqueValues);
+    }
+  }
+}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java
new file mode 100644
index 000000000..5843fc061
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityDuplicateReviewDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "FacilityDuplicateReview")
+public record FacilityDuplicateReviewDto(
+    @NotNull @Valid FacilityForDuplicateReviewDto importedFacility,
+    @NotNull @Valid List<FacilityForDuplicateReviewDto> existingFacilities) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java
new file mode 100644
index 000000000..c0cf01245
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/FacilityForDuplicateReviewDto.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import de.eshg.inspection.objecttype.api.ObjectTypeRefDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+@Schema(name = "FacilityForDuplicateReview")
+public record FacilityForDuplicateReviewDto(
+    // note: referenceId may not be persisted!
+    @NotNull UUID referenceId,
+    // objectType is only set for the entry representing the imported inspection/facility
+    @Valid ObjectTypeRefDto objectType,
+    @NotNull String name,
+    @NotNull String street,
+    String houseNo,
+    String addressAddition,
+    @NotNull String postalCode,
+    @NotNull String city,
+    @NotNull List<String> emailAddresses,
+    @NotNull List<String> phoneNumbers) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
index dce475a59..34a2ae752 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDto.java
@@ -40,7 +40,9 @@ public record InspectionDto(
     @Valid InspectionFollowupInfoDto followupInfo,
     @Valid List<InspectionIncidentDto> incidents,
     @Valid UserDto assignee,
-    @Valid UserDto lockedByUser) {
+    @Valid UserDto lockedByUser,
+    @NotNull boolean possibleFacilityDuplicate,
+    @NotNull boolean possibleInspectionDuplicate) {
 
   @Schema(name = "ReportInfo")
   public record ReportInfoDto(
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java
new file mode 100644
index 000000000..166cb8b7d
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionDuplicateReviewDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "InspectionDuplicateReview")
+public record InspectionDuplicateReviewDto(
+    @NotNull @Valid InspectionForDuplicateReviewDto importedInspection,
+    @NotNull @Valid List<InspectionForDuplicateReviewDto> existingInspections) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java
new file mode 100644
index 000000000..aa8e3c660
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionForDuplicateReviewDto.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.util.UUID;
+
+@Schema(name = "InspectionForDuplicateReview")
+public record InspectionForDuplicateReviewDto(
+    @NotNull UUID externalId,
+    @NotNull String title,
+    @NotNull InspectionType type,
+    @NotNull InspectionResult result,
+    @NotNull Instant executedTime,
+    @NotNull int numberOfIncidents) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
index 902512738..909fc5214 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/InspectionType.java
@@ -14,7 +14,8 @@ public enum InspectionType {
   REVIEW,
   INITIAL,
   COMPLAINT,
-  DOCUMENT_INSPECTION;
+  DOCUMENT_INSPECTION,
+  IMPORT;
 
   public boolean isComplaint() {
     return this == REVIEW || this == COMPLAINT || this == DOCUMENT_INSPECTION;
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
similarity index 56%
rename from backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java
rename to backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
index e14b0c51a..c75a7f188 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/GetInspectionsResponse.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveFacilityDuplicateRequest.java
@@ -6,9 +6,8 @@
 package de.eshg.inspection.inspection.api;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
-import java.util.List;
+import java.util.UUID;
 
-@Schema(name = "GetInspectionsResponse")
-public record GetInspectionsResponse(@NotNull @Valid List<InspectionDto> inspections) {}
+@Schema(name = "ResolveFacilityDuplicateRequest")
+public record ResolveFacilityDuplicateRequest(@NotNull UUID chosenReferenceId) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java
new file mode 100644
index 000000000..2f5b72c91
--- /dev/null
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/api/ResolveInspectionDuplicateRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.inspection.inspection.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+
+@Schema(name = "ResolveInspectionDuplicateRequest")
+public record ResolveInspectionDuplicateRequest(@NotNull boolean keepInspection) {}
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
index acf869236..96ea396c1 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/Inspection.java
@@ -172,6 +172,16 @@ public class Inspection
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private Instant followupDate;
 
+  @NotNull
+  @ManyToMany(fetch = FetchType.LAZY)
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @OrderBy
+  @JoinTable(
+      name = "inspection_possible_duplicates",
+      joinColumns = {@JoinColumn(name = "inspection_id")},
+      inverseJoinColumns = {@JoinColumn(name = "duplicate_id")})
+  private final List<Inspection> possibleDuplicates = new ArrayList<>();
+
   public InspectionType getType() {
     return type;
   }
@@ -447,6 +457,10 @@ public class Inspection
     this.followupDate = followupDate;
   }
 
+  public @NotNull List<Inspection> getPossibleDuplicates() {
+    return possibleDuplicates;
+  }
+
   private Optional<InspectionTask> getTask(TaskType taskType) {
     return getTasks().stream()
         .filter(t -> taskType.equals(t.getTaskType()))
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
index 1df6854be..a5f032cf1 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/inspection/persistence/InspectionRepository.java
@@ -8,9 +8,11 @@ package de.eshg.inspection.inspection.persistence;
 import de.eshg.inspection.facility.persistence.Facility;
 import de.eshg.inspection.inspection.api.InspectionType;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import java.time.Instant;
 import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
+import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 
 public interface InspectionRepository extends ProcedureRepository<Inspection> {
@@ -47,6 +49,16 @@ public interface InspectionRepository extends ProcedureRepository<Inspection> {
               """)
   Inspection findNewestClosedInspectionForFacility(Facility facility);
 
+  @Query(
+      """
+            select i
+            from Inspection i
+            join InspectionRelatedFacility irf on irf.procedure = i
+            join Facility f on f = irf.facility
+            where f = :facility
+            """)
+  List<Inspection> findAllInspectionsForFacility(Facility facility);
+
   @Query("select i from Inspection i where i.report.id = :reportId")
   Optional<Inspection> findByReportId(UUID reportId);
 
@@ -62,4 +74,40 @@ public interface InspectionRepository extends ProcedureRepository<Inspection> {
           """)
   List<InspectionAppointment> findInspectionAppointmentsToUpdate(
       UUID objectTypeId, InspectionType inspectionType);
+
+  @Query(
+      """
+              select i
+              from Inspection i
+              join InspectionRelatedFacility irf on irf.procedure = i
+              where i.id != :excludedInspectionId
+              and irf.centralFileStateId in :centralFileStateIds
+              and i.executionAppointment.appointmentStart >= :startTime
+              and i.executionAppointment.appointmentStart < :endTime
+              """)
+  List<Inspection> findByCentralFileStateIdsAndAppointment(
+      List<UUID> centralFileStateIds,
+      Instant startTime,
+      Instant endTime,
+      Long excludedInspectionId);
+
+  @Query(
+      """
+            select i
+            from Inspection i
+            join InspectionRelatedFacility irf on irf.procedure = i
+            where irf.centralFileStateId in :centralFileStateIds
+            """)
+  List<Inspection> findByCentralFileStateIds(List<UUID> centralFileStateIds);
+
+  @Query(
+      value = "select distinct inspection_id from inspection_possible_duplicates",
+      nativeQuery = true)
+  List<Long> getInspectionIdsWithDuplicates();
+
+  @Modifying
+  @Query(
+      value = "delete from inspection_possible_duplicates where duplicate_id = ?1",
+      nativeQuery = true)
+  void deleteInspectionFromDuplicatesLists(Long duplicateId);
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java b/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
index d3f280a62..509f496b2 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/packlist/PacklistController.java
@@ -5,8 +5,6 @@
 
 package de.eshg.inspection.packlist;
 
-import de.eshg.inspection.feature.InspectionFeature;
-import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.inspection.InspectionService;
 import de.eshg.inspection.packlist.api.GetPacklistsResponse;
 import de.eshg.inspection.packlist.api.PacklistDto;
@@ -34,12 +32,8 @@ public class PacklistController {
 
   private final InspectionService inspectionService;
 
-  private final InspectionFeatureToggle inspectionFeatureToggle;
-
-  public PacklistController(
-      InspectionService inspectionService, InspectionFeatureToggle inspectionFeatureToggle) {
+  public PacklistController(InspectionService inspectionService) {
     this.inspectionService = inspectionService;
-    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @GetMapping(path = "/{inspectionExternalId}")
@@ -48,7 +42,6 @@ public class PacklistController {
   @NotNull
   public GetPacklistsResponse getPacklists(
       @PathVariable("inspectionExternalId") UUID inspectionExternalId) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return inspectionService.getPacklists(inspectionExternalId);
   }
 
@@ -61,7 +54,6 @@ public class PacklistController {
       @PathVariable("packlistId") UUID packlistId,
       @PathVariable("packlistElementId") UUID packlistElementId,
       @Valid @RequestBody UpdatePacklistElementRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return inspectionService.checkPacklistElement(
         inspectionExternalId, packlistId, packlistElementId, request);
   }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java b/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
index 17cecab1e..968ff127d 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/packlistdefinition/PacklistDefinitionController.java
@@ -5,8 +5,6 @@
 
 package de.eshg.inspection.packlistdefinition;
 
-import de.eshg.inspection.feature.InspectionFeature;
-import de.eshg.inspection.feature.InspectionFeatureToggle;
 import de.eshg.inspection.packlistdefinition.api.AddPacklistDefinitionRevisionRequest;
 import de.eshg.inspection.packlistdefinition.api.CreateNewPacklistDefinitionRequest;
 import de.eshg.inspection.packlistdefinition.api.PacklistDefinitionDto;
@@ -38,13 +36,8 @@ public class PacklistDefinitionController {
 
   private final PacklistDefinitionService packlistDefinitionService;
 
-  private final InspectionFeatureToggle inspectionFeatureToggle;
-
-  public PacklistDefinitionController(
-      PacklistDefinitionService packlistDefinitionService,
-      InspectionFeatureToggle inspectionFeatureToggle) {
+  public PacklistDefinitionController(PacklistDefinitionService packlistDefinitionService) {
     this.packlistDefinitionService = packlistDefinitionService;
-    this.inspectionFeatureToggle = inspectionFeatureToggle;
   }
 
   @GetMapping
@@ -52,7 +45,6 @@ public class PacklistDefinitionController {
   @Transactional(readOnly = true)
   @NotNull
   public PacklistDefinitionsResponse getPacklistDefinitions() {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return this.packlistDefinitionService.getPacklistDefinitions();
   }
 
@@ -61,7 +53,6 @@ public class PacklistDefinitionController {
   @Transactional(readOnly = true)
   @NotNull
   public PacklistDefinitionDto getPacklistDefinitionRevisions(@PathVariable("id") UUID id) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return this.packlistDefinitionService.getPacklistDefinitionRevisions(id);
   }
 
@@ -71,7 +62,6 @@ public class PacklistDefinitionController {
   @NotNull
   public PacklistDefinitionRevisionDto getPacklistDefinitionRevision(
       @PathVariable("revisionId") UUID revisionId) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.getPacklistDefinitionRevision(revisionId);
   }
 
@@ -85,7 +75,6 @@ public class PacklistDefinitionController {
   @NotNull
   public PacklistDefinitionDto createNewPacklistDefinition(
       @Valid @RequestBody CreateNewPacklistDefinitionRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.createNewPacklistDefinition(request);
   }
 
@@ -96,7 +85,6 @@ public class PacklistDefinitionController {
   public PacklistDefinitionRevisionDto addPacklistDefinitionRevision(
       @PathVariable("id") UUID id,
       @Valid @RequestBody AddPacklistDefinitionRevisionRequest request) {
-    inspectionFeatureToggle.assertNewFeatureIsEnabled(InspectionFeature.PACKLISTS);
     return packlistDefinitionService.addPacklistDefinitionRevision(id, request);
   }
 }
diff --git a/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java b/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
index 225fbeded..8742a60ee 100644
--- a/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
+++ b/backend/inspection/src/main/java/de/eshg/inspection/report/InspectionReportService.java
@@ -86,7 +86,7 @@ public class InspectionReportService {
     String facilityName = baseResponse.name();
     ChecklistReportMapper.addTopLevelTitle(report, facilityName);
     addInitialParticipants(report, inspection);
-    addDateOfInspection(report, inspection);
+    addDateOfInspection(report, inspection, clock);
 
     for (Checklist checklist : inspection.getChecklists()) {
       ChecklistReportMapper.addChecklist(report, checklist);
@@ -102,7 +102,7 @@ public class InspectionReportService {
     return inspectionReportRepository.saveAndFlush(report);
   }
 
-  private void addDateOfInspection(Report report, Inspection inspection) {
+  public static void addDateOfInspection(Report report, Inspection inspection, Clock clock) {
     Instant appointmentEnd = inspection.getExecutionAppointment().getAppointmentEnd();
     ChecklistReportMapper.addTextBlock(
         report,
@@ -262,8 +262,12 @@ public class InspectionReportService {
     adjustPosition(reportElements, deletedPosition);
   }
 
-  private static void adjustPosition(List<ReportElement> reportElements, int deletedPosition) {
-    for (int i = deletedPosition; i < reportElements.size(); i++) {
+  public static void adjustPositions(Report report) {
+    adjustPosition(report.getReportElements(), 0);
+  }
+
+  private static void adjustPosition(List<ReportElement> reportElements, int fromPosition) {
+    for (int i = fromPosition; i < reportElements.size(); i++) {
       reportElements.get(i).setPosition(i);
     }
   }
diff --git a/backend/inspection/src/main/resources/application-preview-features.properties b/backend/inspection/src/main/resources/application-preview-features.properties
index edf0000c1..16a70ef9f 100644
--- a/backend/inspection/src/main/resources/application-preview-features.properties
+++ b/backend/inspection/src/main/resources/application-preview-features.properties
@@ -1 +1 @@
-de.eshg.inspection.feature-toggle.enabled-new-features=PACKLISTS,OFFLINE,CHECKLIST_AUDIOS
+de.eshg.inspection.feature-toggle.enabled-new-features=OFFLINE
diff --git a/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml b/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml
new file mode 100644
index 000000000..eb3ee2ff1
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0045_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml b/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml
new file mode 100644
index 000000000..01650cd8b
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0046_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml b/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml
new file mode 100644
index 000000000..4496a2858
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0047_inspection_import_merge.xml
@@ -0,0 +1,39 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729867011677-1">
+    <createTable tableName="inspection_possible_duplicates">
+      <column name="inspection_id" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="duplicate_id" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-2">
+    <validCheckSum>9:e6a29080c43fc930a3369a88e6849a4b</validCheckSum>
+    <addColumn tableName="facility">
+      <column name="possible_duplicates" type="bool" valueBoolean="false">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-3">
+    <addForeignKeyConstraint baseColumnNames="duplicate_id" baseTableName="inspection_possible_duplicates" constraintName="fk_inspection_possible_duplicates_duplicate" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="inspection" validate="true"/>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-4">
+    <addForeignKeyConstraint baseColumnNames="inspection_id" baseTableName="inspection_possible_duplicates" constraintName="fk_inspection_possible_duplicates_inspection" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="inspection" validate="true"/>
+  </changeSet>
+
+  <changeSet author="GA-Lotse" id="1729867011677-5">
+    <ext:addPostgresEnumValues enumTypeName="inspectiontype" valuesToAdd="IMPORT"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml b/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 000000000..232646271
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0048_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml b/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml
new file mode 100644
index 000000000..4cf74a39c
--- /dev/null
+++ b/backend/inspection/src/main/resources/migrations/0049_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/migrations/changelog.xml b/backend/inspection/src/main/resources/migrations/changelog.xml
index 7c09e6e53..d328be2bc 100644
--- a/backend/inspection/src/main/resources/migrations/changelog.xml
+++ b/backend/inspection/src/main/resources/migrations/changelog.xml
@@ -52,5 +52,10 @@
   <include file="migrations/0042_remove_key_document_type_enum.xml"/>
   <include file="migrations/0043_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
   <include file="migrations/0044_shedlock.xml"/>
+  <include file="migrations/0045_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0046_cemetery_sequence.xml"/>
+  <include file="migrations/0047_inspection_import_merge.xml"/>
+  <include file="migrations/0048_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0049_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx b/backend/inspection/src/main/resources/templates/import/InspectionImportTemplate.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..562bdf7a60fc8de096da21bb3cd04b936cd791ff
GIT binary patch
literal 9547
zcma)i1ymf{(k{VeaCZsr?!gigG`PD49~c~hyK8VKKyVH2?(PJ4m*9}_Ai2l*&%NvI
zwR*bObjj|oc317LDp^SgNO-WP5%7LZ=;{2&fP1_!1RBVKfY!E5vX5jKkAJ}aB8w@)
z$a8oB29^L028Qx?GCgZ+Mkh;))M)AV9n2_xS7P5tD`q)|Ws-eq#r=Fy5XkGP{j@gc
zW2ULUzFhEaSDP&pT7xdCMHC}|po$`aQa&)gPh0v(#FuKpm80M@t{DutDG4AzQDkLQ
z#;s3C1)y!1yoP+4gAtj@H);}ne4L{|K_~0)<6z-CimQ&V5XLfXhlro0MTby`A#WL2
zUaYRChLO@<{+SCO1sWy|TW=N*1u&a&3;#xIt(Nlv(I}epql-X8A-#1Pk&;p$4a;TR
z4XzF?H&D*{AY5|<5t*@0_e`0oB-#WOF*VIdudc)kPYPoB>h+d0sSzm#d1m&gOf)y1
zzl~Hh3DPWS2+rqPLHbuv<Mg%?wd2m}$u^c9pF!*`HT!_bN^(5BLBP}-8?w+YOAD=u
zX``I5{$-uCC2e8>qI3m|i=!fjyJ`2KfVyQoi*j=da9$+31GM2<8sT(og{(p1R(IlB
zqaAC$lsY#fGUcR@qq)O+;rn(=Y+aZK9KV(p#h-Bhw9%x5`_=158)-h;2=(taG5~^%
zo{Z!dJton?jNyL_wVb)pEInQ@IBJ|xK?mcF+0~GcV%bK?AYN?+1TNUyXRR1rKYTyk
zB)|CxEHHr?-k-+`q=XdJH7c&s!rsZz>}12ZAt^#rvdk2MIWZY#Vgk)EtMNI3>>(IU
zPUPNIr1j+rC8OZ^m&fBXw^M0#III>py!AcWnXW}WQz&|o=le|;LYNvA^2p1E4RtI~
z5hTK?p@IlT!J??!3LRl8a_Y$C$@X)^i<HM4j}S@PF;DI)rFerSW!|+kPk*6WJQ)!$
zRj>;<(6hZ-P+t9`gq&pQI1f#LzCo#k{YwLcO0GY0(*@7Z)C+qUzC}TZ5&v$x!A}H(
ziFPF5U%uGtz5A|kmb54si_obMt=+Z3nQJOuiklf4c}0+}zRsWSX&ieOAts(OEa5Ux
z5u3A%dkVSYb2{?S+5+}Z#Ho^#(vThzKYBdS|ADx@tsT(v3G<vNVHw}Y7vq@nXF%@N
zIF@$<itfv@WE|wAS`d}x97QIBDS|}k!=lwRhs#M;P2%8&>+z6bvI>u<8qR|Gr`LEW
zu)g|9S9_5i+dq(sNy|!<8VRe=cr<<elGko&UD;hQx-Z(nBV;)P-feed7z~7qRCAsf
zY*M9iR@gUlz!Wu<e8`%%h!)`?Km+;A8K%aW+_$%}F{Li*L}^yW@GTs~k1a4%$XtW-
z?z*IP)bN(<@lvwD*NFR3XHrOH8T$1mMV0{ywLlGS0lNGQ0VrRTZ+9G(*3~K{15Uem
z6%4Ok-Nvr-)a~RNJX}sluRAq)E+BxuH>B^y-=OArJ*W!p(uTU*`pD#Q3l?1eg74pb
z55yyUP8Pqs&c72N{U&e(g3N7AjEwA_dnmrFxwgtjDW(WuU^xGW_7@AwlR-WyYs4gT
z;<z14JwRk%fwglr8t}!GG)T`=&UKpJ6+z8S)05C{7*)132ZS)!WX|bz_m1C$HxI1F
za01iUdtKIO(OKuv%o;k^#7SO9rBrW}0v&-L^^KA!NJ9+UYc}{sdKVT)1T8`NM3rpY
z^Wd+eE&J5=&%13ZRt62?=2$o3l*8q_q}EG$dDIHH5hSy@LHsjXye#N&`8ZaS5!Suy
za*!XHd?W|awW6qrSP&m7E5+D4E(w03=zLKPR}B+nz`mN2=y<mc@t}`No!FOM4S~z|
z$-IMlX1E^x0|6lLZ8Rp^g_HQQC%33IG8x*fD{hHR;69y9+O|WQu|wpfb`{zi6{MM(
zc&P!W5%!l2(rR9=n+KekLRcZ=8o|Uh8-z_aZ{fBiJD0t;)yyqSFRfQlk=c<+G4-3o
zaEZaOgt6?PSJ@u~F-0Tx=0vEG<pr?ph;?_ch+^5hn(0T^f|k!<PaIbUe-^)@`DTqL
zIB&2%p+!^dYUFp{%NW)CX-tr8^25RYykt6O#5=%7;o!2Z4X?Q_Ru__2s8E0o9hTqE
zAk50`vs4wuv_qy3#R00qi4v6x((;K0+2aJq^C=|<U5yt~Nh4;qjbU}b(r*_RiR|e!
zTTU}u1LF5t-RqXs8gts_T!sc@s*&++YnFrYm-?F=$n6QtI#XNwh0a_vum<fZB0!~l
zX68Gl1BVjS84O_sa{!=iFxS%G6kNe_o|X$1Z%&RFL4hd2WkR`4&WqA($&A-;Qn3*y
z0cGn_iSfv#-uD`MkGz_FrNzHB(6d^xtW0Bh0Dz#t!_1oTNYtnjn6~z@z|PZ)RCWHl
z16JNEPcS;~LbO*8m?D><XiagETMsfTicNVIHlhYgma&5j1nL=WHuH)lX0}sy{lPJD
zOS&?4#o(HkgCNvb3>^m(!;9Q9JMqF{*Ey@^C{-b~gk+73`hsr;>jQNH0-dpKxZcp$
z2QK>u8eyNh2ddhm68popgj~A?syd>208p#qCk5Y;G!?lMc%6^mnnI2b>~EurF_mAv
zjMO%62^Dh5|G^8syLrhBzDt2U6QJIxQm$3GFSNQvUP_)`f&S1tDNde<dHOO`6F*f*
z>4P25G%Lf0ADBs#K9jXQnh)tVnH$#tO;_PD=luPlxU}uzHk(-a2&UC<OqZ{$-NcJy
zb2JG%5g1uaz~tdH=>`@y;y%lpcKK3H<9=sCLl_gl)L^we9a<kRg0d1E0+`S_9`&b&
z+)3JsFa3ada=QQS6rOf0k6y#h#K_X<*^WhuFm7}K3Ji<{_qT8Ulk6AqFIRA=CJn^r
zLTSR@@lZWziZa}P$w~%OEwD!Efc8crm>^%vEFRUdj)aD<9<#4p$SccqOwwz~g?b)Z
z%u{<@XPr|t8T<x;5TrsUpJmq5OUj_HLw=OJx;}V<SXU3a&=YAg7=+R@?J3nNxFvcI
zffdcZckTGjCxY=VEs`1$)rjWk+Lr#luz}d!8tSkYhiVq$PVpckoLDmPP?46*Q;(dn
z9FTWn(eC6;{&7=|Vq*vLp0cV!shE=vjx7H;hx$cL4JA67u7~4lwM?5MtsfpNH8E|T
z6CwV4b9mCNEXg{@P?MejToDJ>cI2F6IdXFTool5Z+bRyFilguK-fTi^bXa9Pz&Es$
zq*(3pII}ZS70=*bY-#W6Kzz~cxzPAw+@f|n0fqrtHl7l)TUoxmbj$%?^rByo_v4j%
z(_xYy*FCV*X36}E2STZE-NP-OJM)eR216tC=5;q-vo_GuejiKQ2lUYvak=2=VvpgG
ziiKj=4pGaJy`~{#DlQT(8|J-JgB)>HHT<?DhT>~AEwf-a$<17K$x(=I7%*9(U~=(S
zJR>KNP_ivrZ~79@k0aldD^6q(hK6q6$dXL^Pll$!a`zebw4{;FqDq#Q>fB%7cCj5_
zgyiK#q@@eoZl0g6PrMV{Zl>>mh<%&8T6o-YeYYUf)RN|Qwb!R&)yRC>@^C%}Y;oU(
zZWmBzb-B5i5KI$vyBXT{_<7R#VtL)fqWv9tTYE#0Od}t?*Vo<F*}>C<mc%aVx1AM2
zEY}XTM+39qODQk++7Au*oUXXV0uh`nFI(4mLvN(edIq`HI>Cup-d^E4La+EI4FsN;
z#JxT0yBX|U6yBNTaKz40HoL0?RK?V4Iv7}tHA>6T%K0kLzq$yOW^pJw5+GI9j1X&0
z)J5N2q*@Xn8;%U^ou&PpGxkE9gvl0LSnMlia<=P|Kz51J)k%4nFQV5MpL<vZy3m<X
zsjSXTkT04U^gT-7tSE?k+bd`v0WL9|*+^TkQ>50y%0{&`>Kvbh_^66_%vt|s9l1jM
z$m`C8po4N#uD9Y_?MUKy$+Hn%7!nxHHHmg@5eZGNCmnksB)D?#h}x9vM;<pa87Y)G
zMqXd2*p7NPIR=Yv35Rzf^@>?Z4&t<_$X_Wme#c!JaOhcH@2qo1Qtos?KN6%Kd&MpZ
zG?>NQXV_~JX;CqG@F*QkzC2r)+04Q4ktpoYbzhs_J~muGbrqo|?C0qU5J_)^V$ZM!
z^XY%D!)xPAIG}(1#er2yca<!>Dy%2*-nZ8YCmZnom_vsr=*=oGnsztjH@u6oiJJBa
zHBe{!{j^$PKE5#{5qRIG(^t9hjxgb^9b<Fn7}`bi>Zs;ZMp)_vE>b-y<Cs~8ND~HV
zrO5Lic-~tinJSmcs#S#ajC5BaipAw%W=6^@z8h_I1?yaUy5MiEEk!T_Xl0xWu;-@w
zJ|-tFVMrB%7o5mvfAKWRT?pRq-prrc%y9^p(jX57Rf_NQ?hsMxW2|NIPVG3GG@6zy
z48*FRD^drm;;^g7KZJFO+9Em>&I>6h(3tF{+TCwCKuAyusr96{9uf*r;!cLkQfa(-
z>2!lBmIk~Ip_c6SJ0L~fele33Qz$G#zC}bK-lH<WG$Eu=Oq!%&i2%8pN-+bi<~-9Y
zJbp;N-UkUYF4e%2MV9IT<u~%R{YEiahoFdl4lw>Iv;!R|_2C}L8_m4RjMkGB^&~hi
z?j0NXiE;%-h0%{<_FNH%p&9Rb|1+C6l{lD$T^Qd9N^%-MXSaM}VQ$7W_xHpjrNLX}
zmPech6ii7EltkIi&f1S~%15!iv7KXMP?FP{DAu0_gm9Lj4TNww`%IB_jxr7*rAuBF
zY*b^b*MQzrH$Xx*hNfrlr)87kOvc5iO=7Iu<0>a<h0Zq_l(({dNzD_-DOlF5)e6#>
zdzmuPw&KStI9sgD0FcJomLN3!(cyQ40D&(EcDmEn@D2A988$59{O^$Q*Vn7W>4y@Y
z$9>_R92gkl?~q|@=WJnQ`?MjP(j2l{??r8f>E%NqAP*p^ppL#>E!qsC&}b0m^nvQ`
zgT86=jHXV+37(9e>|7#==YJQ@vxT~~#nl({xggm>SwUKY&0L}iauy?b>f%btzVd-_
znco8ZFdLi79V7f=$uHAfyEZV+y_RU-qc++8ZGGHlt6DoB>htc~?D!pEiTlsW0dhA$
zqvKUtyqT<MLg>XE`(pEvLrhmwjZI95$-bX#Metf!a{GwWqI7lnHX}OuJfzkHGxTEE
ze!%UNQSHJYr)d^LmR3Tzy~ek3r#@i3^~5-b_5!=QH}x@J%eeNB+bO#BcIG9==G(%1
zbV-k!4n~QVy%P`MQtRtS0_Wwi2A<dQu0U>Tm66?_SEUbUA!s%A<5##cA_K&N@39(G
zuqf80rwr#~?f5fR%SzP03KVrWk%{B>S0(eD?SU};`~;;D2!b6;7-NXPMp%K#?q}j-
z$)R+qH>O2g%nj-iZxK4KpZ4Tht&A9?zj8h9hHDLrSx;pwjebb&2<xWB#>B61nkheq
zzH3*zOmoVD&T@p00v9rNZ1<sMo2IX4U9h-k4!*(}$|t;);xyg}P4sg_V%xXDM5<hn
zb9htpE$@dH$18c^%Y=;yyzEZ|a(*#PEO=vCq}fwD_C>|v7ZJfqTlhpaJ*%r?d_+PW
zuw)<48`BEMMiRjr+A8<B@=QKtcCx9y=8l?0RfUHcUYSVoepz%&56>BmYo5W{K(vH2
zf*_SHVzlhof$N?=<;ZcX#%2ypNPx0yy(h>zxqWfVp*x26BV{+^Veltk2C@1iZ{h<<
z#6_C4;B+VDDCu_}A-g(;X;B|Om3R#<=_c-!He`l|X4h{b?<jm#z&d5IX?Wa*CiZ9D
z4ibxCKm@{QP(RW5g7;|-%V+E3Zm*SfH35V@xEIjbW4-0YMEizAl>tAjcV9W3Br&hJ
zF=cg@gR7i4%5Fl>MH&jEat_iB6jD_XG|FwJ?fYyKm~Q~2tTWn#Y~W+w2TUyZODEMG
z*GDY-I~yJOIG%l<RXPE3l0iz06mA7I?qV$QOS-?jnerwfyu8bHA7MGvo(N*HW$Q!x
zu;zirj?OW%W*p5vtZ$6-u|H;7?VOl1C0UeBAeEOYm|$&uB8h!wDy6MPufbT&yLG$D
zEy(TX2RPz!i4@TXchu&*DZ!rk1BbPgFRoG+Zdh7*J_~B=Kc)dQouzq0q<8loWF>Qw
zl#SEF*-mii>gt^F8?E>hE%x@fPK6kFce6Un>ksVn`V+)Crd3`cImad0qlhaN$qE|}
z=G1!bfZX=(+is2921M(aw}aOU)Y~=*qS`7hBe`8p(rS=WQuq4Rhgs~2c<<zyKP7g*
zL-Vu*O%RBm*6}iCFY=%GHnB2<*kFE&gE76l;1|mpJtr9|ulKHmosIP$T=QJtD5D==
z*P(=l?Siv)p4=NYX4V{mW+cU9euLDZqU;zUqvRVHo*A~zcR9-G@32f@T|_*<r}7q)
zw@bGWUJ-`+lGNT)XtfgpV<gkSZRHCj2}aMZ7;N}tl<eegLXL*m5&{$KlAgei2?;}M
ze1+?dV5#X2TBwxsE~OVvaM@9F6f!gSFs-G*uz9y^WaxOV5;=7XmM7N^HHogLp~YId
z{IiR2=4!#qGhQu7TtxWElRT@AHnA=tvBkN}LKX?lTM;W{A(J{QlULx>`~o(2Wd&Gh
z@q8O1-mQli<DEJ1b+IHiq!f)QlL-}S6Q~cDLg6CRLXu&!c^5$Bc!MvQ8dMtqF?iTC
zWkY!J%3=qK;efe-INo&FISAQm^vuacW{FuBQoYWK91%3fWTv)765w4E;!S0$h&-?b
zlGL@>o(o!X>1|T6WuJIjmHGm`JZa{{^lh)#?hkB|4M}S*`mdJx6w=H(L`%x%3!>4(
zq=ECe+~oBd3vlhmlvmXo7CSjJji36M_=lRgF9Qj4wxEw@0%wb4J1v@~n@3k%9gZ{!
zFsR!hU4%ZcEh0E~ZQ%=Cdt!+P$w#&#8+Z3@BZ*9f`jDo8^27%-4vSCc#9vnQ6M2?b
z2f}7;C|jsF5+`|oOsVP*m2M3tFS^{K^K?35hr6v4o_yWPdM?O^<uUHb?DE|x3_F@r
zcy9;-XI~%N|1t!1*mz;O02-V`lPZ%Pdco88W9d3B9;DENWUFem#&-ZTnJ{bvp2ql4
zzFGHoP32*QGkPUym`qyn>bz45LWhG!?8RYCM|&bBjn7^<-Ln0ZG(nM(x9a2uz(6?W
zn2I1qk@)vJXS-UgmFDhaoh6rLyxbj<^@b}jeQP`%GAC@)vl~v7%)P)d)zv~$SqfQi
zDdho$DG?(?E{(8@8tyQH9T5x5=I;=y2z0U3=zvfi6T-vWjtFKs03s?j&IYNSTQzr<
zlu02hstr_!z;bY$OyvhM?c?aLd{-cC95E=k#$n-L>hD=1B0pC}^yzd^=08H{1<jma
z>fzUBCciz+cG!OPnTO>P6JbqYSzC>aGsxeYZZ;5Q-XdKiwU&ee8{2gs>Robi8x1;$
z3s#$(R!D4?$cq)u-e<+HgVpyW!@)ap2pLTxXS*&IDl8PssYTH#G8m3%M3=IoOJ6c^
z0A8L6BbL>SKIc1)nWsWG`UYinc`VWf9@aO*Dpd<-N44RaCC0FjQG@RUeiV8ltDZ{m
za7Le1C2AB3ANh2zs}CzuCTUmd25DLKb&Z8d8ES%8x+$>{5cL$&z5Q>stPKn}edU7^
zN*zrZ36U1j(i1rZ&}l;L6X1!;O13@i$o8agNvp>fJkHmUS10lgX)me-UoV~b7O^U4
zutMp)&3xtNZ7;ZRy*ts509BWwBZL6OW1k6@Pt(zDtYJPd+~QZMEOulxq3IXZnhkFy
z^8n4VYVAL(lamzaQXQ91vWf51f8dU7$$6v`+Oj!cZgb@=w%b$M)VT;dqX7b~YVZ$O
zki$U<%$=*Y0pePzV_j=%ksG_*+GO6->ncK_vQNxv!WXwvL#FjRZI-_z1M^Ob_GH1d
zIJ%!im*yd-m3WbD3LkvDh%+4sHK0|C)!tXu8!kfg)o%HEb@`PbDm#eC>#cB`Bx}78
z>4vqql+nCFS#RPCSsFjI6Lh9Iub4*FJ7`Xr9xq~dlnqv)SpWN<yMk9N<v|SF=thC>
z8*!{r<qOc*V|hS20=NVcu41_uIORs6LOxk1;Fd6Nj(oJ?I81eR1tru@V#mNw3sw@7
zV<0)to2lPnafmt+)R*QX?3d2zOOAO!wJ<e(YCst0ljEKa*<Y^YI9e$<$UnZ1kn1nN
z+3&ijscjahL_FGn)}Fhxs4mBMZ`TJp^hNw!g?Tvnakz=bzv`WCQ?BD}XA%ku(pwp$
z+(V;R_(X!ufw-hHvEpVAM`V{(f`}}XPuqw5j%J^&;4=-9BF%)Z*8P`HpuWl0(tc>M
z%-puc1v6<ql^PCRJ=rav>Q(&t0g4IJPm9nH+`AcB)wFW}o6b=I;*HPa7){yJRr3qf
zwx6~O&hsy2UWrUrWKJsGDw?WWn$yeO!F*kH#Tme|PoWhM%Pc{>dR>t7_NSRZAPq;D
zk@RZz8O@o>9n6j5C+_!3aQbpxAHayeP7Z^$(BgG`2}VP3NXSQxU^j!Kzy2c0j8`Z*
z`LT@itAJ{|4rxEP)9$UE<e^;rVU@Q(od3&aX<Z=Z{ftAo;PitHA1*If4O=A}D`j@L
zrSp&QiS%!Fx0iMxTQ*z3M8Z#W7Xc^Owf%s!CP$RDZk<j!wBqp)C=0d_Wjc@5Lt?)j
zyVQH;H(xgF(OMO3rnx_a9X6~jsZt3#*@l$ud(Yl8DP$!FzQ!y4iTux8_>x9U7TIGS
zJngai@U-)O%(vK@=z)w373@H!Rv&(Cwe6#YpQ=Qnk5!_!vou~iFBA!RQtB*lIY=)k
zzM90`+)HjJ%D^=L4r`WBSrpKG&ho_7U`>jQKnmtDO0N_B#wm3Sxb?2Nk2Gm|L1DH6
zO(4hgyo;}hmDk2+e*WGd)y~JDGBbFXC|{-NGr@kwpvz82oU-Kv2+QL1EHOoF9WJnm
zOQp_3lqf$Z1d;aGSOjbp`-BL}ZW;Ec49ppo)DBLK?8XmbmzL=*Rp-}EAjvy;+(l<O
zuVB0ajq0TPoP0fk<=ZCt>}C3GIkyZPfi8lk`Lx%iV~qe}(g(Mx7|^lOtu5eWenpw?
z)=D^ZcTl3TyEu&3^iAQzi?D8JS(A9qRDWnJ6Y5Qyjxa|?JiMxex@nkjkI)7W9?S=o
z8^nKNO6(eB!2XEo-Q$7$S58|W2sD5CatVv-dxF#dSgg@!lh3~1H^)pK?nM^1BI(ZJ
zSE?W-V1<_aQd&@U#W(J`%Ijng_tI&q>^>OZFDb=4cJi{|%G~)D8e>;Ad4(Dh`fJLd
zLwdz?bTX}(c<jO@zmBCk2w~4K%tQGz$?mM3))Q_UB6?*!Y=8`of1D_>=fLa>^6a@H
z>XO9O{_&_Xz=K|Gt{gZ~3O8PvsW0Rxf9Cxx4Y6sHS*-tJE$Zr0i?vU^OcL6VflDl{
zSJ4i^>zNLjtk2{GKYqxW7A*~h;2K?AMK7FCS;KWQqsI6^@j<!Wn-YnP)6r=?016J#
z&ak*lEY|L3giS>UvT#lgX7&#d<j=${u$o>k)LLd&WJhqLjh23^5KdH>Y*ciD!{-@l
zWfQe#sP+Q9v(lvRS4-p@eo*#!@!xZa(2oG?fQ&4j7ZzN(9U31V?=3$r?8k4&|Ijc#
zQ5Hv!NOUrzbRT;kqii)xJ7S{F*)fqFNE^C&CP+PuQldwz9X2-!%h6BP!$c(BY$Y#$
zG;F|MB++Kc4R7?em-7|^_Bu4noqTjsUGx?!YMO#@R<v%1Y}h-~zw`Ck;*XtYB#+Yi
zC_l;?z(*5fI<~7QE0oU1A)3b{Xz<d9v&l-<s{ib5cRG=*G=J9;ZJ=udv<<gk;|&m4
zus^)=Dlz-@2pipoVpOWVMI>gZ&&TZ97L*H7XlR}hkiEdbv7<ylQL!-tqNt`{f<o}O
zP0bb?`otd1_r$>z8ACTh6BFC*ZBb(yRRdzYz7$#e59F*pof~n^0vtMGpfiR-$N!Gy
zfAX^b2IuBU7S7=#jP;Lj;ys=p{lsswr^?PBEBg<0%{rI?-N!ynK4We+<6$gsQ?<J@
zW{O?G4Cp*e^kF15l55g?Gu1Ne;xz&3zAeX80}G3F$CJpEW(Lf%hCI{&##_!w=w_s2
zWTuQj%aiJm7l@%^Wpu~K)x@)Eagvi&5HvP$h8cQ9+FvPWbb&M>5YW4dEEjR2x$4N%
zgqC40G20`&KQ25$tNE8pLi2Z2vH>=c8xKteN?(zdv+j;qPCmg(he_`U^7J{8cXLkl
zLSt=81q$l~IBR11_q^A(%}qEjz8*?CrRQtsaf^204bcE}B?0mh!Y4`225o4fEF^ic
zahk4eHlHQ0znrSuet3N85g!xSPlZK|*FY;fBP%-{MQ3XxTdk*;zc_Z_Jva-7{}p9(
z$7c;T1#7)fJ>6BKeDXDj$;(#u^SCtclgl(3U2tKq&}4Dm!<&|e`zF9!I`d$PH<L?K
zMLHL%FW$T4EEX9P$HbqdVJ7%0v^&9YXh2kqo!pu+tHHE-7Z6StiPP{~hgZCyD?58Z
zFeK$xMK;Df?1TbhG;RgDHt{hwNv_Bzsv~j|@V6<_OVOsOoV%`*dkZV`TVYifB?X~`
z#*sxSG~4ZY%3Hx<Ds6Gh?s#$;`kZF_I&3+%Rv(3B1*r?OV6im8OLEqpfe74$-uoA{
zD230SPvpl5;UP$(Dvnkc34QV+C#6IbOnWetBwhtwGiF!c>aT&v)WowP<Hf0MNnQ6_
zTfBk1NFz%XJCN!@5vH~Pi{_Y0yFB_r5YluJH4Xi9Q3&gm@O!&_&uw6fEtuLC<jFwK
zP2Sb_;j)e@nu~t;!{L=}29bHUg59Ny=BVlx`o`~V-winLp=BjsfMfg$XulgFo-f}=
z<AA*YBYFI_A^Km7`MI3u7XC|49Pz)}`?rAqUIuv*@Vhrd;_nlAuJ^e&dt&-Me=@><
z__pVa&*S@FjN*^4@xMj;=MtVrv%e&y;{THHcMtIY|F{*fznK510sYfl&oh62>D+uQ
zC_l~m&phDs={_$Y{lz->ca49Qlb#EBUXlJwKp*6v&-<TR_Fwhs=Te?`b^Ik|;L$<+
z*{<_PkH`PQ?J2_j9#1ViKMDS@_j8rcn|+>mey<U#$E!b8{-^cle`$QG>i-^38bAJi
z?El5^ROSCYo)}X94^Gdg`n&_<iTC$ZUp?ynbE*b^_GA1n<xf%T_jpqNH2xGID+%=r
RJ=Dj}8komfF_J#r{(nL|kf#6u

literal 0
HcmV?d00001

diff --git a/backend/keycloak/Dockerfile b/backend/keycloak/Dockerfile
index 0ea379fcb..5e946a9f0 100644
--- a/backend/keycloak/Dockerfile
+++ b/backend/keycloak/Dockerfile
@@ -12,7 +12,7 @@ FROM registry.access.redhat.com/ubi9:9.4-1214.1726694543@sha256:b00d5990a00937bd
 FROM quay.io/keycloak/keycloak:$keycloakVersion AS keycloak-builder
 
 ENV KC_HEALTH_ENABLED true
-ENV KC_FEATURES_DISABLED impersonation
+ENV KC_FEATURES_DISABLED impersonation,organization
 ENV KC_DB postgres
 ENV KC_CACHE local
 
@@ -34,7 +34,7 @@ COPY --from=ubi-micro-build /usr/lib64/ /usr/lib64/
 COPY --from=ubi-micro-build /usr/bin/curl /usr/bin/
 
 ENV KC_HEALTH_ENABLED true
-ENV KC_FEATURES_DISABLED impersonation
+ENV KC_FEATURES_DISABLED impersonation,organization
 ENV KC_DB postgres
 ENV KC_CACHE local
 
diff --git a/backend/keycloak/build.gradle b/backend/keycloak/build.gradle
index ec38cc982..3640f9a0a 100644
--- a/backend/keycloak/build.gradle
+++ b/backend/keycloak/build.gradle
@@ -50,7 +50,7 @@ tasks.register('buildDockerImage', DockerBuildImage) {
     dependsOn 'copyFilesToBuildContext'
     inputDir.set(dockerBuildContext)
     dockerFile.set(inputDir.file("Dockerfile"))
-    buildArgs["keycloakVersion"] = libs.versions.keycloak.get()
+    buildArgs["keycloakVersion"] = libs.versions.keycloak.server.get()
 
     if (project.hasProperty("tagName")) {
         images.add(getImageNameWithTag())
diff --git a/backend/keycloak/gradle.lockfile b/backend/keycloak/gradle.lockfile
index 674642f5d..373e7c880 100644
--- a/backend/keycloak/gradle.lockfile
+++ b/backend/keycloak/gradle.lockfile
@@ -3,13 +3,25 @@
 # This file is expected to be part of source control.
 ch.qos.logback:logback-classic:1.5.11=testCompileClasspath,testRuntimeClasspath
 ch.qos.logback:logback-core:1.5.11=testCompileClasspath,testRuntimeClasspath
+com.aayushatharva.brotli4j:brotli4j:1.16.0=compileClasspath
+com.aayushatharva.brotli4j:service:1.16.0=compileClasspath
 com.apicatalog:titanium-json-ld:1.3.3=compileClasspath
 com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=compileClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath
 com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath
+com.github.ben-manes.caffeine:caffeine:3.1.8=compileClasspath
 com.github.ua-parser:uap-java:1.5.4=compileClasspath
+com.google.api.grpc:proto-google-common-protos:2.29.0=compileClasspath
+com.google.code.findbugs:jsr305:3.0.2=compileClasspath
+com.google.errorprone:error_prone_annotations:2.23.0=compileClasspath
+com.google.guava:failureaccess:1.0.1=compileClasspath
+com.google.guava:guava:32.1.3-android=compileClasspath
+com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=compileClasspath
+com.google.j2objc:j2objc-annotations:2.8=compileClasspath
+com.google.protobuf:protobuf-java:3.25.1=compileClasspath
 com.google.zxing:core:3.4.0=compileClasspath
 com.google.zxing:javase:3.4.0=compileClasspath
 com.googlecode.owasp-java-html-sanitizer:java10-shim:20240325.1=compileClasspath
@@ -20,14 +32,101 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 com.webauthn4j:webauthn4j-core:0.21.5.RELEASE=compileClasspath
 com.webauthn4j:webauthn4j-util:0.21.5.RELEASE=compileClasspath
 commons-codec:commons-codec:1.16.1=compileClasspath
+io.github.crac:org-crac:0.1.3=compileClasspath
+io.grpc:grpc-api:1.65.1=compileClasspath
+io.grpc:grpc-core:1.65.1=compileClasspath
+io.grpc:grpc-netty:1.65.0=compileClasspath
+io.grpc:grpc-protobuf:1.65.0=compileClasspath
+io.grpc:grpc-stub:1.65.0=compileClasspath
 io.micrometer:micrometer-commons:1.13.6=testCompileClasspath,testRuntimeClasspath
 io.micrometer:micrometer-observation:1.13.6=testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.1.114.Final=compileClasspath
+io.netty:netty-codec-dns:4.1.114.Final=compileClasspath
+io.netty:netty-codec-haproxy:4.1.114.Final=compileClasspath
+io.netty:netty-codec-http2:4.1.114.Final=compileClasspath
+io.netty:netty-codec-http:4.1.114.Final=compileClasspath
+io.netty:netty-codec-socks:4.1.114.Final=compileClasspath
+io.netty:netty-codec:4.1.114.Final=compileClasspath
+io.netty:netty-common:4.1.114.Final=compileClasspath
+io.netty:netty-handler-proxy:4.1.114.Final=compileClasspath
+io.netty:netty-handler:4.1.114.Final=compileClasspath
+io.netty:netty-resolver-dns:4.1.114.Final=compileClasspath
+io.netty:netty-resolver:4.1.114.Final=compileClasspath
+io.netty:netty-transport-native-unix-common:4.1.114.Final=compileClasspath
+io.netty:netty-transport:4.1.114.Final=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support:2.5.0-alpha=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.5.0=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.5.0-alpha=compileClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.5.0=compileClasspath
+io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.25.0-alpha=compileClasspath
+io.opentelemetry.semconv:opentelemetry-semconv:1.26.0-alpha=compileClasspath
+io.opentelemetry:opentelemetry-api-incubator:1.39.0-alpha=compileClasspath
+io.opentelemetry:opentelemetry-api:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-context:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-otlp-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-exporter-otlp:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-common:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-logs:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-metrics:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk-trace:1.37.0=compileClasspath
+io.opentelemetry:opentelemetry-sdk:1.37.0=compileClasspath
+io.quarkus.arc:arc:3.15.1=compileClasspath
 io.quarkus.resteasy.reactive:resteasy-reactive-common-types:3.16.0=compileClasspath
 io.quarkus.resteasy.reactive:resteasy-reactive-common:3.16.0=compileClasspath
+io.quarkus.security:quarkus-security:2.1.0=compileClasspath
+io.quarkus:quarkus-arc:3.15.1=compileClasspath
+io.quarkus:quarkus-bootstrap-runner:3.15.1=compileClasspath
+io.quarkus:quarkus-classloader-commons:3.15.1=compileClasspath
+io.quarkus:quarkus-core:3.15.1=compileClasspath
+io.quarkus:quarkus-credentials:3.15.1=compileClasspath
+io.quarkus:quarkus-development-mode-spi:3.15.1=compileClasspath
+io.quarkus:quarkus-fs-util:0.0.10=compileClasspath
+io.quarkus:quarkus-grpc-common:3.15.1=compileClasspath
+io.quarkus:quarkus-ide-launcher:3.15.1=compileClasspath
+io.quarkus:quarkus-mutiny:3.15.1=compileClasspath
+io.quarkus:quarkus-netty:3.15.1=compileClasspath
+io.quarkus:quarkus-opentelemetry:3.15.1=compileClasspath
+io.quarkus:quarkus-security-runtime-spi:3.15.1=compileClasspath
+io.quarkus:quarkus-smallrye-context-propagation:3.15.1=compileClasspath
+io.quarkus:quarkus-tls-registry:3.15.1=compileClasspath
+io.quarkus:quarkus-vertx-latebound-mdc-provider:3.15.1=compileClasspath
+io.quarkus:quarkus-vertx:3.15.1=compileClasspath
+io.quarkus:quarkus-virtual-threads:3.15.1=compileClasspath
+io.reactivex.rxjava3:rxjava:3.1.9=compileClasspath
 io.setl:rdf-urdna:1.1=compileClasspath
 io.smallrye.common:smallrye-common-annotation:2.7.0=compileClasspath
+io.smallrye.common:smallrye-common-classloader:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-constraint:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-cpu:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-expression:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-function:2.4.0=compileClasspath
+io.smallrye.common:smallrye-common-io:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-net:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-os:2.6.0=compileClasspath
+io.smallrye.common:smallrye-common-ref:2.2.0=compileClasspath
+io.smallrye.common:smallrye-common-vertx-context:2.6.0=compileClasspath
+io.smallrye.config:smallrye-config-common:3.9.1=compileClasspath
+io.smallrye.config:smallrye-config-core:3.9.1=compileClasspath
+io.smallrye.config:smallrye-config:3.9.1=compileClasspath
+io.smallrye.reactive:mutiny-smallrye-context-propagation:2.6.2=compileClasspath
 io.smallrye.reactive:mutiny-zero-flow-adapters:1.1.0=compileClasspath
 io.smallrye.reactive:mutiny:2.6.2=compileClasspath
+io.smallrye.reactive:smallrye-mutiny-vertx-core:3.15.0=compileClasspath
+io.smallrye.reactive:smallrye-mutiny-vertx-runtime:3.15.0=compileClasspath
+io.smallrye.reactive:vertx-mutiny-generator:3.15.0=compileClasspath
+io.smallrye:smallrye-context-propagation-api:2.1.2=compileClasspath
+io.smallrye:smallrye-context-propagation-storage:2.1.2=compileClasspath
+io.smallrye:smallrye-context-propagation:2.1.2=compileClasspath
+io.smallrye:smallrye-fault-tolerance-vertx:6.4.0=compileClasspath
+io.vertx:vertx-codegen:4.5.10=compileClasspath
+io.vertx:vertx-core:4.5.10=compileClasspath
+io.vertx:vertx-grpc-client:4.5.10=compileClasspath
+io.vertx:vertx-grpc-common:4.5.10=compileClasspath
+io.vertx:vertx-grpc-server:4.5.10=compileClasspath
+io.vertx:vertx-grpc:4.5.10=compileClasspath
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.el:jakarta.el-api:6.0.0=compileClasspath
@@ -56,14 +155,24 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
 org.assertj:assertj-core:3.25.3=testCompileClasspath,testRuntimeClasspath
 org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath
 org.eclipse.angus:angus-mail:2.0.3=compileClasspath
+org.eclipse.microprofile.config:microprofile-config-api:3.1=compileClasspath
+org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api:1.3=compileClasspath
 org.eclipse.microprofile.openapi:microprofile-openapi-api:3.1.1=compileClasspath
 org.eclipse.parsson:parsson:1.1.7=compileClasspath
 org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath
+org.infinispan.protostream:protostream-processor:5.0.10.Final=compileClasspath
+org.infinispan.protostream:protostream-types:5.0.10.Final=compileClasspath
+org.infinispan.protostream:protostream:5.0.10.Final=compileClasspath
+org.infinispan:infinispan-commons:15.0.10.Final=compileClasspath
 org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:jboss-logging-annotations:3.0.1.Final=compileClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath
+org.jboss.logmanager:jboss-logmanager:3.0.6.Final=compileClasspath
+org.jboss.slf4j:slf4j-jboss-logmanager:2.0.0.Final=compileClasspath
+org.jboss.threads:jboss-threads:3.6.1.Final=compileClasspath
 org.jctools:jctools-core:4.0.5=compileClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.5=testRuntimeClasspath
@@ -73,13 +182,14 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage-private:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi-private:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi:25.0.6=compileClasspath
-org.keycloak:keycloak-services:25.0.6=compileClasspath
+org.keycloak:keycloak-common:26.0.2=compileClasspath
+org.keycloak:keycloak-config-api:26.0.2=compileClasspath
+org.keycloak:keycloak-core:26.0.2=compileClasspath
+org.keycloak:keycloak-model-storage-private:26.0.2=compileClasspath
+org.keycloak:keycloak-model-storage:26.0.2=compileClasspath
+org.keycloak:keycloak-server-spi-private:26.0.2=compileClasspath
+org.keycloak:keycloak-server-spi:26.0.2=compileClasspath
+org.keycloak:keycloak-services:26.0.2=compileClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.3=testRuntimeClasspath
@@ -90,7 +200,7 @@ org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath
 org.reactivestreams:reactive-streams:1.0.4=compileClasspath
 org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath
 org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath
-org.slf4j:slf4j-api:2.0.16=testCompileClasspath,testRuntimeClasspath
+org.slf4j:slf4j-api:2.0.16=compileClasspath,testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-autoconfigure:3.3.5=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-logging:3.3.5=testCompileClasspath,testRuntimeClasspath
 org.springframework.boot:spring-boot-starter-test:3.3.5=testCompileClasspath,testRuntimeClasspath
@@ -106,6 +216,7 @@ org.springframework:spring-expression:6.1.14=testCompileClasspath,testRuntimeCla
 org.springframework:spring-jcl:6.1.14=testCompileClasspath,testRuntimeClasspath
 org.springframework:spring-test:6.1.14=testCompileClasspath,testRuntimeClasspath
 org.twitter4j:twitter4j-core:4.1.2=compileClasspath
+org.wildfly.common:wildfly-common:1.7.0.Final=compileClasspath
 org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
 org.yaml:snakeyaml:2.2=testCompileClasspath,testRuntimeClasspath
 empty=annotationProcessor,developmentOnly,productionRuntimeClasspath,runtimeClasspath,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath
diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
index b5782df5d..241c1fe98 100644
--- a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
+++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeForm.java
@@ -154,7 +154,9 @@ public class AccessCodeForm extends AbstractFormAuthenticator {
     }
     RealmModel realm = context.getRealm();
     if (realm.isBruteForceProtected()) {
-      context.getProtector().failedLogin(realm, user, context.getConnection());
+      context
+          .getProtector()
+          .failedLogin(realm, user, context.getConnection(), context.getUriInfo());
     }
   }
 
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java
deleted file mode 100644
index 93d737ddf..000000000
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleAggregationHelper.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.aggregation;
-
-import de.eshg.rest.service.error.ErrorCode;
-import de.eshg.rest.service.error.ErrorResponseWithLocation;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Function;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractBaseModuleAggregationHelper<B extends AbstractBaseModuleClient>
-    extends AggregationHelper {
-
-  private static final Logger log =
-      LoggerFactory.getLogger(AbstractBaseModuleAggregationHelper.class);
-
-  protected abstract List<B> getBaseModuleClients();
-
-  @Override
-  protected ErrorResponseWithLocation createErrorResponse(
-      ErrorCode errorCode, String healthDepartmentName, ExecutionException e) {
-    String message = "Error retrieving data from health department";
-    if (errorCode.equals(ErrorCode.TIMEOUT)) {
-      message = "Timeout from health department";
-    }
-    if (errorCode.equals(ErrorCode.INSUFFICIENT_USER_RIGHTS)) {
-      message = "Insufficient user rights";
-    }
-
-    log.error(message, e);
-    return new ErrorResponseWithLocation(errorCode, message, healthDepartmentName);
-  }
-
-  public <T> List<ClientResponse<T>> requestFromBaseModules(Function<B, T> getFromBaseModule) {
-    return requestFromClients(getBaseModuleClients(), getFromBaseModule);
-  }
-}
diff --git a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java b/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java
deleted file mode 100644
index 064fd67a3..000000000
--- a/backend/lib-aggregation/src/main/java/de/eshg/lib/aggregation/AbstractBaseModuleClient.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.aggregation;
-
-import java.time.Duration;
-
-public class AbstractBaseModuleClient extends ClientWithLocationAndTimeout {
-  private static final Duration TIMEOUT = Duration.ofSeconds(10);
-
-  public AbstractBaseModuleClient(String location, String url) {
-    super(location, url, TIMEOUT);
-  }
-}
diff --git a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
index ee3ba9b14..85ed2cb81 100644
--- a/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
+++ b/backend/lib-base-client/src/main/java/de/eshg/base/client/BaseClientAutoConfiguration.java
@@ -13,6 +13,7 @@ import de.eshg.base.citizenuser.CitizenAccessCodeUserApi;
 import de.eshg.base.contact.ContactApi;
 import de.eshg.base.department.DepartmentApi;
 import de.eshg.base.feature.BaseFeatureTogglesApi;
+import de.eshg.base.gdpr.GdprProcedureApi;
 import de.eshg.base.inventory.InventoryApi;
 import de.eshg.base.mail.MailApi;
 import de.eshg.base.resource.ResourceApi;
@@ -123,6 +124,11 @@ class BaseClientAutoConfiguration {
     return createClient(DepartmentApi.class);
   }
 
+  @Bean
+  GdprProcedureApi gdprApiClient() {
+    return createClient(GdprProcedureApi.class);
+  }
+
   private <T> T createClient(Class<T> serviceClass) {
     RestClient restClient =
         restClientBuilder
diff --git a/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java b/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
index 472f822ff..85a463b3e 100644
--- a/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
+++ b/backend/lib-base-client/src/main/java/de/eshg/base/client/ContactClient.java
@@ -11,6 +11,7 @@ import de.eshg.base.contact.api.ContactDto;
 import de.eshg.base.contact.api.InstitutionContactCategoryDto;
 import de.eshg.base.contact.api.InstitutionContactDto;
 import de.eshg.rest.service.error.BadRequestException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -40,6 +41,16 @@ public class ContactClient {
     return contactApiClient.getBulkContacts(new GetContactsRequest(contactIds)).contactResponses();
   }
 
+  public ArrayList<UUID> getContactAliases(UUID contactId) {
+    if (contactId == null) {
+      return new ArrayList<>();
+    }
+    List<UUID> mergeSources = contactApiClient.getMergedContacts(contactId).contactIds();
+    ArrayList<UUID> list = new ArrayList<>(mergeSources);
+    list.add(contactId);
+    return list;
+  }
+
   public void validateContactIsInstitutionWithCategory(
       UUID locationId, InstitutionContactCategoryDto category) {
     try {
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
index 5fef174b7..1754f90e2 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java
@@ -114,6 +114,10 @@ public enum EmployeePermissionRole implements PermissionRole {
       Module.BASE,
       BASE_CONTACTS_READ),
 
+  BASE_GDPR_PROCEDURE_REVIEW(
+      "Limitierte Lese- und Schreibberechtigung %s für Module".formatted("DSGVO-Prozesse"),
+      "Kann SachstandsIds zu DSGVO-Prozessen abfragen und DownloadIds anlegen",
+      Module.BASE),
   BASE_GDPR_PROCEDURE_READ(
       READ_PERMISSION_TEMPLATE.formatted("DSGVO-Prozesse"),
       "Kann DSGVO-Prozesse (Löschanfrage, Widerspruch und Datenauskunft) abrufen",
@@ -221,6 +225,7 @@ public enum EmployeePermissionRole implements PermissionRole {
       INSPECTION_CENTRALREPOSITORY_WRITE),
   INSPECTION_CENTRALREPOSITORY_WRITE_CORECHECKLISTS(
       "Kern-Checklisten-Definition in Zentralen Diensten bereitstellen", Module.INSPECTION),
+  INSPECTION_IMPORT("Vorgänge importieren", Module.INSPECTION),
 
   SCHOOL_ENTRY_ADMIN(
       ADMIN_KEYCLOAK_NAME.formatted("Einschulungsuntersuchung"),
@@ -231,7 +236,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       BASE_RESOURCES_READ,
       BASE_CALENDAR_BUSINESS_EVENTS_WRITE,
       BASE_CONTACTS_READ,
-      BASE_CONTACTS_WRITE),
+      BASE_CONTACTS_WRITE,
+      BASE_GDPR_PROCEDURE_REVIEW),
 
   STATISTICS_STATISTICS_ADMIN(
       ADMIN_KEYCLOAK_NAME.formatted("Statistik"),
@@ -257,7 +263,8 @@ public enum EmployeePermissionRole implements PermissionRole {
       BASE_FACILITIES_WRITE,
       BASE_ACCESS_CODE_USER_ADMIN,
       BASE_RESOURCES_READ,
-      BASE_CALENDAR_BUSINESS_EVENTS_WRITE),
+      BASE_CALENDAR_BUSINESS_EVENTS_WRITE,
+      BASE_GDPR_PROCEDURE_REVIEW),
 
   STI_PROTECTION_USER(
       "HIV-STI Benutzer",
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
index 13861e36b..c92643f31 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeeTestUser.java
@@ -96,6 +96,14 @@ public enum EmployeeTestUser implements KeycloakUser {
           ModuleLeaderGroup.INSPECTION,
           ModuleMemberGroup.INSPECTION,
           ModuleMemberGroup.INSPECTION_CHECKLISTS)),
+  INSPECTION_GA_IMPORT(
+      "inspection_ga_import",
+      "+49 555 123 459 3",
+      "password",
+      "Max",
+      "Import",
+      List.of(EmployeePermissionRole.INSPECTION_IMPORT),
+      List.of(ModuleMemberGroup.INSPECTION)),
   INSPECTION_LANDESAMT_USER(
       "inspection_la_user",
       "+49 555 123 460",
diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
index 57de29806..1966f96bc 100644
--- a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
+++ b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/ModuleMemberGroup.java
@@ -79,7 +79,8 @@ public enum ModuleMemberGroup implements KeycloakGroup {
     return List.of(
         EmployeePermissionRole.BASE_TASKS_READ,
         EmployeePermissionRole.BASE_PROCEDURES_READ,
-        EmployeePermissionRole.CHAT_MANAGEMENT_WRITE);
+        EmployeePermissionRole.CHAT_MANAGEMENT_WRITE,
+        EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
   }
 
   private static List<EmployeePermissionRole> getStandardInspectionRoles() {
diff --git a/backend/lib-lsd-api/build.gradle b/backend/lib-lsd-api/build.gradle
index 2026ac1d9..defb78e63 100644
--- a/backend/lib-lsd-api/build.gradle
+++ b/backend/lib-lsd-api/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     api project(':api-commons')
     api project(':lib-commons')
     implementation 'org.springframework.boot:spring-boot-autoconfigure'
-    implementation libs.bundles.keycloak.client
+    implementation libs.keycloak.client.admin.client
 
     testImplementation('com.squareup.okhttp3:mockwebserver') {
         exclude group: "junit", module: "junit"
diff --git a/backend/lib-lsd-api/gradle.lockfile b/backend/lib-lsd-api/gradle.lockfile
index 2a9fcba3e..2cc15d351 100644
--- a/backend/lib-lsd-api/gradle.lockfile
+++ b/backend/lib-lsd-api/gradle.lockfile
@@ -7,6 +7,7 @@ com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath,productio
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -16,10 +17,10 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.java-json-tools:btf:1.3=testFixturesRuntimeClasspath
+com.github.java-json-tools:jackson-coreutils:2.0=testFixturesRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+com.github.java-json-tools:msg-simple:1.2=testFixturesRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=testFixturesRuntimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -46,7 +47,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat
 commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.17.0=testFixturesRuntimeClasspath
-commons-logging:commons-logging:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+commons-logging:commons-logging:1.2=testFixturesRuntimeClasspath
 de.cronn:commons-lang:1.2=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testFixturesRuntimeClasspath,testRuntimeClasspath
 de.cronn:test-utils:1.1.1=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
@@ -73,9 +74,9 @@ org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testFixturesComp
 org.apache.commons:commons-lang3:3.14.0=testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=testCompileClasspath,testRuntimeClasspath
 org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testFixturesCompileClasspath
@@ -101,15 +102,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testCompileClasspath,testRuntimeClasspath
@@ -123,9 +125,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testFixtur
 org.junit.platform:junit-platform-engine:1.10.5=testFixturesRuntimeClasspath,testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.objenesis:objenesis:3.3=testRuntimeClasspath
diff --git a/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java b/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
index d8012e043..809554774 100644
--- a/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
+++ b/backend/lib-lsd-api/src/testFixtures/java/de/eshg/lsd/testcontainers/LsdTestContainerUtil.java
@@ -61,13 +61,13 @@ public class LsdTestContainerUtil {
     GenericContainer<?> container =
         new GenericContainer<>(DockerImageName.parse(AUTHORIZATION_SERVER_IMAGE))
             .withNetwork(network)
-            .withEnv("KEYCLOAK_ADMIN", KEYCLOAK_ADMIN_NAME)
-            .withEnv("KEYCLOAK_ADMIN_PASSWORD", KEYCLOAK_ADMIN_PASSWORD)
+            .withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", KEYCLOAK_ADMIN_NAME)
+            .withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", KEYCLOAK_ADMIN_PASSWORD)
             .withEnv("KC_DB", "dev-file")
             .withExposedPorts(port)
             .waitingFor(
                 new LogMessageWaitStrategy()
-                    .withRegEx(".*Added user 'admin' to realm 'master'.*")
+                    .withRegEx(".*\\(main\\) Installed features:.*")
                     .withStartupTimeout(Duration.ofMinutes(3)))
             .withCreateContainerCmdModifier(
                 command -> command.withName(TestContainersUtil.generateName("authorization")))
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java
new file mode 100644
index 000000000..8c29f55e4
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/GdprValidationTaskApi.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.api;
+
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.service.annotation.HttpExchange;
+import org.springframework.web.service.annotation.PostExchange;
+
+@HttpExchange(GdprValidationTaskApi.BASE_URL)
+public interface GdprValidationTaskApi {
+
+  String BASE_URL = "/gdpr-validation-tasks";
+
+  @PostExchange
+  @ApiResponse(responseCode = "200", description = "Add a GDPR validation task")
+  @Operation(summary = "Add a GDPR validation task")
+  void addGdprValidationTask(@RequestBody @Valid AddGdprValidationTaskRequest request);
+}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
index 22ad13241..177b5215c 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/api/ProcedureMetricsApi.java
@@ -18,7 +18,7 @@ import org.springframework.web.service.annotation.GetExchange;
 
 public interface ProcedureMetricsApi {
 
-  int MAXIMUM_DAYS_METRICS = 366;
+  int MAXIMUM_DAYS_METRICS = 367;
 
   class QueryParameter {
 
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
index 4987a627f..a777840b7 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/CreateManualProgressEntryRequest.java
@@ -9,7 +9,5 @@ import jakarta.validation.constraints.NotNull;
 
 public record CreateManualProgressEntryRequest(
     @NotNull ManualProgressEntryTypeDto manualProgressEntryType,
-    String subject,
-    String messageText,
     String note,
     String keyDocumentType) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java
new file mode 100644
index 000000000..240a83537
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresRequest.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record FindProceduresRequest(@Valid @NotNull List<UUID> fileStateIds) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java
new file mode 100644
index 000000000..4de29de45
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/FindProceduresResponse.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import java.util.UUID;
+
+public record FindProceduresResponse(@Valid @NotNull Map<UUID, UUID> procedureIdsByFileStateIds) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
index e0c6d17d2..0f3559fc1 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/GetProgressEntryResponse.java
@@ -11,4 +11,4 @@ import java.util.List;
 
 public record GetProgressEntryResponse(
     @NotNull @Valid ProgressEntryDto progressEntry,
-    @NotNull @Valid List<ManualProgressEntryDto> relatedKeyDocumentProgressEntries) {}
+    @NotNull @Valid List<KeyDocumentAwareProgressEntryDto> relatedKeyDocumentProgressEntries) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java
new file mode 100644
index 000000000..00ddcc826
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/KeyDocumentAwareProgressEntryDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "KeyDocumentAwareProgressEntry")
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
+public sealed interface KeyDocumentAwareProgressEntryDto
+    permits ManualProgressEntryDto, SystemProgressEntryDto {
+
+  String getKeyDocumentType();
+
+  Integer getKeyDocumentVersion();
+}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
index b757c3463..3df9f4339 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/MailMetaDataDto.java
@@ -16,6 +16,8 @@ public final class MailMetaDataDto extends MetaDataDto {
 
   public static final String SCHEMA_NAME = "MailMetaData";
 
+  private @NotNull String subject;
+  private @NotNull String messageText;
   private @NotNull String mailFrom;
   private @NotNull String mailTo;
   private @NotNull Instant sentDate;
@@ -43,4 +45,20 @@ public final class MailMetaDataDto extends MetaDataDto {
   public void setSentDate(Instant sentDate) {
     this.sentDate = sentDate;
   }
+
+  public @NotNull String getSubject() {
+    return subject;
+  }
+
+  public void setSubject(@NotNull String subject) {
+    this.subject = subject;
+  }
+
+  public @NotNull String getMessageText() {
+    return messageText;
+  }
+
+  public void setMessageText(@NotNull String messageText) {
+    this.messageText = messageText;
+  }
 }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
index ca3d34f81..506044667 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/ManualProgressEntryDto.java
@@ -17,12 +17,10 @@ import java.util.UUID;
 @Schema(name = ManualProgressEntryDto.SCHEMA_NAME, allOf = ProgressEntryDto.class)
 @JsonTypeName(ManualProgressEntryDto.SCHEMA_NAME)
 public final class ManualProgressEntryDto extends ProgressEntryDto
-    implements ApprovalRequestEntityDto {
+    implements ApprovalRequestEntityDto, KeyDocumentAwareProgressEntryDto {
   public static final String SCHEMA_NAME = "ManualProgressEntry";
 
   @NotNull private ManualProgressEntryTypeDto manualProgressEntryType;
-  private String subject;
-  private String messageText;
   private String note;
   private String keyDocumentType;
   private Integer keyDocumentVersion;
@@ -40,22 +38,6 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.manualProgressEntryType = manualProgressEntryType;
   }
 
-  public String getSubject() {
-    return subject;
-  }
-
-  public void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  public String getMessageText() {
-    return messageText;
-  }
-
-  public void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
   public String getNote() {
     return note;
   }
@@ -72,6 +54,7 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.createdBy = createdBy;
   }
 
+  @Override
   public String getKeyDocumentType() {
     return keyDocumentType;
   }
@@ -80,6 +63,7 @@ public final class ManualProgressEntryDto extends ProgressEntryDto
     this.keyDocumentType = keyDocumentType;
   }
 
+  @Override
   public Integer getKeyDocumentVersion() {
     return keyDocumentVersion;
   }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
index 9ffbbaecb..8c3d3962f 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/PatchManualProgressEntryRequest.java
@@ -13,6 +13,4 @@ import org.openapitools.jackson.nullable.JsonNullable;
 public record PatchManualProgressEntryRequest(
     @NotNull @Schema(requiredMode = RequiredMode.NOT_REQUIRED)
         JsonNullable<ManualProgressEntryTypeDto> manualProgressEntryType,
-    @Schema(nullable = true) JsonNullable<String> subject,
-    @Schema(nullable = true) JsonNullable<String> messageText,
     @Schema(nullable = true) JsonNullable<String> note) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
index 71cc179a8..fb9f0bd81 100644
--- a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/SystemProgressEntryDto.java
@@ -12,12 +12,15 @@ import java.util.UUID;
 
 @Schema(name = SystemProgressEntryDto.SCHEMA_NAME)
 @JsonTypeName(SystemProgressEntryDto.SCHEMA_NAME)
-public final class SystemProgressEntryDto extends ProgressEntryDto {
+public final class SystemProgressEntryDto extends ProgressEntryDto
+    implements KeyDocumentAwareProgressEntryDto {
   public static final String SCHEMA_NAME = "SystemProgressEntry";
 
   @NotNull private String systemProgressEntryType;
   @NotNull private TriggerTypeDto triggerType;
   private String changeDescription;
+  private String keyDocumentType;
+  private Integer keyDocumentVersion;
 
   private UUID triggeredBy;
   private String triggeredByUserFirstName;
@@ -85,4 +88,22 @@ public final class SystemProgressEntryDto extends ProgressEntryDto {
   public void setTriggeredByUserLastName(String triggeredByUserLastName) {
     this.triggeredByUserLastName = triggeredByUserLastName;
   }
+
+  @Override
+  public String getKeyDocumentType() {
+    return keyDocumentType;
+  }
+
+  @Override
+  public Integer getKeyDocumentVersion() {
+    return keyDocumentVersion;
+  }
+
+  public void setKeyDocumentVersion(Integer keyDocumentVersion) {
+    this.keyDocumentVersion = keyDocumentVersion;
+  }
+
+  public void setKeyDocumentType(String keyDocumentType) {
+    this.keyDocumentType = keyDocumentType;
+  }
 }
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java
new file mode 100644
index 000000000..6b33a5f1d
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/AddGdprValidationTaskRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model.gdpr;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.UUID;
+
+public record AddGdprValidationTaskRequest(
+    @NotNull UUID procedureId, @NotNull GdprValidationTaskTypeDto type) {}
diff --git a/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java
new file mode 100644
index 000000000..2fb81dea0
--- /dev/null
+++ b/backend/lib-procedures-api/src/main/java/de/eshg/lib/procedure/model/gdpr/GdprValidationTaskTypeDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.model.gdpr;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "GdprProcedureType", description = "A list of types of GDPR procedures.")
+public enum GdprValidationTaskTypeDto {
+  RIGHT_OF_ACCESS,
+  RIGHT_TO_ERASURE
+}
diff --git a/backend/lib-procedures/build.gradle b/backend/lib-procedures/build.gradle
index cf35733a9..f5baa4662 100644
--- a/backend/lib-procedures/build.gradle
+++ b/backend/lib-procedures/build.gradle
@@ -15,6 +15,7 @@ dependencies {
     api project(':lib-auditlog')
     api project(':business-module-persistence-commons')
 
+    implementation project(':lib-commons')
     implementation project(':lib-base-client')
     implementation project(':business-module-commons')
     implementation project(':rest-oauth-client-commons')
diff --git a/backend/lib-procedures/openApi.yaml b/backend/lib-procedures/openApi.yaml
index ad369efea..58aa2b052 100644
--- a/backend/lib-procedures/openApi.yaml
+++ b/backend/lib-procedures/openApi.yaml
@@ -387,6 +387,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1556,6 +1571,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2095,12 +2121,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     DataOrigin:
@@ -2347,6 +2369,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -2626,7 +2654,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -2972,6 +3002,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3017,13 +3061,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3057,13 +3107,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3131,15 +3178,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -3559,6 +3600,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -3570,6 +3616,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
index 84c112aca..fd9d23b91 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Cemetery.java
@@ -5,7 +5,7 @@
 
 package de.eshg.lib.procedure.domain.model;
 
-import de.eshg.domain.model.BaseEntity;
+import de.eshg.domain.model.SequencedBaseEntity;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.Column;
@@ -20,7 +20,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
 @Entity
 @EntityListeners(AuditingEntityListener.class)
-public class Cemetery extends BaseEntity {
+public class Cemetery extends SequencedBaseEntity {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
index 3f26b5fb1..68045f12f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/File.java
@@ -163,7 +163,7 @@ public abstract class File extends BaseEntityWithExternalId implements LockableE
     return deletable;
   }
 
-  public void setDeletable(boolean deletable) {
+  public void updateDeletable(boolean deletable) {
     this.deletable = deletable;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
index 1515086fa..2b22d0be2 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/FileAware.java
@@ -15,15 +15,5 @@ public interface FileAware {
 
   void setFile(File file);
 
-  UUID getCreatedBy();
-
-  String getSubject();
-
-  void setSubject(String subject);
-
-  String getMessageText();
-
-  void setMessageText(String messageText);
-
   boolean supportsUpload(ProcedureFileType fileType);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java
new file mode 100644
index 000000000..40e36601b
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTask.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+import de.eshg.domain.model.BaseEntity;
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import java.time.Instant;
+import java.util.UUID;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+
+@Entity
+public class GdprValidationTask extends BaseEntity {
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @Column(nullable = false, unique = true)
+  private UUID procedureId;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @Column(nullable = false)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private GdprValidationTaskStatus status;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  @Column(nullable = false)
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  private GdprValidationTaskType type;
+
+  @Column(nullable = false)
+  @CreatedDate
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant createdAt;
+
+  @Column(nullable = false)
+  @LastModifiedDate
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant modifiedAt;
+
+  @Column
+  @DataSensitivity(SensitivityLevel.PROTECTED)
+  private Instant closedAt;
+
+  public UUID getProcedureId() {
+    return procedureId;
+  }
+
+  public void setProcedureId(UUID procedureId) {
+    this.procedureId = procedureId;
+  }
+
+  public GdprValidationTaskStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(GdprValidationTaskStatus status) {
+    this.status = status;
+  }
+
+  public GdprValidationTaskType getType() {
+    return type;
+  }
+
+  public void setType(GdprValidationTaskType type) {
+    this.type = type;
+  }
+
+  public Instant getCreatedAt() {
+    return createdAt;
+  }
+
+  public void setCreatedAt(Instant createdAt) {
+    this.createdAt = createdAt;
+  }
+
+  public Instant getModifiedAt() {
+    return modifiedAt;
+  }
+
+  public void setModifiedAt(Instant modifiedAt) {
+    this.modifiedAt = modifiedAt;
+  }
+
+  public Instant getClosedAt() {
+    return closedAt;
+  }
+
+  public void setClosedAt(Instant closedAt) {
+    this.closedAt = closedAt;
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java
new file mode 100644
index 000000000..b0f554e8e
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskStatus.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public enum GdprValidationTaskStatus {
+  OPEN,
+  CLOSED;
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java
new file mode 100644
index 000000000..068bf80c4
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/GdprValidationTaskType.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public enum GdprValidationTaskType {
+  RIGHT_OF_ACCESS,
+  RIGHT_TO_ERASURE,
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
index 127da4687..2792b80e7 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/InboxProgressEntry.java
@@ -54,22 +54,18 @@ public class InboxProgressEntry extends BaseEntityWithExternalId implements File
     this.inboxProgressEntryType = inboxProgressEntryType;
   }
 
-  @Override
   public String getSubject() {
     return subject;
   }
 
-  @Override
   public void setSubject(String subject) {
     this.subject = subject;
   }
 
-  @Override
   public String getMessageText() {
     return messageText;
   }
 
-  @Override
   public void setMessageText(String messageText) {
     this.messageText = messageText;
   }
@@ -84,7 +80,6 @@ public class InboxProgressEntry extends BaseEntityWithExternalId implements File
     this.file = file;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return getInboxProcedure().getCreatedBy();
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java
new file mode 100644
index 000000000..903560f46
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/KeyDocumentAware.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.model;
+
+public sealed interface KeyDocumentAware permits SystemProgressEntry, ManualProgressEntry {
+
+  String getKeyDocumentType();
+
+  void setKeyDocumentType(String keyDocumentType);
+
+  Integer getKeyDocumentVersion();
+
+  void setKeyDocumentVersion(Integer keyDocumentVersion);
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
index 0e64db944..d421df8fc 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Mail.java
@@ -78,4 +78,10 @@ public class Mail extends File {
     super.lock(locked);
     this.attachments.forEach(file -> file.lockByMail(locked));
   }
+
+  @Override
+  public void updateDeletable(boolean deletable) {
+    super.updateDeletable(deletable);
+    this.attachments.forEach(file -> file.updateDeletable(deletable));
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
index a47f36d1b..c559b2ff6 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/MailMetaData.java
@@ -37,6 +37,14 @@ public class MailMetaData extends MetaData {
   @Column(nullable = false)
   private Instant sentDate;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(nullable = false)
+  private String subject;
+
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(nullable = false)
+  private String messageText;
+
   public String getMailFrom() {
     return mailFrom;
   }
@@ -64,4 +72,20 @@ public class MailMetaData extends MetaData {
   public void setSentDate(Instant sentDate) {
     this.sentDate = sentDate;
   }
+
+  public String getSubject() {
+    return subject;
+  }
+
+  public void setSubject(String subject) {
+    this.subject = subject;
+  }
+
+  public String getMessageText() {
+    return messageText;
+  }
+
+  public void setMessageText(String messageText) {
+    this.messageText = messageText;
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
index 789095ffb..c092d63ce 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ManualProgressEntry.java
@@ -21,19 +21,14 @@ import org.springframework.data.annotation.CreatedBy;
 
 @Entity
 @Audited
-public class ManualProgressEntry extends ProgressEntry implements FileAware, LockableEntity {
+public non-sealed class ManualProgressEntry extends ProgressEntry
+    implements LockableEntity, KeyDocumentAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @JdbcType(PostgreSQLEnumJdbcType.class)
   @Column(nullable = false)
   private ManualProgressEntryType manualProgressEntryType;
 
-  @DataSensitivity(SensitivityLevel.SENSITIVE)
-  private String subject;
-
-  @DataSensitivity(SensitivityLevel.SENSITIVE)
-  private String messageText;
-
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private String note;
 
@@ -64,26 +59,6 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     this.manualProgressEntryType = manualProgressEntryType;
   }
 
-  @Override
-  public String getSubject() {
-    return subject;
-  }
-
-  @Override
-  public void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  @Override
-  public String getMessageText() {
-    return messageText;
-  }
-
-  @Override
-  public void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
   public String getNote() {
     return note;
   }
@@ -92,7 +67,6 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     this.note = note;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return createdBy;
   }
@@ -106,18 +80,22 @@ public class ManualProgressEntry extends ProgressEntry implements FileAware, Loc
     return getManualProgressEntryType().supports(fileType);
   }
 
+  @Override
   public String getKeyDocumentType() {
     return keyDocumentType;
   }
 
+  @Override
   public void setKeyDocumentType(String keyDocumentType) {
     this.keyDocumentType = keyDocumentType;
   }
 
+  @Override
   public Integer getKeyDocumentVersion() {
     return keyDocumentVersion;
   }
 
+  @Override
   public void setKeyDocumentVersion(Integer keyDocumentVersion) {
     this.keyDocumentVersion = keyDocumentVersion;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
index b25f53de5..dfcca93ab 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProcessedInboxProgressEntry.java
@@ -16,7 +16,7 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.data.annotation.CreatedBy;
 
 @Entity
-public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAware {
+public class ProcessedInboxProgressEntry extends ProgressEntry {
 
   @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
   @OneToOne(optional = false)
@@ -54,22 +54,18 @@ public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAw
     this.inboxProgressEntryType = inboxProgressEntryType;
   }
 
-  @Override
   public String getSubject() {
     return subject;
   }
 
-  @Override
   public void setSubject(String subject) {
     this.subject = subject;
   }
 
-  @Override
   public String getMessageText() {
     return messageText;
   }
 
-  @Override
   public void setMessageText(String messageText) {
     this.messageText = messageText;
   }
@@ -79,7 +75,6 @@ public class ProcessedInboxProgressEntry extends ProgressEntry implements FileAw
     return false;
   }
 
-  @Override
   public UUID getCreatedBy() {
     return createdBy;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
index d739965e8..61a39d7a3 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/ProgressEntry.java
@@ -30,7 +30,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 @EntityListeners(AuditingEntityListener.class)
 @Audited
 @Table(indexes = @Index(columnList = ProgressEntry_.PROCEDURE_ID))
-public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId {
+public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId implements FileAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
@@ -59,10 +59,12 @@ public abstract class ProgressEntry extends SequencedBaseEntityWithExternalId {
     return modifiedAt;
   }
 
+  @Override
   public File getFile() {
     return file;
   }
 
+  @Override
   public void setFile(File file) {
     this.file = file;
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
index 6576ba21f..b8279203c 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/SystemProgressEntry.java
@@ -15,7 +15,8 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.data.annotation.CreatedBy;
 
 @Entity
-public class SystemProgressEntry extends ProgressEntry {
+public non-sealed class SystemProgressEntry extends ProgressEntry
+    implements KeyDocumentAware, FileAware {
 
   @DataSensitivity(SensitivityLevel.PUBLIC)
   @Column(nullable = false)
@@ -33,6 +34,12 @@ public class SystemProgressEntry extends ProgressEntry {
   @DataSensitivity(SensitivityLevel.SENSITIVE)
   private String changeDescription;
 
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private String keyDocumentType;
+
+  @DataSensitivity(SensitivityLevel.PUBLIC)
+  private Integer keyDocumentVersion;
+
   public String getSystemProgressEntryType() {
     return systemProgressEntryType;
   }
@@ -64,4 +71,29 @@ public class SystemProgressEntry extends ProgressEntry {
   public void setChangeDescription(String changeDescription) {
     this.changeDescription = changeDescription;
   }
+
+  @Override
+  public String getKeyDocumentType() {
+    return keyDocumentType;
+  }
+
+  @Override
+  public void setKeyDocumentType(String keyDocumentType) {
+    this.keyDocumentType = keyDocumentType;
+  }
+
+  @Override
+  public Integer getKeyDocumentVersion() {
+    return keyDocumentVersion;
+  }
+
+  @Override
+  public void setKeyDocumentVersion(Integer keyDocumentVersion) {
+    this.keyDocumentVersion = keyDocumentVersion;
+  }
+
+  @Override
+  public boolean supportsUpload(ProcedureFileType fileType) {
+    return true;
+  }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
index 13e009177..61e7dd745 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/model/Task.java
@@ -139,6 +139,14 @@ public abstract class Task<ProcedureT extends Procedure<ProcedureT, ?, ?, ?>>
     return currentAssignment.assignedById();
   }
 
+  public Instant getAssignmentDate() {
+    if (currentAssignment == null) {
+      return null;
+    }
+
+    return currentAssignment.assignmentDate();
+  }
+
   public void assign(UUID assigneeId, UUID assignedById, Instant assignmentDate) {
     boolean assigneeHasChanged = !Objects.equals(getAssigneeId(), assigneeId);
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
index a8c72008e..541631ef6 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/CemeteryRepository.java
@@ -6,6 +6,7 @@
 package de.eshg.lib.procedure.domain.repository;
 
 import de.eshg.lib.procedure.domain.model.Cemetery;
+import java.time.Instant;
 import java.util.UUID;
 import java.util.stream.Stream;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -15,4 +16,6 @@ public interface CemeteryRepository extends JpaRepository<Cemetery, Long> {
   long countByFormerExternalId(UUID formerExternalId);
 
   Stream<Cemetery> findAllByOrderById();
+
+  long deleteByCreatedAtBefore(Instant createdAt);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java
new file mode 100644
index 000000000..0a31cc399
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/GdprValidationTaskRepository.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.domain.repository;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import java.util.Optional;
+import java.util.UUID;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface GdprValidationTaskRepository
+    extends JpaRepository<GdprValidationTask, Long>, JpaSpecificationExecutor<GdprValidationTask> {
+
+  Optional<GdprValidationTask> findByProcedureId(UUID procedureId);
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
index 2f9f79c5b..b0663261b 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ManualProgressEntryRepository.java
@@ -7,13 +7,10 @@ package de.eshg.lib.procedure.domain.repository;
 
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
 @Repository
@@ -21,31 +18,6 @@ public interface ManualProgressEntryRepository
     extends JpaRepository<ManualProgressEntry, Long>,
         JpaSpecificationExecutor<ManualProgressEntry> {
 
-  @Query(
-      """
-        SELECT e FROM ManualProgressEntry e
-        LEFT JOIN FETCH e.file as file
-        LEFT JOIN FETCH file.attachments
-        WHERE e.procedureId = :procedureId
-        AND e.keyDocumentType = :keyDocumentType
-        AND e.id != :id
-        """)
-  List<ManualProgressEntry>
-      findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
-          @Param("procedureId") Long procedureId,
-          @Param("keyDocumentType") String keyDocumentType,
-          @Param("id") Long id);
-
-  @Query(
-      value =
-          """
-  SELECT count(*) FROM manual_progress_entry mpe
-  LEFT JOIN progress_entry pe ON mpe.id = pe.id
-  WHERE pe.procedure_id = :procedureId AND mpe.key_document_type = :keyDocumentType""",
-      nativeQuery = true)
-  Integer countByProcedureIdAndKeyDocumentType(
-      @Param("procedureId") Long procedureId, @Param("keyDocumentType") String keyDocumentType);
-
   boolean existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
       Long procedureId, String keyDocumentType, ProcedureFileType fileType);
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
index a1f49a7af..ef09417e7 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProcedureRepository.java
@@ -175,6 +175,18 @@ public interface ProcedureRepository<ProcedureT extends Procedure<ProcedureT, ?,
   List<ProcedureT> findByRelatedPersonsCentralFileStateIdInOrderByCreatedAtDescIdAsc(
       Collection<UUID> centralFileStateIds);
 
+  @Query(
+      """
+   SELECT procedure.externalId from #{#entityName} procedure
+   LEFT JOIN procedure.relatedPersons relatedPerson
+   LEFT JOIN procedure.relatedFacilities relatedFacility
+   WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
+   OR relatedFacility.centralFileStateId IN :centralFileStateIds
+   ORDER BY procedure.createdAt DESC, procedure.id ASC
+   """)
+  List<UUID> findIdsByFileStateIds(
+      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds);
+
   @Query(
       """
  SELECT procedure from #{#entityName} procedure
@@ -187,20 +199,6 @@ ORDER BY procedure.createdAt DESC, procedure.id ASC
       @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
       @Param("personType") PersonType personType);
 
-  @Query(
-      """
-    SELECT procedure from #{#entityName} procedure
-    JOIN procedure.relatedPersons relatedPerson
-    WHERE relatedPerson.centralFileStateId IN :centralFileStateIds
-    AND relatedPerson.personType = :personType
-    AND procedure.procedureStatus = :procedureStatus
-    ORDER BY procedure.createdAt DESC, procedure.id ASC
-    """)
-  List<ProcedureT> findByRelatedPersonsCentralFileStateIds(
-      @Param("centralFileStateIds") Collection<UUID> centralFileStateIds,
-      @Param("personType") PersonType personType,
-      @Param("procedureStatus") ProcedureStatus procedureStatus);
-
   List<ProcedureT> findAllByArchivingRelevance(ArchivingRelevance archivingRelevance);
 
   List<ProcedureT> findByProcedureStatusIn(Set<ProcedureStatus> procedureStatuses);
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
index f0acb0cd1..04a2aa978 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/domain/repository/ProgressEntryRepository.java
@@ -40,4 +40,36 @@ public interface ProgressEntryRepository
   @Override
   @EntityGraph(attributePaths = ProgressEntry_.FILE)
   Page<ProgressEntry> findAll(Specification<ProgressEntry> spec, Pageable pageable);
+
+  @Query(
+      value =
+          """
+            SELECT COUNT(*) FROM ProgressEntry progressEntry
+            WHERE progressEntry.procedureId = :procedureId
+            AND (
+            TREAT(progressEntry as ManualProgressEntry).keyDocumentType = :keyDocumentType
+            OR
+            TREAT(progressEntry as SystemProgressEntry).keyDocumentType = :keyDocumentType
+            )
+            """)
+  Integer countByProcedureIdAndKeyDocumentType(
+      @Param("procedureId") Long procedureId, @Param("keyDocumentType") String keyDocumentType);
+
+  @Query(
+      """
+        SELECT progressEntry FROM ProgressEntry progressEntry
+        LEFT JOIN FETCH progressEntry.file as file
+        LEFT JOIN FETCH file.attachments
+        WHERE progressEntry.procedureId = :procedureId
+        AND progressEntry.id != :id
+        AND (
+        TREAT(progressEntry as ManualProgressEntry).keyDocumentType = :keyDocumentType
+        OR
+        TREAT(progressEntry as SystemProgressEntry).keyDocumentType = :keyDocumentType
+        )
+        """)
+  List<ProgressEntry> findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
+      @Param("procedureId") Long procedureId,
+      @Param("keyDocumentType") String keyDocumentType,
+      @Param("id") Long id);
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
index 2bf6528f2..9dcd42194 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/EmlParser.java
@@ -15,9 +15,9 @@ import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
 import de.eshg.file.common.FileType;
 import de.eshg.file.common.FileTypeDetector;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.ImageMetaData;
+import de.eshg.lib.procedure.domain.model.FileContent;
+import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.MailMetaData;
-import de.eshg.lib.procedure.domain.model.PdfMetaData;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.rest.service.error.BadRequestException;
 import jakarta.mail.Address;
@@ -50,22 +50,25 @@ class EmlParser {
 
   private EmlParser() {}
 
-  static ParsedMail parse(byte[] file, boolean deletable) {
+  static Mail parse(byte[] file) {
     try (InputStream inputStream = new ByteArrayInputStream(file)) {
       Session session = Session.getDefaultInstance(new Properties());
       Message message = new FixedMessageIdMimeMessage(session, inputStream);
 
-      ParsedMail parsedMail = new ParsedMail();
+      Mail parsedMail = new Mail();
       parsedMail.setFileType(ProcedureFileType.EML);
-      parsedMail.setSubject(message.getSubject());
-      parsedMail.setMessageText(extractMessageText(message));
-      parsedMail.setMetaData(extractMetaData(message));
-      parsedMail.setDeletable(deletable);
+      parsedMail.addMetaData(extractMetaData(message));
 
       int removedInvalidAttachments = removeAndCountInvalidAttachments(message);
       parsedMail.setRemovedInvalidAttachments(removedInvalidAttachments);
-      parsedMail.getAttachments().addAll(extractAttachments(message, deletable));
-      parsedMail.setContent(extractContent(message));
+      extractAttachments(message).forEach(parsedMail::addAttachment);
+
+      FileContent fileContent = new FileContent();
+      byte[] content = extractContent(message);
+      fileContent.setContent(content);
+
+      parsedMail.setFileSizeBytes(content.length);
+      parsedMail.setFileContent(fileContent);
 
       return parsedMail;
     } catch (MessagingException | IOException e) {
@@ -102,7 +105,8 @@ class EmlParser {
     return part.getContent().toString().strip();
   }
 
-  private static MailMetaData extractMetaData(Message message) throws MessagingException {
+  private static MailMetaData extractMetaData(Message message)
+      throws MessagingException, IOException {
     Address[] mailFrom = message.getFrom();
     Address[] mailTo = message.getAllRecipients();
     Date sentDate = message.getSentDate();
@@ -112,6 +116,8 @@ class EmlParser {
     }
 
     MailMetaData mailMetaData = new MailMetaData();
+    mailMetaData.setSubject(message.getSubject());
+    mailMetaData.setMessageText(extractMessageText(message));
     mailMetaData.setMailFrom(convertAddressesToString(mailFrom));
     mailMetaData.setMailTo(convertAddressesToString(mailTo));
     mailMetaData.setSentDate(sentDate.toInstant());
@@ -156,7 +162,7 @@ class EmlParser {
     return IOUtils.toByteArray(part.getInputStream());
   }
 
-  private static List<File> extractAttachments(Message message, boolean deletable)
+  private static List<File> extractAttachments(Message message)
       throws MessagingException, IOException {
     if (!(message.getContent() instanceof MimeMultipart content)) {
       return Collections.emptyList();
@@ -168,8 +174,7 @@ class EmlParser {
           createFile(
               bodyPart.getFileName(),
               FileTypeMapper.mapToProcedureFileType(parseFileType(bodyPart)),
-              parseFileContent(bodyPart),
-              deletable);
+              parseFileContent(bodyPart));
       files.add(file);
     }
     return files;
@@ -202,24 +207,15 @@ class EmlParser {
     return fileType != null && VALID_ATTACHMENT_FILE_TYPES.contains(fileType);
   }
 
-  private static File createFile(
-      String fileName, ProcedureFileType fileType, byte[] fileContent, boolean deletable)
+  private static File createFile(String fileName, ProcedureFileType fileType, byte[] fileContent)
       throws IOException {
     return switch (fileType) {
-      case JPEG, PNG -> {
-        ImageMetaData imageMetaData = new ImageMetaData();
-        ImageMetaDataExtractor.extract(fileContent, imageMetaData);
-
-        yield FileFactory.createImageWithMetaData(
-            fileName, fileType, fileContent, imageMetaData, deletable);
-      }
-      case PDF -> {
-        PdfMetaData pdfMetaData = new PdfMetaData();
-        PdfMetaDataExtractor.extract(fileContent, pdfMetaData);
-
-        yield FileFactory.createPdfWithMetaData(
-            fileName, fileType, fileContent, pdfMetaData, deletable);
-      }
+      case JPEG, PNG ->
+          FileFactory.createImageWithMetaData(
+              fileName, fileType, fileContent, ImageMetaDataExtractor.fromFileContent(fileContent));
+      case PDF ->
+          FileFactory.createPdfWithMetaData(
+              fileName, fileContent, PdfMetaDataExtractor.fromFileContent(fileContent));
       default -> throw new IllegalStateException("Unexpected value: " + fileType);
     };
   }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java
deleted file mode 100644
index 149256708..000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileAwareResolver.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import de.eshg.lib.procedure.domain.model.FileAware;
-import de.eshg.lib.procedure.domain.model.InboxProgressEntry;
-import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
-import de.eshg.lib.procedure.domain.repository.InboxProgressEntryRepository;
-import de.eshg.lib.procedure.domain.repository.ProgressEntryRepository;
-import de.eshg.rest.service.error.BadRequestException;
-
-public class FileAwareResolver {
-
-  private final InboxProgressEntryRepository inboxProgressEntryRepository;
-  private final ProgressEntryRepository progressEntryRepository;
-
-  public FileAwareResolver(
-      InboxProgressEntryRepository inboxProgressEntryRepository,
-      ProgressEntryRepository progressEntryRepository) {
-    this.inboxProgressEntryRepository = inboxProgressEntryRepository;
-    this.progressEntryRepository = progressEntryRepository;
-  }
-
-  public FileAware resolve(FileAware fileAware) {
-    return switch (fileAware) {
-      case ManualProgressEntry manualProgressEntry ->
-          progressEntryRepository
-              .findByExternalId(manualProgressEntry.getExternalId())
-              .filter(FileAware.class::isInstance)
-              .map(FileAware.class::cast)
-              .orElseThrow(
-                  () -> new BadRequestException("Could not resolve " + getClassName(fileAware)));
-      case InboxProgressEntry inboxProgressEntry ->
-          inboxProgressEntryRepository
-              .findByExternalId(inboxProgressEntry.getExternalId())
-              .orElseThrow(
-                  () -> new BadRequestException("Could not resolve " + getClassName(fileAware)));
-      default ->
-          throw new BadRequestException("Unsupported file aware type: " + getClassName(fileAware));
-    };
-  }
-
-  private String getClassName(FileAware fileAware) {
-    return fileAware.getClass().getSimpleName();
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
index c2f70f64b..4913d1489 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileFactory.java
@@ -9,7 +9,6 @@ import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.FileContent;
 import de.eshg.lib.procedure.domain.model.Image;
 import de.eshg.lib.procedure.domain.model.ImageMetaData;
-import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
 import de.eshg.lib.procedure.domain.model.ProcedureFileType;
@@ -19,49 +18,23 @@ public class FileFactory {
   private FileFactory() {}
 
   public static Image createImageWithMetaData(
-      String fileName,
-      ProcedureFileType fileType,
-      byte[] content,
-      ImageMetaData imageMetaData,
-      boolean deletable) {
+      String fileName, ProcedureFileType fileType, byte[] content, ImageMetaData imageMetaData) {
     Image image = new Image();
     image.addMetaData(imageMetaData);
-    setFileProperties(image, fileName, fileType, content, deletable);
+    setFileProperties(image, fileName, fileType, content);
     return image;
   }
 
   public static Pdf createPdfWithMetaData(
-      String fileName,
-      ProcedureFileType fileType,
-      byte[] content,
-      PdfMetaData pdfMetaData,
-      boolean deletable) {
+      String fileName, byte[] content, PdfMetaData pdfMetaData) {
     Pdf pdf = new Pdf();
     pdf.addMetaData(pdfMetaData);
-    setFileProperties(pdf, fileName, fileType, content, deletable);
+    setFileProperties(pdf, fileName, ProcedureFileType.PDF, content);
     return pdf;
   }
 
-  static Mail fromParsedMail(String fileName, ParsedMail parsedMail) {
-    Mail mail = new Mail();
-    mail.addMetaData(parsedMail.getMetaData());
-    setFileProperties(
-        mail,
-        fileName,
-        parsedMail.getFileType(),
-        parsedMail.getContent(),
-        parsedMail.isDeletable());
-
-    for (File attachment : parsedMail.getAttachments()) {
-      mail.addAttachment(attachment);
-    }
-
-    mail.setRemovedInvalidAttachments(parsedMail.getRemovedInvalidAttachments());
-    return mail;
-  }
-
   private static void setFileProperties(
-      File file, String fileName, ProcedureFileType fileType, byte[] content, boolean deletable) {
+      File file, String fileName, ProcedureFileType fileType, byte[] content) {
     FileContent fileContent = new FileContent();
     fileContent.setContent(content);
 
@@ -69,6 +42,5 @@ public class FileFactory {
     file.setFileName(fileName);
     file.setFileType(fileType);
     file.setFileSizeBytes(content.length);
-    file.setDeletable(deletable);
   }
 }
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
index 8045d93d0..3b861da93 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileStorageService.java
@@ -8,7 +8,6 @@ package de.eshg.lib.procedure.file;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.foureyes.domain.repository.GenericApprovalRequestRepository;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.FileAware;
 import de.eshg.lib.procedure.domain.model.FileContent;
 import de.eshg.lib.procedure.domain.model.FileDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.FileDeletionApprovalRequestNotification;
@@ -39,7 +38,6 @@ import org.springframework.stereotype.Service;
 public class FileStorageService {
 
   private final FileRepository fileRepository;
-  private final FileAwareResolver fileAwareResolver;
   private final ProcedureRepository<?> procedureRepository;
   private final GenericApprovalRequestRepository approvalRequestRepository;
   private final UserHelper userHelper;
@@ -47,32 +45,17 @@ public class FileStorageService {
 
   public FileStorageService(
       FileRepository fileRepository,
-      FileAwareResolver fileAwareResolver,
       ProcedureRepository<?> procedureRepository,
       GenericApprovalRequestRepository approvalRequestRepository,
       UserHelper userHelper,
       AuditLogger auditLogger) {
     this.fileRepository = fileRepository;
-    this.fileAwareResolver = fileAwareResolver;
     this.procedureRepository = procedureRepository;
     this.approvalRequestRepository = approvalRequestRepository;
     this.userHelper = userHelper;
     this.auditLogger = auditLogger;
   }
 
-  public void persistFile(File file, FileAware fileAware) {
-    FileAware resolvedFileAware = fileAwareResolver.resolve(fileAware);
-    resolvedFileAware.setFile(file);
-  }
-
-  public void persistFileAndUpdateProgressEntry(
-      Mail mail, String subject, String messageText, FileAware fileAware) {
-    FileAware resolvedFileAware = fileAwareResolver.resolve(fileAware);
-    resolvedFileAware.setFile(mail);
-    resolvedFileAware.setSubject(subject);
-    resolvedFileAware.setMessageText(messageText);
-  }
-
   File updateFileMetaData(UUID fileId, MetaData metaData) {
     File file = findFileForModificationOrThrow(fileId);
     validateNotAttachedToClosedProcedure(file);
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java
deleted file mode 100644
index 0e00079a7..000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadService.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import static de.eshg.lib.procedure.file.FileFactory.createImageWithMetaData;
-import static de.eshg.lib.procedure.file.FileFactory.createPdfWithMetaData;
-
-import de.eshg.file.common.FileTypeDetector;
-import de.eshg.file.common.PdfAConformanceValidator;
-import de.eshg.lib.procedure.domain.model.*;
-import de.eshg.lib.procedure.model.FileMetaDataDto;
-import java.io.IOException;
-import java.util.Optional;
-import org.springframework.stereotype.Service;
-import org.springframework.web.multipart.MultipartFile;
-
-@Service
-public class FileUploadService {
-
-  private final FileUploadValidator fileUploadValidator;
-  private final FileStorageService fileStorageService;
-
-  public FileUploadService(
-      FileUploadValidator fileUploadValidator, FileStorageService fileStorageService) {
-    this.fileUploadValidator = fileUploadValidator;
-    this.fileStorageService = fileStorageService;
-  }
-
-  public void handleFile(FileAware fileAware, MultipartFile file, FileMetaDataDto fileMetaData)
-      throws IOException {
-    ProcedureFileType fileType =
-        FileTypeMapper.mapToProcedureFileType(
-            FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes()));
-    fileUploadValidator.validateFileAwareSupportsFileUpload(fileAware, fileType);
-
-    switch (fileType) {
-      case JPEG, PNG -> handleImage(fileAware, fileType, file, fileMetaData);
-      case PDF -> handlePdf(fileAware, fileType, file, fileMetaData);
-      case EML -> handleMailEml(fileAware, fileType, file, fileMetaData);
-    }
-  }
-
-  private void handleImage(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    ImageMetaData imageMetaData = new ImageMetaData();
-    ImageMetaDataExtractor.extract(fileContent, imageMetaData);
-    imageMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    Image image =
-        createImageWithMetaData(
-            fileName, fileType, fileContent, imageMetaData, isFileDeletable(fileAware));
-
-    fileStorageService.persistFile(image, fileAware);
-  }
-
-  private void handlePdf(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    PdfAConformanceValidator.validate(fileContent);
-
-    PdfMetaData pdfMetaData = new PdfMetaData();
-    PdfMetaDataExtractor.extract(fileContent, pdfMetaData);
-    pdfMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    Pdf pdf =
-        createPdfWithMetaData(
-            fileName, fileType, fileContent, pdfMetaData, isFileDeletable(fileAware));
-
-    fileStorageService.persistFile(pdf, fileAware);
-  }
-
-  private void handleMailEml(
-      FileAware fileAware,
-      ProcedureFileType fileType,
-      MultipartFile file,
-      FileMetaDataDto fileMetaData)
-      throws IOException {
-    byte[] fileContent = file.getBytes();
-    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
-
-    ParsedMail parsedMail = EmlParser.parse(fileContent, isFileDeletable(fileAware));
-
-    Mail mail = FileFactory.fromParsedMail(fileName, parsedMail);
-    String subject = parsedMail.getSubject();
-    String messageText = parsedMail.getMessageText();
-
-    MailMetaData mailMetaData = mail.getMetaData();
-    mailMetaData.setDescription(getDescriptionOrElseNull(fileMetaData));
-
-    fileStorageService.persistFileAndUpdateProgressEntry(mail, subject, messageText, fileAware);
-  }
-
-  private boolean isFileDeletable(FileAware fileAware) {
-    return fileAware instanceof ManualProgressEntry;
-  }
-
-  private String getDescriptionOrElseNull(FileMetaDataDto fileMetaData) {
-    return Optional.ofNullable(fileMetaData).map(FileMetaDataDto::getDescription).orElse(null);
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java
deleted file mode 100644
index fbae13626..000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/FileUploadValidator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import static de.eshg.lib.procedure.domain.model.ProcedureFileType.EML;
-
-import de.eshg.lib.procedure.domain.model.FileAware;
-import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import de.eshg.lib.procedure.domain.repository.ManualProgressEntryRepository;
-import de.eshg.rest.service.error.BadRequestException;
-import java.util.Objects;
-
-public class FileUploadValidator {
-
-  private final ManualProgressEntryRepository manualProgressEntryRepository;
-
-  public FileUploadValidator(ManualProgressEntryRepository manualProgressEntryRepository) {
-    this.manualProgressEntryRepository = manualProgressEntryRepository;
-  }
-
-  void validateFileAwareSupportsFileUpload(FileAware fileAware, ProcedureFileType fileType) {
-    validateProgressEntryTypeSupportsFileType(fileAware, fileType);
-    validateFileAwareSubjectAndMessageTextIsNull(fileAware, fileType);
-
-    if (fileAware instanceof ManualProgressEntry manualProgressEntry) {
-      validateKeyDocumentsUniformFileTypes(manualProgressEntry, fileType);
-    }
-  }
-
-  private void validateProgressEntryTypeSupportsFileType(
-      FileAware fileAware, ProcedureFileType fileType) {
-    if (!fileAware.supportsUpload(fileType)) {
-      throw new BadRequestException(
-          "File upload not supported for file type `%s`.".formatted(fileType));
-    }
-  }
-
-  private void validateFileAwareSubjectAndMessageTextIsNull(
-      FileAware fileAware, ProcedureFileType fileType) {
-    if (EML.equals(fileType) && hasFileAwareSubjectOrMessageText(fileAware)) {
-      throw new BadRequestException(
-          "Subject and message text are parsed from eml and should not be given");
-    }
-  }
-
-  private boolean hasFileAwareSubjectOrMessageText(FileAware fileAware) {
-    return !Objects.isNull(fileAware.getSubject()) || !Objects.isNull(fileAware.getMessageText());
-  }
-
-  private void validateKeyDocumentsUniformFileTypes(
-      ManualProgressEntry manualProgressEntry, ProcedureFileType fileType) {
-    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
-
-    if (keyDocumentType == null) {
-      return;
-    }
-
-    if (manualProgressEntryRepository.existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
-        manualProgressEntry.getProcedureId(), keyDocumentType, fileType)) {
-      throw new BadRequestException(
-          "Key document type `%s` does not support file type `%s`."
-              .formatted(keyDocumentType, fileType));
-    }
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
index 13c114d90..84c9598d9 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ImageMetaDataExtractor.java
@@ -25,13 +25,15 @@ class ImageMetaDataExtractor {
 
   private ImageMetaDataExtractor() {}
 
-  static void extract(byte[] file, ImageMetaData imageMetaData) {
+  static ImageMetaData fromFileContent(byte[] file) {
     Metadata metadata = readImageMetaData(file);
 
     Instant createdDate =
         getExifDateOriginal(metadata).or(() -> getIptcDateCreated(metadata)).orElse(null);
 
+    ImageMetaData imageMetaData = new ImageMetaData();
     imageMetaData.setCreatedDate(createdDate);
+    return imageMetaData;
   }
 
   private static Metadata readImageMetaData(byte[] file) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java
new file mode 100644
index 000000000..50cba3557
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/MultipartFileParser.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.file;
+
+import static de.eshg.lib.procedure.file.FileFactory.createImageWithMetaData;
+import static de.eshg.lib.procedure.file.FileFactory.createPdfWithMetaData;
+
+import de.eshg.file.common.FileTypeDetector;
+import de.eshg.file.common.PdfAConformanceValidator;
+import de.eshg.lib.procedure.domain.model.*;
+import de.eshg.lib.procedure.model.FileMetaDataDto;
+import de.eshg.rest.service.error.BadRequestException;
+import java.io.IOException;
+import java.util.Optional;
+import org.springframework.web.multipart.MultipartFile;
+
+public final class MultipartFileParser {
+
+  private MultipartFileParser() {}
+
+  public static File parseFile(MultipartFile file) throws IOException {
+    if (file == null) {
+      return null;
+    }
+
+    ProcedureFileType fileType = parseProcedureFileType(file);
+    return switch (fileType) {
+      case JPEG, PNG -> parseImage(fileType, file);
+      case PDF -> parsePdf(file);
+      case EML -> parseEmail(file);
+    };
+  }
+
+  public static ProcedureFileType parseProcedureFileType(MultipartFile file) throws IOException {
+    return FileTypeMapper.mapToProcedureFileType(
+        FileTypeDetector.getSupportedFileTypeOrThrow(file.getBytes()));
+  }
+
+  public static void validateProgressEntryTypeSupportsFileType(
+      FileAware fileAware, MultipartFile multipartFile) throws IOException {
+    ProcedureFileType fileType = parseProcedureFileType(multipartFile);
+    if (!fileAware.supportsUpload(fileType)) {
+      throw new BadRequestException(
+          "File upload not supported for file type `%s`.".formatted(fileType));
+    }
+  }
+
+  public static void validateProgressEntryTypeSupportsFileType(
+      FileAware fileAware, ProcedureFileType fileType) {
+    if (!fileAware.supportsUpload(fileType)) {
+      throw new BadRequestException(
+          "File upload not supported for file type `%s`.".formatted(fileType));
+    }
+  }
+
+  private static Image parseImage(ProcedureFileType fileType, MultipartFile file)
+      throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName = FileExtensionEnricher.enrich(file.getOriginalFilename(), fileType);
+
+    ImageMetaData imageMetaData = ImageMetaDataExtractor.fromFileContent(fileContent);
+    return createImageWithMetaData(fileName, fileType, fileContent, imageMetaData);
+  }
+
+  private static Pdf parsePdf(MultipartFile file) throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName =
+        FileExtensionEnricher.enrich(file.getOriginalFilename(), ProcedureFileType.PDF);
+
+    PdfAConformanceValidator.validate(fileContent);
+
+    PdfMetaData pdfMetaData = PdfMetaDataExtractor.fromFileContent(fileContent);
+    return createPdfWithMetaData(fileName, fileContent, pdfMetaData);
+  }
+
+  private static Mail parseEmail(MultipartFile file) throws IOException {
+    byte[] fileContent = file.getBytes();
+    String fileName =
+        FileExtensionEnricher.enrich(file.getOriginalFilename(), ProcedureFileType.EML);
+
+    Mail mail = EmlParser.parse(fileContent);
+    mail.setFileName(fileName);
+    return mail;
+  }
+
+  private static String getDescriptionOrElseNull(FileMetaDataDto fileMetaData) {
+    return Optional.ofNullable(fileMetaData).map(FileMetaDataDto::getDescription).orElse(null);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java
deleted file mode 100644
index d31455a95..000000000
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/ParsedMail.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.lib.procedure.file;
-
-import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.MailMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
-import java.util.ArrayList;
-import java.util.List;
-
-class ParsedMail {
-  private String fileName;
-  private ProcedureFileType fileType;
-  private byte[] content;
-  private String subject;
-  private String messageText;
-  private MailMetaData metaData;
-  private boolean deletable;
-  private List<File> attachments = new ArrayList<>();
-  private int removedInvalidAttachments;
-
-  public String getFileName() {
-    return fileName;
-  }
-
-  public void setFileName(String fileName) {
-    this.fileName = fileName;
-  }
-
-  ProcedureFileType getFileType() {
-    return fileType;
-  }
-
-  void setFileType(ProcedureFileType fileType) {
-    this.fileType = fileType;
-  }
-
-  byte[] getContent() {
-    return content;
-  }
-
-  void setContent(byte[] content) {
-    this.content = content;
-  }
-
-  String getSubject() {
-    return subject;
-  }
-
-  void setSubject(String subject) {
-    this.subject = subject;
-  }
-
-  String getMessageText() {
-    return messageText;
-  }
-
-  void setMessageText(String messageText) {
-    this.messageText = messageText;
-  }
-
-  MailMetaData getMetaData() {
-    return metaData;
-  }
-
-  void setMetaData(MailMetaData metaData) {
-    this.metaData = metaData;
-  }
-
-  public boolean isDeletable() {
-    return deletable;
-  }
-
-  public void setDeletable(boolean deletable) {
-    this.deletable = deletable;
-  }
-
-  List<File> getAttachments() {
-    return attachments;
-  }
-
-  int getRemovedInvalidAttachments() {
-    return removedInvalidAttachments;
-  }
-
-  void setRemovedInvalidAttachments(int removedInvalidAttachments) {
-    this.removedInvalidAttachments = removedInvalidAttachments;
-  }
-}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
index a169a9472..4cccb68ab 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/file/PdfMetaDataExtractor.java
@@ -20,7 +20,7 @@ class PdfMetaDataExtractor {
 
   private PdfMetaDataExtractor() {}
 
-  static void extract(byte[] fileContent, PdfMetaData pdfMetaData) throws IOException {
+  static PdfMetaData fromFileContent(byte[] fileContent) throws IOException {
     Metadata metadata = new Metadata();
 
     try (InputStream inputStream = new ByteArrayInputStream(fileContent)) {
@@ -30,7 +30,9 @@ class PdfMetaDataExtractor {
     Instant createdDate =
         getPdfDocInfoCreated(metadata).or(() -> getXmpCreatedDate(metadata)).orElse(null);
 
+    PdfMetaData pdfMetaData = new PdfMetaData();
     pdfMetaData.setCreatedDate(createdDate);
+    return pdfMetaData;
   }
 
   private static Optional<Instant> getPdfDocInfoCreated(Metadata metadata) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java
new file mode 100644
index 000000000..31bff3fbe
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskController.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.gdpr;
+
+import de.eshg.base.feature.BaseFeature;
+import de.eshg.base.feature.BaseFeatureTogglesApi;
+import de.eshg.base.gdpr.GdprProcedureApi;
+import de.eshg.base.gdpr.GetGdprProcedureFileStateIdsResponse;
+import de.eshg.lib.procedure.api.GdprValidationTaskApi;
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskStatus;
+import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import de.eshg.lib.procedure.mapping.GdprValidationTaskMapper;
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import de.eshg.rest.service.error.BadRequestException;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.HttpClientErrorException;
+
+@RestController
+@Tag(name = "GdprValidationTask")
+public class GdprValidationTaskController implements GdprValidationTaskApi {
+
+  private final GdprValidationTaskService service;
+  private final BaseFeatureTogglesApi baseFeatureTogglesApi;
+  private final GdprProcedureApi baseGdprProcedureApi;
+  private final ProcedureRepository<?> procedureRepository;
+
+  public GdprValidationTaskController(
+      GdprValidationTaskService service,
+      GdprProcedureApi baseGdprProcedureApi,
+      BaseFeatureTogglesApi baseFeatureTogglesApi,
+      ProcedureRepository<?> procedureRepository) {
+    this.service = service;
+    this.baseGdprProcedureApi = baseGdprProcedureApi;
+    this.baseFeatureTogglesApi = baseFeatureTogglesApi;
+    this.procedureRepository = procedureRepository;
+  }
+
+  @Override
+  @Transactional
+  public void addGdprValidationTask(AddGdprValidationTaskRequest request) {
+    if (!baseFeatureTogglesApi
+        .getFeatureToggles()
+        .enabledNewFeatures()
+        .contains(BaseFeature.GDPR)) {
+      throw new BadRequestException("New feature %s is not enabled".formatted(BaseFeature.GDPR));
+    }
+    GdprValidationTask procedure = GdprValidationTaskMapper.mapGdprValidationTaskToDm(request);
+    GetGdprProcedureFileStateIdsResponse fileStateIds;
+    try {
+      fileStateIds = baseGdprProcedureApi.getFileStateIds(request.procedureId());
+    } catch (HttpClientErrorException e) {
+      throw new BadRequestException(
+          "Error while getting file state ids from base: " + e.getMessage());
+    }
+    if (fileStateIds.facilityFileStateIds().isEmpty()
+        && fileStateIds.personFileStateIds().isEmpty()) {
+      throw new BadRequestException(
+          "The base GDPR procedure %s does not have any file state"
+              .formatted(request.procedureId()));
+    } else if (fileStateIds.facilityFileStateIds().isEmpty()) {
+      List<UUID> procedureIds =
+          procedureRepository.findIdsByFileStateIds(fileStateIds.personFileStateIds());
+      if (procedureIds.isEmpty()) {
+        procedure.setStatus(GdprValidationTaskStatus.CLOSED);
+      }
+    } else if (fileStateIds.personFileStateIds().isEmpty()) {
+      List<UUID> procedureIds =
+          procedureRepository.findIdsByFileStateIds(fileStateIds.facilityFileStateIds());
+      if (procedureIds.isEmpty()) {
+        procedure.setStatus(GdprValidationTaskStatus.CLOSED);
+      }
+    } else {
+      throw new IllegalStateException(
+          "The base Gdpr procedure %s has BOTH facility and person file states associated with it"
+              .formatted(request.procedureId()));
+    }
+    service.add(procedure);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java
new file mode 100644
index 000000000..6fbeeca26
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/gdpr/GdprValidationTaskService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.gdpr;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.repository.GdprValidationTaskRepository;
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class GdprValidationTaskService {
+  private final GdprValidationTaskRepository repository;
+  private final Clock clock;
+  private static final Logger log = LoggerFactory.getLogger(GdprValidationTaskService.class);
+
+  public GdprValidationTaskService(GdprValidationTaskRepository repository, Clock clock) {
+    this.repository = repository;
+    this.clock = clock;
+  }
+
+  public GdprValidationTask add(GdprValidationTask procedure) {
+    Optional<GdprValidationTask> existingTask =
+        repository.findByProcedureId(procedure.getProcedureId());
+    if (existingTask.isPresent()) {
+      log.info(
+          "A GdprValidationTask already exists for GdprProcedure with id {}",
+          existingTask.get().getProcedureId());
+      return existingTask.get();
+    }
+
+    Instant now = clock.instant();
+    procedure.setCreatedAt(now);
+    procedure.setModifiedAt(now);
+
+    return repository.save(procedure);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
index fd4cc2635..08feb4c8f 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/archiving/ArchivingJob.java
@@ -50,7 +50,7 @@ public class ArchivingJob<ProcedureT extends Procedure<ProcedureT, ?, ?, ?>> {
     this.clock = clock;
   }
 
-  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.archiving.schedule}")
+  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.archiving.schedule:@daily}")
   public void run() {
     boolean withinGracePeriod = isWithinGracePeriod();
     logger.info(
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java
new file mode 100644
index 000000000..b1722ac17
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeeping.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import de.eshg.lib.procedure.domain.repository.CemeteryRepository;
+import jakarta.transaction.Transactional;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.LocalDate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CemeteryHousekeeping {
+  private static final Logger logger = LoggerFactory.getLogger(CemeteryHousekeeping.class);
+  private final CemeteryRepository repository;
+  private final CemeteryHousekeepingProperties properties;
+  private final Clock clock;
+
+  public CemeteryHousekeeping(
+      CemeteryRepository repository, CemeteryHousekeepingProperties properties, Clock clock) {
+    this.repository = repository;
+    this.properties = properties;
+    this.clock = clock;
+  }
+
+  @Scheduled(cron = "${de.eshg.lib.procedure.housekeeping.cemetery.schedule:@daily}")
+  @Transactional
+  void run() {
+    Instant retentionExpirationDay =
+        LocalDate.now(clock)
+            .minusDays(properties.getRetentionTimeDays())
+            .atStartOfDay(clock.getZone())
+            .toInstant();
+    logger.info(
+        "Attempting to delete all cemetery entries created before {}", retentionExpirationDay);
+    long numberOfDeletedEntries = repository.deleteByCreatedAtBefore(retentionExpirationDay);
+    logger.info("Successfully deleted {} cemetery entries", numberOfDeletedEntries);
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java
new file mode 100644
index 000000000..585625ff9
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingConfiguration.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@EnableConfigurationProperties(CemeteryHousekeepingProperties.class)
+@Import({CemeteryHousekeeping.class})
+public class CemeteryHousekeepingConfiguration {}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java
new file mode 100644
index 000000000..a75618c2e
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/housekeeping/cemetery/CemeteryHousekeepingProperties.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.housekeeping.cemetery;
+
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties("de.eshg.lib.procedure.housekeeping.cemetery")
+public class CemeteryHousekeepingProperties {
+
+  @NotNull @Positive private int retentionTimeDays = 365;
+
+  public int getRetentionTimeDays() {
+    return retentionTimeDays;
+  }
+
+  public void setRetentionTimeDays(int retentionTimeDays) {
+    this.retentionTimeDays = retentionTimeDays;
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
index 9262d83de..f88254376 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/inbox/InboxProcedureController.java
@@ -6,6 +6,8 @@
 package de.eshg.lib.procedure.inbox;
 
 import static de.eshg.lib.procedure.MapperHelper.mapEnumSet;
+import static de.eshg.lib.procedure.file.MultipartFileParser.parseFile;
+import static de.eshg.lib.procedure.file.MultipartFileParser.validateProgressEntryTypeSupportsFileType;
 import static de.eshg.lib.procedure.mapping.InboxProcedureMapper.toInterfaceTypeWithResolvedFile;
 import static de.eshg.lib.procedure.model.GetInboxProceduresSortOrderDto.*;
 
@@ -13,12 +15,14 @@ import de.eshg.base.feature.BaseFeature;
 import de.eshg.base.feature.BaseFeatureTogglesApi;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.lib.procedure.api.InboxProcedureApi;
+import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.InboxProcedure;
 import de.eshg.lib.procedure.domain.model.InboxProcedureStatus;
 import de.eshg.lib.procedure.domain.model.InboxProcedure_;
+import de.eshg.lib.procedure.domain.model.InboxProgressEntry;
+import de.eshg.lib.procedure.domain.model.Mail;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.procedure.domain.repository.InboxProcedureRepository;
-import de.eshg.lib.procedure.file.FileUploadService;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.mapping.InboxProcedureMapper;
 import de.eshg.lib.procedure.mapping.ProcedureMapper;
@@ -60,19 +64,16 @@ import org.springframework.web.multipart.MultipartFile;
 public class InboxProcedureController implements InboxProcedureApi {
 
   private final InboxProcedureRepository inboxProcedureRepository;
-  private final FileUploadService fileUploadService;
   private final Clock clock;
   private final BaseFeatureTogglesApi baseFeatureTogglesApi;
   private final UserHelper userHelper;
 
   public InboxProcedureController(
       InboxProcedureRepository inboxProcedureRepository,
-      FileUploadService fileUploadService,
       Clock clock,
       BaseFeatureTogglesApi baseFeatureTogglesApi,
       UserHelper userHelper) {
     this.inboxProcedureRepository = inboxProcedureRepository;
-    this.fileUploadService = fileUploadService;
     this.clock = clock;
     this.baseFeatureTogglesApi = baseFeatureTogglesApi;
     this.userHelper = userHelper;
@@ -89,15 +90,36 @@ public class InboxProcedureController implements InboxProcedureApi {
     validateContactDetails(createInboxProcedureRequest.contactDetails());
     InboxProcedure inboxProcedure =
         InboxProcedureMapper.createInboxProcedure(createInboxProcedureRequest, clock);
-    InboxProcedure persistedInboxProcedure = inboxProcedureRepository.save(inboxProcedure);
 
     if (Optional.ofNullable(file).isPresent()) {
-      fileUploadService.handleFile(
-          persistedInboxProcedure.getInboxProgressEntry(), file, fileMetaData);
+      InboxProgressEntry inboxProgressEntry = inboxProcedure.getInboxProgressEntry();
+      validateProgressEntryTypeSupportsFileType(inboxProgressEntry, file);
+
+      File parsedFile = parseFile(file);
+      Optional.ofNullable(fileMetaData)
+          .map(FileMetaDataDto::getDescription)
+          .ifPresent(parsedFile.getMetaData()::setDescription);
+
+      copySubjectAndMessageTextFromMailIfNecessary(inboxProgressEntry, parsedFile);
+      inboxProgressEntry.setFile(parsedFile);
     }
 
-    inboxProcedureRepository.flush();
-    return InboxProcedureMapper.toInterfaceType(inboxProcedure);
+    return InboxProcedureMapper.toInterfaceType(inboxProcedureRepository.save(inboxProcedure));
+  }
+
+  private void copySubjectAndMessageTextFromMailIfNecessary(
+      InboxProgressEntry inboxProgressEntry, File file) {
+    if (!(file instanceof Mail mail)) {
+      return;
+    }
+
+    if (inboxProgressEntry.getSubject() != null || inboxProgressEntry.getMessageText() != null) {
+      throw new BadRequestException(
+          "Subject and message text are parsed from eml and should not be given");
+    }
+
+    inboxProgressEntry.setSubject(mail.getMetaData().getSubject());
+    inboxProgressEntry.setMessageText(mail.getMetaData().getMessageText());
   }
 
   @Override
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
index ba53ea0da..6d8f02760 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/FileMapper.java
@@ -174,6 +174,8 @@ public final class FileMapper {
     }
 
     MailMetaDataDto metaDataDto = new MailMetaDataDto();
+    metaDataDto.setSubject(metaData.getSubject());
+    metaDataDto.setMessageText(metaData.getMessageText());
     metaDataDto.setMailFrom(metaData.getMailFrom());
     metaDataDto.setMailTo(metaData.getMailTo());
     metaDataDto.setSentDate(metaData.getSentDate());
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java
new file mode 100644
index 000000000..7cae1076d
--- /dev/null
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/GdprValidationTaskMapper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.procedure.mapping;
+
+import de.eshg.lib.procedure.domain.model.GdprValidationTask;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskStatus;
+import de.eshg.lib.procedure.domain.model.GdprValidationTaskType;
+import de.eshg.lib.procedure.model.gdpr.AddGdprValidationTaskRequest;
+import de.eshg.lib.procedure.model.gdpr.GdprValidationTaskTypeDto;
+
+public class GdprValidationTaskMapper {
+
+  private GdprValidationTaskMapper() {
+    throw new IllegalStateException("Utility class");
+  }
+
+  public static GdprValidationTask mapGdprValidationTaskToDm(AddGdprValidationTaskRequest request) {
+    GdprValidationTask task = new GdprValidationTask();
+    task.setProcedureId(request.procedureId());
+    task.setType(mapToDm(request.type()));
+    task.setStatus(GdprValidationTaskStatus.OPEN);
+    return task;
+  }
+
+  public static GdprValidationTaskType mapToDm(GdprValidationTaskTypeDto type) {
+    return switch (type) {
+      case null -> null;
+      case RIGHT_OF_ACCESS -> GdprValidationTaskType.RIGHT_OF_ACCESS;
+      case RIGHT_TO_ERASURE -> GdprValidationTaskType.RIGHT_TO_ERASURE;
+    };
+  }
+}
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
index 9d0c18530..863a89c19 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/mapping/ProgressEntryMapper.java
@@ -66,6 +66,8 @@ public class ProgressEntryMapper {
     systemProgressEntryDto.setTriggeredBy(progressEntry.getTriggeredBy());
     systemProgressEntryDto.setChangeDescription(progressEntry.getChangeDescription());
     systemProgressEntryDto.setTriggerType(toInterfaceType(progressEntry.getTriggerType()));
+    systemProgressEntryDto.setKeyDocumentType(progressEntry.getKeyDocumentType());
+    systemProgressEntryDto.setKeyDocumentVersion(progressEntry.getKeyDocumentVersion());
     fillGeneralProgressEntry(systemProgressEntryDto, progressEntry);
     return systemProgressEntryDto;
   }
@@ -74,8 +76,6 @@ public class ProgressEntryMapper {
     ManualProgressEntryDto manualProgressEntryDto = new ManualProgressEntryDto();
     manualProgressEntryDto.setManualProgressEntryType(
         toInterfaceType(progressEntry.getManualProgressEntryType()));
-    manualProgressEntryDto.setSubject(progressEntry.getSubject());
-    manualProgressEntryDto.setMessageText(progressEntry.getMessageText());
     manualProgressEntryDto.setNote(progressEntry.getNote());
     manualProgressEntryDto.setCreatedBy(progressEntry.getCreatedBy());
     manualProgressEntryDto.setKeyDocumentType(progressEntry.getKeyDocumentType());
@@ -142,8 +142,6 @@ public class ProgressEntryMapper {
     ManualProgressEntry manualProgressEntry = new ManualProgressEntry();
     manualProgressEntry.setManualProgressEntryType(
         toDomainType(createRequest.manualProgressEntryType()));
-    manualProgressEntry.setSubject(createRequest.subject());
-    manualProgressEntry.setMessageText(createRequest.messageText());
     manualProgressEntry.setNote(createRequest.note());
     manualProgressEntry.setKeyDocumentType(createRequest.keyDocumentType());
     return manualProgressEntry;
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
index 34f03e287..5d075e892 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureSearchService.java
@@ -19,11 +19,15 @@ import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.procedure.domain.model.PersonType;
 import de.eshg.lib.procedure.domain.model.Procedure;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.Procedure_;
 import de.eshg.lib.procedure.domain.model.RelatedFacility;
 import de.eshg.lib.procedure.domain.model.RelatedPerson;
+import de.eshg.lib.procedure.domain.model.RelatedPerson_;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.lib.procedure.helper.FacilityFileStateSearchableStringFormatter;
 import de.eshg.lib.procedure.helper.PersonFileStateSearchableStringFormatter;
+import jakarta.persistence.criteria.Root;
+import jakarta.persistence.criteria.Subquery;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -42,6 +46,9 @@ import java.util.stream.Collectors;
 import org.apache.commons.text.similarity.FuzzyScore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StopWatch;
 
@@ -109,8 +116,12 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
     return foundProcedures;
   }
 
-  public Map<PersonKeyAttributes, List<ProcedureT>> searchOpenProceduresByPersons(
-      Set<PersonKeyAttributes> searchAttributes, PersonType personType) {
+  public <PersonT extends RelatedPerson<ProcedureT>>
+      Map<PersonKeyAttributes, List<ProcedureT>> searchProceduresByPersons(
+          Set<PersonKeyAttributes> searchAttributes,
+          PersonType personType,
+          Specification<ProcedureT> additionalProcedureSpecification,
+          Class<PersonT> relatedPersonClass) {
     if (searchAttributes.isEmpty()) {
       return Map.of();
     }
@@ -127,9 +138,24 @@ public class ProcedureSearchService<ProcedureT extends Procedure<ProcedureT, ?,
     List<UUID> allPersonFileStateIds =
         fileStateIdsByPersonAttributes.values().stream().flatMap(Collection::stream).toList();
 
+    Specification<ProcedureT> proceduresByRelatedPersons =
+        (root, query, criteriaBuilder) -> {
+          Subquery<PersonT> relatedPersonSubquery = query.subquery(relatedPersonClass);
+          Root<PersonT> personRoot = relatedPersonSubquery.from(relatedPersonClass);
+
+          relatedPersonSubquery.where(
+              criteriaBuilder.and(
+                  criteriaBuilder.equal(personRoot.get(RelatedPerson_.procedure), root),
+                  personRoot.get(RelatedPerson_.centralFileStateId).in(allPersonFileStateIds),
+                  criteriaBuilder.equal(personRoot.get(RelatedPerson_.personType), personType)));
+          return criteriaBuilder.exists(relatedPersonSubquery);
+        };
+
     List<ProcedureT> allProcedures =
-        procedureRepository.findByRelatedPersonsCentralFileStateIds(
-            allPersonFileStateIds, personType, ProcedureStatus.OPEN);
+        procedureRepository.findAll(
+            proceduresByRelatedPersons.and(additionalProcedureSpecification),
+            Sort.by(Direction.DESC, Procedure_.CREATED_AT)
+                .and(Sort.by(Direction.ASC, Procedure_.ID)));
 
     Map<UUID, List<ProcedureT>> proceduresPerPersonFileStateId = new LinkedHashMap<>();
     for (ProcedureT procedure : allProcedures) {
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
index 01c5bd2ed..c6ae40685 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryController.java
@@ -18,6 +18,7 @@ import de.eshg.lib.foureyes.model.ApprovalRequestDto;
 import de.eshg.lib.foureyes.model.CreateApprovalRequestRequest;
 import de.eshg.lib.procedure.api.ProgressEntryApi;
 import de.eshg.lib.procedure.domain.model.InboxProgressEntryType;
+import de.eshg.lib.procedure.domain.model.KeyDocumentAware;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryType;
@@ -45,6 +46,7 @@ import de.eshg.lib.procedure.model.GetProgressEntriesResponse;
 import de.eshg.lib.procedure.model.GetProgressEntriesSortOptions;
 import de.eshg.lib.procedure.model.GetProgressEntryPaginationOptions;
 import de.eshg.lib.procedure.model.GetProgressEntryResponse;
+import de.eshg.lib.procedure.model.KeyDocumentAwareProgressEntryDto;
 import de.eshg.lib.procedure.model.ManualProgressEntryDto;
 import de.eshg.lib.procedure.model.PatchManualProgressEntryRequest;
 import de.eshg.lib.procedure.model.ProgressEntryClassDto;
@@ -302,31 +304,30 @@ public class ProgressEntryController<P extends Procedure<P, ?, ?, ?>> implements
   public GetProgressEntryResponse getProgressEntry(UUID procedureId, UUID progressEntryId) {
     ProgressEntry progressEntry =
         progressEntryService.getProgressEntryOrThrow(procedureId, progressEntryId);
-
-    List<ManualProgressEntryDto> relatedKeyDocumentProgressEntries =
-        progressEntry instanceof ManualProgressEntry manualProgressEntry
-            ? getRelatedKeyDocumentProgressEntries(manualProgressEntry)
-            : Collections.emptyList();
-
     return new GetProgressEntryResponse(
-        mapAndEnrichWithFileDetails(progressEntry), relatedKeyDocumentProgressEntries);
+        mapAndEnrichWithFileDetails(progressEntry),
+        getRelatedKeyDocumentProgressEntries(progressEntry));
   }
 
-  private List<ManualProgressEntryDto> getRelatedKeyDocumentProgressEntries(
-      ManualProgressEntry manualProgressEntry) {
-    if (manualProgressEntry.getKeyDocumentType() == null) {
+  private List<KeyDocumentAwareProgressEntryDto> getRelatedKeyDocumentProgressEntries(
+      ProgressEntry progressEntry) {
+    if (!(progressEntry instanceof KeyDocumentAware keyDocumentAware)) {
+      return Collections.emptyList();
+    }
+
+    if (keyDocumentAware.getKeyDocumentType() == null) {
       return Collections.emptyList();
     }
 
-    return manualProgressEntryRepository
+    return progressEntryRepository
         .findAllByProcedureIdAndKeyDocumentTypeAndNotIdFetchingFileAndAttachments(
-            manualProgressEntry.getProcedureId(),
-            manualProgressEntry.getKeyDocumentType(),
-            manualProgressEntry.getId())
+            progressEntry.getProcedureId(),
+            keyDocumentAware.getKeyDocumentType(),
+            progressEntry.getId())
         .stream()
         .map(this::mapAndEnrichWithFileDetails)
-        .filter(ManualProgressEntryDto.class::isInstance)
-        .map(ManualProgressEntryDto.class::cast)
+        .filter(KeyDocumentAwareProgressEntryDto.class::isInstance)
+        .map(KeyDocumentAwareProgressEntryDto.class::cast)
         .toList();
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
index b3873ca78..0e35407d6 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/progressentry/ProgressEntryService.java
@@ -6,21 +6,27 @@
 package de.eshg.lib.procedure.progressentry;
 
 import static de.eshg.lib.procedure.MapperHelper.mapAndSet;
+import static de.eshg.lib.procedure.file.MultipartFileParser.parseFile;
 
+import de.eshg.file.common.PdfAConformanceValidator;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.foureyes.domain.repository.GenericApprovalRequestRepository;
 import de.eshg.lib.procedure.audit.AuditService;
 import de.eshg.lib.procedure.domain.model.File;
+import de.eshg.lib.procedure.domain.model.KeyDocumentAware;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntry;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequest;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryDeletionApprovalRequestNotification;
 import de.eshg.lib.procedure.domain.model.ManualProgressEntryType;
+import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.repository.ManualProgressEntryRepository;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.lib.procedure.domain.repository.ProgressEntryRepository;
-import de.eshg.lib.procedure.file.FileUploadService;
+import de.eshg.lib.procedure.file.MultipartFileParser;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.mapping.ProgressEntryMapper;
 import de.eshg.lib.procedure.model.FileMetaDataDto;
@@ -45,11 +51,15 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 import org.apache.commons.lang3.exception.UncheckedException;
 import org.openapitools.jackson.nullable.JsonNullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
 import org.springframework.web.multipart.MultipartFile;
 
 @Service
@@ -61,7 +71,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
   private final ProgressEntryRepository progressEntryRepository;
   private final ManualProgressEntryRepository manualProgressEntryRepository;
   private final GenericApprovalRequestRepository approvalRequestRepository;
-  private final FileUploadService fileUploadService;
   private final CemeteryService cemeteryService;
   private final AuditService auditService;
   private final UserHelper userHelper;
@@ -72,7 +81,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
       ProgressEntryRepository progressEntryRepository,
       ManualProgressEntryRepository manualProgressEntryRepository,
       GenericApprovalRequestRepository approvalRequestRepository,
-      FileUploadService fileUploadService,
       CemeteryService cemeteryService,
       AuditService auditService,
       UserHelper userHelper,
@@ -82,7 +90,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     this.progressEntryRepository = progressEntryRepository;
     this.manualProgressEntryRepository = manualProgressEntryRepository;
     this.approvalRequestRepository = approvalRequestRepository;
-    this.fileUploadService = fileUploadService;
     this.cemeteryService = cemeteryService;
     this.auditService = auditService;
     this.userHelper = userHelper;
@@ -95,6 +102,41 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .orElseThrow(() -> new NotFoundException("Procedure not found"));
   }
 
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry) {
+    return addSystemProgressEntry(procedure, systemProgressEntry, (File) null);
+  }
+
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry, MultipartFile file) throws IOException {
+    return addSystemProgressEntry(procedure, systemProgressEntry, parseFile(file));
+  }
+
+  @Transactional(propagation = Propagation.MANDATORY)
+  public SystemProgressEntry addSystemProgressEntry(
+      P procedure, SystemProgressEntry systemProgressEntry, File file) {
+    validateProcedureIsOpen(procedure, IllegalArgumentException::new);
+    if (file != null) {
+      validateFile(file);
+    } else {
+      validateKeyDocumentTypesAreUnset(systemProgressEntry);
+    }
+
+    procedure.addProgressEntry(systemProgressEntry);
+
+    if (file != null) {
+      systemProgressEntry.setKeyDocumentVersion(
+          getKeyDocumentVersion(procedure, systemProgressEntry));
+      systemProgressEntry.setFile(file);
+
+      auditLogFileAdd(procedure.getExternalId(), systemProgressEntry);
+    }
+
+    return systemProgressEntry;
+  }
+
   public ManualProgressEntry addManualProgressEntry(
       UUID procedureId,
       ManualProgressEntry manualProgressEntry,
@@ -109,7 +151,18 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     resolvedProcedure.addProgressEntry(manualProgressEntry);
 
     if (Optional.ofNullable(file).isPresent()) {
-      fileUploadService.handleFile(manualProgressEntry, file, fileMetaData);
+      ProcedureFileType fileType = MultipartFileParser.parseProcedureFileType(file);
+
+      MultipartFileParser.validateProgressEntryTypeSupportsFileType(manualProgressEntry, fileType);
+      validateKeyDocumentsUniformFileTypes(manualProgressEntry, fileType);
+
+      File parsedFile = parseFile(file);
+      Optional.ofNullable(fileMetaData)
+          .map(FileMetaDataDto::getDescription)
+          .ifPresent(parsedFile.getMetaData()::setDescription);
+      parsedFile.updateDeletable(true);
+      manualProgressEntry.setFile(parsedFile);
+
       Integer keyDocumentVersion = getKeyDocumentVersion(resolvedProcedure, manualProgressEntry);
       manualProgressEntry.setKeyDocumentVersion(keyDocumentVersion);
     } else if (Optional.ofNullable(manualProgressEntry.getKeyDocumentType()).isPresent()) {
@@ -117,7 +170,7 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
 
     manualProgressEntryRepository.flush();
-    auditLogFileUpload(manualProgressEntry);
+    auditLogFileUpload(procedureId, manualProgressEntry);
 
     return manualProgressEntry;
   }
@@ -151,8 +204,8 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
   }
 
-  private void auditLogFileUpload(ManualProgressEntry savedManualProgressEntry) {
-    File uploadedFile = savedManualProgressEntry.getFile();
+  private void auditLogFileUpload(UUID procedureId, ProgressEntry progressEntry) {
+    File uploadedFile = progressEntry.getFile();
     if (uploadedFile != null) {
       auditLogger.log(
           "Dokumentenmanagement",
@@ -161,9 +214,9 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
               "durch Benutzer",
               CurrentUserHelper.getCurrentUserIdAsStringGracefully().orElse("-"),
               "ID Vorgang",
-              savedManualProgressEntry.getProcedureId().toString(),
+              procedureId.toString(),
               "ID Verlaufseintrag",
-              savedManualProgressEntry.getExternalId().toString(),
+              progressEntry.getExternalId().toString(),
               "ID Datei",
               uploadedFile.getExternalId().toString(),
               "Dateityp",
@@ -171,15 +224,34 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
     }
   }
 
-  private Integer getKeyDocumentVersion(
-      P resolvedProcedure, ManualProgressEntry manualProgressEntry) {
-    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
+  private void auditLogFileAdd(UUID procedureId, ProgressEntry progressEntry) {
+    File uploadedFile = progressEntry.getFile();
+    if (uploadedFile != null) {
+      auditLogger.log(
+          "Dokumentenmanagement",
+          "Hinzufügen Datei",
+          Map.of(
+              "durch Benutzer",
+              CurrentUserHelper.getCurrentUserIdAsStringGracefully().orElse("-"),
+              "ID Vorgang",
+              procedureId.toString(),
+              "ID Verlaufseintrag",
+              progressEntry.getExternalId().toString(),
+              "ID Datei",
+              uploadedFile.getExternalId().toString(),
+              "Dateityp",
+              uploadedFile.getFileType().toString()));
+    }
+  }
+
+  private Integer getKeyDocumentVersion(P resolvedProcedure, KeyDocumentAware keyDocumentAware) {
+    String keyDocumentType = keyDocumentAware.getKeyDocumentType();
 
     if (keyDocumentType == null) {
       return null;
     }
 
-    return manualProgressEntryRepository.countByProcedureIdAndKeyDocumentType(
+    return progressEntryRepository.countByProcedureIdAndKeyDocumentType(
         resolvedProcedure.getId(), keyDocumentType);
   }
 
@@ -195,7 +267,7 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
   }
 
   private void removeProgressEntry(P procedure, ManualProgressEntry progressEntry) {
-    validateProcedureIsOpen(procedure);
+    validateProcedureIsOpen(procedure, BadRequestException::new);
     validateProgressEntryNotLocked(progressEntry);
     auditLogProgressEntryDeletion(progressEntry.getExternalId());
 
@@ -236,8 +308,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .ifPresent(
             mapAndSet(
                 ProgressEntryMapper::toDomainType, progressEntry::setManualProgressEntryType));
-    patchManualProgressEntryRequest.subject().ifPresent(progressEntry::setSubject);
-    patchManualProgressEntryRequest.messageText().ifPresent(progressEntry::setMessageText);
     patchManualProgressEntryRequest.note().ifPresent(progressEntry::setNote);
 
     manualProgressEntryRepository.flush();
@@ -262,20 +332,6 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
       }
     }
 
-    JsonNullable<String> messageTextJsonNullable = patchManualProgressEntryRequest.messageText();
-    if (messageTextJsonNullable.isPresent()) {
-      if (!Objects.equals(messageTextJsonNullable.get(), progressEntry.getMessageText())) {
-        updatedFields.add("Inhalt");
-      }
-    }
-
-    JsonNullable<String> subjectJsonNullable = patchManualProgressEntryRequest.subject();
-    if (subjectJsonNullable.isPresent()) {
-      if (!Objects.equals(subjectJsonNullable.get(), progressEntry.getSubject())) {
-        updatedFields.add("Betreff");
-      }
-    }
-
     JsonNullable<String> noteJsonNullable = patchManualProgressEntryRequest.note();
     if (noteJsonNullable.isPresent()) {
       if (!Objects.equals(noteJsonNullable.get(), progressEntry.getNote())) {
@@ -403,22 +459,56 @@ public class ProgressEntryService<P extends Procedure<P, ?, ?, ?>> {
         .orElseThrow(() -> new NotFoundException("ProgressEntry does not exist for Procedure"));
   }
 
+  private void validateFile(File file) {
+    if (file instanceof Pdf) {
+      PdfAConformanceValidator.validate(
+          file.getFileContent().getContent(), IllegalArgumentException::new);
+    }
+  }
+
   private void validateProgressEntryNotLocked(ManualProgressEntry progressEntry) {
     if (progressEntry.isLocked()) {
       throw new BadRequestException("Progress Entry is locked");
     }
   }
 
-  private void validateProcedureIsOpen(P procedure) {
+  private void validateProcedureIsOpen(
+      P procedure, Function<String, RuntimeException> exceptionConstructor) {
     if (!procedure.getProcedureStatus().isOpen()) {
-      throw new BadRequestException(
+      throw exceptionConstructor.apply(
           "Procedure " + procedure.getExternalId() + " is already closed.");
     }
   }
 
+  private void validateKeyDocumentsUniformFileTypes(
+      ManualProgressEntry manualProgressEntry, ProcedureFileType fileType) {
+    String keyDocumentType = manualProgressEntry.getKeyDocumentType();
+
+    if (keyDocumentType == null) {
+      return;
+    }
+
+    if (manualProgressEntryRepository.existsByProcedureIdAndKeyDocumentTypeAndFileFileTypeNot(
+        manualProgressEntry.getProcedureId(), keyDocumentType, fileType)) {
+      throw new BadRequestException(
+          "Key document type `%s` does not support file type `%s`."
+              .formatted(keyDocumentType, fileType));
+    }
+  }
+
+  private void validateKeyDocumentTypesAreUnset(SystemProgressEntry systemProgressEntry) {
+    Assert.isNull(
+        systemProgressEntry.getKeyDocumentType(),
+        "Key document type can only be set when file is present");
+
+    Assert.isNull(
+        systemProgressEntry.getKeyDocumentVersion(),
+        "Key document version can only be set when file is present");
+  }
+
   private P getOpenProcedureOrThrow(UUID procedureId) {
     P procedure = getProcedureOrThrow(procedureId);
-    validateProcedureIsOpen(procedure);
+    validateProcedureIsOpen(procedure, BadRequestException::new);
     return procedure;
   }
 
diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
index da04dde58..1a14158de 100644
--- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
+++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/spring/ProcedureLibraryAutoConfiguration.java
@@ -9,15 +9,15 @@ import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import de.eshg.lib.procedure.audit.AuditService;
 import de.eshg.lib.procedure.domain.serialization.SerializationService;
-import de.eshg.lib.procedure.file.FileAwareResolver;
 import de.eshg.lib.procedure.file.FileController;
 import de.eshg.lib.procedure.file.FileDeletionApprovalRequestNotificationService;
 import de.eshg.lib.procedure.file.FileDeletionRequestApprovalRequestDecisionHandler;
 import de.eshg.lib.procedure.file.FileStorageService;
-import de.eshg.lib.procedure.file.FileUploadService;
-import de.eshg.lib.procedure.file.FileUploadValidator;
+import de.eshg.lib.procedure.gdpr.GdprValidationTaskController;
+import de.eshg.lib.procedure.gdpr.GdprValidationTaskService;
 import de.eshg.lib.procedure.helper.UserHelper;
 import de.eshg.lib.procedure.housekeeping.archiving.ArchivingConfiguration;
+import de.eshg.lib.procedure.housekeeping.cemetery.CemeteryHousekeepingConfiguration;
 import de.eshg.lib.procedure.housekeeping.inbox.InboxProcedureCleanupJob;
 import de.eshg.lib.procedure.inbox.InboxProcedureController;
 import de.eshg.lib.procedure.mapping.ProcedureApprovalRequestMapper;
@@ -81,10 +81,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
   FileDeletionRequestApprovalRequestDecisionHandler.class,
   FileDeletionApprovalRequestNotificationService.class,
   InboxProcedureController.class,
-  FileUploadService.class,
-  FileUploadValidator.class,
   FileStorageService.class,
-  FileAwareResolver.class,
   ProcedureController.class,
   ProcedureLibraryEnrichingMapper.class,
   TaskController.class,
@@ -100,10 +97,13 @@ import org.springframework.scheduling.annotation.EnableScheduling;
   ProcedureDeletionService.class,
   CemeteryService.class,
   SerializationService.class,
+  CemeteryHousekeepingConfiguration.class,
   DefaultProcedureAsSearchableStringFormatter.class,
   ApprovalRequestMailJob.class,
   ApprovalRequestMailService.class,
-  ProcedureLibrarySchedulingConfig.class
+  ProcedureLibrarySchedulingConfig.class,
+  GdprValidationTaskController.class,
+  GdprValidationTaskService.class
 })
 public class ProcedureLibraryAutoConfiguration {
   @Bean
diff --git a/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties b/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
index 981ae0200..b03898f8a 100644
--- a/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
+++ b/backend/lib-procedures/src/main/resources/lib-procedure-test-helper.properties
@@ -1,3 +1,9 @@
 de.eshg.lib.procedure.mailreminder.schedule=-
 de.eshg.lib.procedure.housekeeping.inbox.schedule=-
 de.eshg.lib.procedure.housekeeping.archiving.schedule=-
+de.eshg.lib.procedure.housekeeping.cemetery.schedule=-
+
+de.eshg.lib.procedure.housekeeping.archiving.details[INSPECTION].years=1
+de.eshg.lib.procedure.housekeeping.archiving.details[INSPECTION].relevance=RELEVANT
+de.eshg.lib.procedure.housekeeping.archiving.details[REGULAR_EXAMINATION].years=5
+de.eshg.lib.procedure.housekeeping.archiving.details[REGULAR_EXAMINATION].relevance=IRRELEVANT
diff --git a/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java b/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
index 051b5bec3..888353759 100644
--- a/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
+++ b/backend/lib-procedures/src/testFixtures/java/de/eshg/lib/procedure/util/CemeteryTestUtils.java
@@ -13,6 +13,7 @@ import de.eshg.lib.procedure.domain.model.Cemetery;
 import de.eshg.lib.procedure.domain.repository.CemeteryRepository;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.util.List;
 import java.util.stream.Collectors;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -37,6 +38,11 @@ public class CemeteryTestUtils {
         .collect(Collectors.joining(",\n", "[\n", "\n]"));
   }
 
+  @Transactional(readOnly = true)
+  public List<Cemetery> cemeteryEntities() {
+    return cemeteryRepository.findAllByOrderById().toList();
+  }
+
   private String prettyJson(String source) {
     try (JsonParser parser = objectMapper.createParser(source)) {
       TreeNode result = parser.readValueAsTree();
diff --git a/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java b/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
index c556483c6..f853ce63f 100644
--- a/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
+++ b/backend/lib-security-config-urls/src/main/java/de/eshg/rest/service/security/config/BaseUrls.java
@@ -89,6 +89,7 @@ public final class BaseUrls {
     public static final String PACKLIST_CONTROLLER = "/packlists";
     public static final String PACKLIST_DEFINITION_CONTROLLER =
         PACKLIST_CONTROLLER + "/definitions";
+    public static final String INSPECTION_IMPORT_CONTROLLER = "/import";
 
     private Inspection() {}
   }
@@ -188,6 +189,7 @@ public final class BaseUrls {
     public static final String TASK_METRICS_API = "/task-metrics";
     public static final String ARCHIVING_API = "/archiving";
     public static final String TASKS_TEAM_VIEW = TASKS_API + "/team-view";
+    public static final String GDPR_VALIDATION_TASK_API = "/gdpr-validation-tasks";
 
     private ProcedureLibrary() {}
   }
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
index 33bb9b44e..4eca3bce8 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/AbstractPublicSecurityConfiguration.java
@@ -166,6 +166,10 @@ public abstract class AbstractPublicSecurityConfiguration {
 
     requestMatchers(GET, ProcedureLibrary.ARCHIVING_API + "/config")
         .hasRole(EmployeePermissionRole.PROCEDURE_ARCHIVE);
+    requestMatchers(GET, ProcedureLibrary.GDPR_VALIDATION_TASK_API + "/**")
+        .hasRole(procedureAccessRole);
+    requestMatchers(POST, ProcedureLibrary.GDPR_VALIDATION_TASK_API + "/**")
+        .hasRole(procedureAccessRole);
   }
 
   protected void grantAccessToStatistics() {
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
index c7f7fb898..52cbde4c3 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/BasePublicSecurityConfig.java
@@ -72,6 +72,10 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
   }
 
   private void gdpr() {
+    requestMatchers(GET, BaseUrls.Base.GDPR_PROCEDURE_API + "/*/fileStateIds")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
+    requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/*/downloads")
+        .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW);
     requestMatchers(GET, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
         .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_READ);
     requestMatchers(POST, BaseUrls.Base.GDPR_PROCEDURE_API + "/**")
@@ -239,8 +243,7 @@ public final class BasePublicSecurityConfig extends AbstractPublicSecurityConfig
   }
 
   private void features() {
-    requestMatchers(GET, BaseUrls.Base.FEATURE_TOGGLES_API + "/**")
-        .hasRole(EmployeePermissionRole.STANDARD_EMPLOYEE);
+    requestMatchers(GET, BaseUrls.Base.FEATURE_TOGGLES_API).permitAll();
   }
 
   private void config() {
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
index f2c7f52c5..0ec6a45b1 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/InspectionPublicSecurityConfig.java
@@ -30,6 +30,7 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
     editor();
     inspectionTestData();
     features();
+    importer();
 
     grantAccessToLibProceduresUrls(
         EmployeePermissionRole.INSPECTION_PROCEDURE_EDIT, ModuleLeaderRole.INSPECTION_LEADER);
@@ -101,4 +102,11 @@ public final class InspectionPublicSecurityConfig extends AbstractPublicSecurity
     requestMatchers(GET, BaseUrls.Inspection.FEATURE_TOGGLES_CONTROLLER + "/**")
         .hasRole(EmployeePermissionRole.STANDARD_EMPLOYEE);
   }
+
+  private void importer() {
+    requestMatchers(GET, Inspection.INSPECTION_IMPORT_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.INSPECTION_IMPORT);
+    requestMatchers(POST, Inspection.INSPECTION_IMPORT_CONTROLLER + "/**")
+        .hasRole(EmployeePermissionRole.INSPECTION_IMPORT);
+  }
 }
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
index 41fbf53f8..79a5fce87 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/SchoolEntryPublicSecurityConfig.java
@@ -26,6 +26,8 @@ public final class SchoolEntryPublicSecurityConfig extends AbstractPublicSecurit
     // school-entry endpoints?
     requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/documents/**")
         .permitAll();
+    requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/opening-hours")
+        .permitAll();
 
     requestMatchers(BaseUrls.SchoolEntry.SCHOOL_ENTRY_CITIZEN_CONTROLLER + "/**")
         .hasRole(CitizenPermissionRole.ACCESS_CODE_USER);
diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
index dd4dbb44d..86e144f72 100644
--- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
+++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StatisticsPublicSecurityConfig.java
@@ -12,6 +12,9 @@ import org.springframework.stereotype.Component;
 
 @Component
 public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurityConfiguration {
+
+  private static final String OVERVIEW_PATH = "/overview/**";
+
   StatisticsPublicSecurityConfig() {
     super("statistics");
 
@@ -49,8 +52,12 @@ public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurity
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
+    requestMatchers(POST, BaseUrls.Statistics.STATISTIC_CONTROLLER + OVERVIEW_PATH)
+        .hasAnyRole(
+            EmployeePermissionRole.STATISTICS_STATISTICS_READ,
+            EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
 
-    requestMatchers(POST, BaseUrls.Statistics.REPORT_SERIES_URL + "/overview/**")
+    requestMatchers(POST, BaseUrls.Statistics.REPORT_SERIES_URL + OVERVIEW_PATH)
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
@@ -110,7 +117,7 @@ public final class StatisticsPublicSecurityConfig extends AbstractPublicSecurity
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
-    requestMatchers(POST, BaseUrls.Statistics.EVALUATION_TEMPLATE_CONTROLLER + "/overview/**")
+    requestMatchers(POST, BaseUrls.Statistics.EVALUATION_TEMPLATE_CONTROLLER + OVERVIEW_PATH)
         .hasAnyRole(
             EmployeePermissionRole.STATISTICS_STATISTICS_READ,
             EmployeePermissionRole.STATISTICS_STATISTICS_WRITE);
diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
index 7bd20c6ce..8b2be7857 100644
--- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
+++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/AbstractStatisticsService.java
@@ -156,12 +156,12 @@ public abstract class AbstractStatisticsService<P extends Procedure<P, ?, ?, ?>>
             Sort.by(Sort.Direction.ASC, BaseEntity_.ID)));
   }
 
-  // probably only closed procedures are relevant
-  private Specification<P> getProcedureSpecification(Instant startTimeStamp, Instant endTimeStamp) {
+  protected Specification<P> getProcedureSpecification(
+      Instant startTimestamp, Instant endTimestamp) {
     return (root, query, criteriaBuilder) ->
         criteriaBuilder.and(
-            criteriaBuilder.greaterThanOrEqualTo(root.get(Procedure_.createdAt), startTimeStamp),
-            criteriaBuilder.lessThan(root.get(Procedure_.createdAt), endTimeStamp));
+            criteriaBuilder.greaterThanOrEqualTo(root.get(Procedure_.createdAt), startTimestamp),
+            criteriaBuilder.lessThan(root.get(Procedure_.createdAt), endTimestamp));
   }
 
   private DataRow createDataRow(
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java
new file mode 100644
index 000000000..e15e7700a
--- /dev/null
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ErrorHandler.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.lib.xlsximport;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+@FunctionalInterface
+public interface ErrorHandler {
+  void handleError(Cell cell, String errorMessage);
+}
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
index 9e215b143..a4b70f631 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportStatus.java
@@ -18,6 +18,7 @@ public enum ImportStatus {
   INVALID_PROCEDURE_ID("Ungültige Vorgangs-ID"),
   DUPLICATE_WITHIN_LIST("Duplikat in der Liste"),
   DUPLICATE_IN_ASSET("Duplikat im Bestand"),
+  BATCH_ERROR("Ignoriert (Fehler in Gruppe)"),
   EXCEPTION("Unbekannter Fehler"),
   ;
 
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
index c1e45d7fa..cc001d5b0 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/ImportValidator.java
@@ -13,10 +13,14 @@ import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.usermodel.XSSFCellStyle;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.web.multipart.MultipartFile;
 
 public class ImportValidator {
 
+  private static final Logger log = LoggerFactory.getLogger(ImportValidator.class);
+
   private static final String INVALID_FILE_STRUCTURE =
       "Invalid file structure. Headers do not match the expected structure.";
 
@@ -54,7 +58,12 @@ public class ImportValidator {
         // skip the header
         continue;
       }
-      if (rowHasMoreColumnsThanHeader(row, headerRowNumberOfColumns)) {
+      if (row.getLastCellNum() > headerRowNumberOfColumns) {
+        log.error(
+            "row {} has more data columns than header columns: {} > {}",
+            row.getRowNum(),
+            row.getLastCellNum(),
+            headerRowNumberOfColumns);
         throw new BadRequestException(
             ErrorCode.INVALID_FILE,
             "Invalid file structure. Number of columns in header and data rows does not match.");
@@ -62,10 +71,6 @@ public class ImportValidator {
     }
   }
 
-  private static boolean rowHasMoreColumnsThanHeader(Row dataRow, int headerRowNumberOfColumns) {
-    return dataRow.getLastCellNum() > headerRowNumberOfColumns;
-  }
-
   public static <T extends XlsxColumn> List<T> validateHeaderFormat(
       T[] expectedColumns, XSSFSheet sheet) {
     Row headerRow = sheet.getRow(0);
@@ -105,6 +110,7 @@ public class ImportValidator {
     } else if (column.isOptional()) {
       return false;
     } else {
+      log.error("missing required column \"{}\"", column.getHeader());
       throw new BadRequestException(ErrorCode.INVALID_FILE, INVALID_FILE_STRUCTURE);
     }
   }
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
index be88c9286..debe0e55c 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/Importer.java
@@ -119,6 +119,10 @@ public abstract class Importer<T extends RowValues, C extends XlsxColumn> {
         // skip the header
         continue;
       }
+      if (RowReader.isEmpty(row)) {
+        // sometimes a row has no cells but still shows up
+        continue;
+      }
       deleteReferenceId(row);
       try {
         rowValues.put(row, rowReader.readRow(row));
@@ -158,7 +162,8 @@ public abstract class Importer<T extends RowValues, C extends XlsxColumn> {
   private XSSFCellStyle getCellStyle(ImportStatus importStatus) {
     return switch (importStatus) {
       case IMPORTED_SUCCESSFULLY, MERGED_SUCCESSFULLY -> importedSuccessfullyCellStyle;
-      case ERROR_INPUT_DATA, INVALID_PROCEDURE_ID, EXCEPTION, MERGE_FAILED -> importFailedCellStyle;
+      case ERROR_INPUT_DATA, INVALID_PROCEDURE_ID, EXCEPTION, MERGE_FAILED, BATCH_ERROR ->
+          importFailedCellStyle;
       case IMPORTED_PREVIOUSLY, DUPLICATE_WITHIN_LIST, DUPLICATE_IN_ASSET -> importWarningCellStyle;
     };
   }
diff --git a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
index 9e7009434..62483cfd0 100644
--- a/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
+++ b/backend/lib-xlsx-import/src/main/java/de/eshg/lib/xlsximport/RowReader.java
@@ -11,11 +11,11 @@ import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.lib.xlsximport.util.XlsxUtil;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.UUID;
-import java.util.function.BiConsumer;
 import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.*;
 import org.springframework.util.StringUtils;
@@ -28,7 +28,6 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   private final CellStyle errorCellStyle;
   private final Drawing<?> drawing;
   private final CreationHelper factory;
-  private final ClientAnchor anchor;
 
   protected RowReader(Sheet sheet, List<C> actualColumns) {
     Workbook workbook = sheet.getWorkbook();
@@ -37,7 +36,6 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     errorCellStyle = createErrorStyle(workbook);
     drawing = sheet.createDrawingPatriarch();
     factory = workbook.getCreationHelper();
-    anchor = factory.createClientAnchor();
   }
 
   public T readRow(Row row) {
@@ -50,8 +48,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
 
   protected abstract T read(ColumnAccessor<C> col);
 
-  protected ImportStatus readStatus(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected ImportStatus readStatus(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String status = cellAsString(cell, true, false, errorHandler);
     if (!StringUtils.hasLength(status)) {
@@ -61,13 +58,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return ImportStatus.map(status);
     } catch (NoSuchElementException e) {
-      errorHandler.accept(cell, "Ungültiger Wert");
+      errorHandler.handleError(cell, "Ungültiger Wert");
       return null;
     }
   }
 
-  protected UUID readProcedureId(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected UUID readProcedureId(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String uuid = cellAsString(cell, true, false, errorHandler);
     if (!StringUtils.hasLength(uuid)) {
@@ -77,12 +73,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return UUID.fromString(uuid);
     } catch (IllegalArgumentException e) {
-      errorHandler.accept(cell, "Ungültiges Format");
+      errorHandler.handleError(cell, "Ungültiges Format");
       return null;
     }
   }
 
-  protected BiConsumer<Cell, String> createErrorHandler(RowValues result) {
+  protected ErrorHandler createErrorHandler(RowValues result) {
     return (cell, errorMessage) -> {
       result.foundInvalidData();
       addCellError(cell, errorMessage);
@@ -91,7 +87,9 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
 
   protected void addCellError(Cell cell, String errorMessage) {
     cell.setCellStyle(errorCellStyle);
-    cell.setCellComment(createComment(errorMessage));
+    if (cell.getCellComment() == null) {
+      cell.setCellComment(createComment(cell, errorMessage));
+    }
   }
 
   private CellStyle createErrorStyle(Workbook workbook) {
@@ -102,19 +100,27 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     return errorStyle;
   }
 
-  private Comment createComment(String errorMessage) {
-    Comment comment = drawing.createCellComment(anchor);
+  private Comment createComment(Cell cell, String errorMessage) {
+    Comment comment = drawing.createCellComment(createClientAnchor(cell));
     comment.setString(factory.createRichTextString(errorMessage));
 
     return comment;
   }
 
-  protected String cellAsString(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  private ClientAnchor createClientAnchor(Cell cell) {
+    ClientAnchor clientAnchor = factory.createClientAnchor();
+    clientAnchor.setCol1(cell.getColumnIndex());
+    clientAnchor.setRow1(cell.getRowIndex());
+    clientAnchor.setCol2(cell.getColumnIndex());
+    clientAnchor.setRow2(cell.getRowIndex());
+    return clientAnchor;
+  }
+
+  protected String cellAsString(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     return cellAsString(col.get(column), errorHandler);
   }
 
-  protected static String cellAsString(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  protected static String cellAsString(Cell cell, ErrorHandler errorHandler) {
     return cellAsString(cell, false, false, errorHandler);
   }
 
@@ -123,15 +129,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       C column,
       boolean optional,
       boolean allowCellTypeNumeric,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     return cellAsString(col.get(column), optional, allowCellTypeNumeric, errorHandler);
   }
 
   private static String cellAsString(
-      Cell cell,
-      boolean optional,
-      boolean allowCellTypeNumeric,
-      BiConsumer<Cell, String> errorHandler) {
+      Cell cell, boolean optional, boolean allowCellTypeNumeric, ErrorHandler errorHandler) {
     List<CellType> expectedTypes =
         allowCellTypeNumeric
             ? List.of(CellType.STRING, CellType.NUMERIC)
@@ -148,20 +151,18 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     }
   }
 
-  protected boolean cellAsFlag(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected boolean cellAsFlag(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     return !isOptionalBlank(cell, true)
         && !invalidType(cell, CellType.STRING, errorHandler)
         && !invalidFlag(cell, errorHandler);
   }
 
-  protected boolean cellAsBoolean(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected boolean cellAsBoolean(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String booleanString = cellAsString(cell, false, false, errorHandler);
     if (booleanString == null) {
-      errorHandler.accept(
+      errorHandler.handleError(
           cell, "Ungültiger Wert (Erwartet: Ja, Nein, Tatsächlich: %s)".formatted(booleanString));
       return false;
     }
@@ -170,7 +171,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "JA" -> true;
       case "NEIN" -> false;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell, "Ungültiger Wert (Erwartet: Ja, Nein, Tatsächlich: %s)".formatted(booleanString));
         yield false;
       }
@@ -178,7 +179,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected Boolean cellAsBooleanOrNull(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String booleanString = cellAsString(cell, true, false, errorHandler);
     if (booleanString == null) {
@@ -189,7 +190,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "JA" -> true;
       case "NEIN" -> false;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: Ja, Nein oder leere Zelle. Tatsächlich: %s)"
                 .formatted(booleanString));
@@ -198,18 +199,30 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     };
   }
 
-  protected Integer cellAsInt(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected Integer cellAsInt(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     if (invalidType(cell, CellType.NUMERIC, errorHandler)) {
-      errorHandler.accept(cell, "Ungültiger Wert");
+      errorHandler.handleError(cell, "Ungültiger Wert");
       return null;
     }
     return (int) cell.getNumericCellValue();
   }
 
+  protected Double cellAsDouble(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    if (invalidType(cell, CellType.NUMERIC, errorHandler)) {
+      errorHandler.handleError(cell, "Ungültiger Wert");
+      return null;
+    }
+    return cell.getNumericCellValue();
+  }
+
   private static boolean isOptionalBlank(Cell cell, boolean optional) {
-    return optional && (cell == null || getNormalizedCellType(cell) == CellType.BLANK);
+    return optional && isBlank(cell);
+  }
+
+  private static boolean isBlank(Cell cell) {
+    return cell == null || getNormalizedCellType(cell) == CellType.BLANK;
   }
 
   private static CellType getNormalizedCellType(Cell cell) {
@@ -219,17 +232,30 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     return cell.getCellType();
   }
 
-  protected LocalDate cellAsDate(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  public static boolean isEmpty(Row row) {
+    for (Cell cell : row) {
+      if (!isBlank(cell)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  protected LocalDate cellAsDate(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
+    LocalDateTime localDateTime = cellAsDateTime(col, column, errorHandler);
+    return localDateTime != null ? localDateTime.toLocalDate() : null;
+  }
+
+  protected LocalDateTime cellAsDateTime(
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     if (invalidType(cell, CellType.NUMERIC, errorHandler) || invalidDate(cell, errorHandler)) {
       return null;
     }
-    return cell.getLocalDateTimeCellValue().toLocalDate();
+    return cell.getLocalDateTimeCellValue();
   }
 
-  protected GenderDto cellAsGender(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+  protected GenderDto cellAsGender(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String gender = cellAsString(cell, true, false, errorHandler);
     if (gender == null) {
@@ -242,7 +268,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "D" -> GenderDto.DIVERSE;
       case "U", "" -> GenderDto.NOT_SPECIFIED;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: M = Männlich, W = Weiblich, D = Divers, U = Unbekannt, Tatsächlich: %s)"
                 .formatted(gender));
@@ -252,7 +278,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected SalutationDto cellAsSalutation(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String salutation = cellAsString(cell, true, false, errorHandler);
     if (salutation == null) {
@@ -265,7 +291,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
       case "Neutral" -> SalutationDto.NEUTRAL;
       case "Unbekannt", "" -> SalutationDto.NOT_SPECIFIED;
       default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell,
             "Ungültiger Wert (Erwartet: Herr, Frau, Neutral, Unbekannt, Tatsächlich: %s)"
                 .formatted(salutation));
@@ -275,7 +301,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   }
 
   protected CountryCode cellAsCountryCode(
-      ColumnAccessor<C> col, C column, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, C column, ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     String countryCode = cellAsString(cell, true, false, errorHandler);
     if (countryCode == null) {
@@ -284,7 +310,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     try {
       return CountryCode.valueOf(countryCode);
     } catch (IllegalArgumentException exception) {
-      errorHandler.accept(
+      errorHandler.handleError(
           cell,
           "Ungültiger Wert (Erwartet: Länderkürzel nach ISO 3166-1, Tatsächlich: %s)"
               .formatted(countryCode));
@@ -292,13 +318,12 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
     }
   }
 
-  private static boolean invalidType(
-      Cell cell, CellType expectedType, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidType(Cell cell, CellType expectedType, ErrorHandler errorHandler) {
     return invalidType(cell, List.of(expectedType), errorHandler);
   }
 
   private static boolean invalidType(
-      Cell cell, List<CellType> expectedTypes, BiConsumer<Cell, String> errorHandler) {
+      Cell cell, List<CellType> expectedTypes, ErrorHandler errorHandler) {
     CellType actualType = getNormalizedCellType(cell);
     if (!expectedTypes.contains(actualType)) {
       String expectedType =
@@ -307,42 +332,39 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
               String.join(",", expectedTypes.stream().map(Enum::name).toList()));
       String errorMessage =
           "Ungültiger Wert (Erwartet: %s, Tatsächlich: %s)".formatted(expectedType, actualType);
-      errorHandler.accept(cell, errorMessage);
+      errorHandler.handleError(cell, errorMessage);
 
       return true;
     }
     return false;
   }
 
-  private static boolean invalidDate(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidDate(Cell cell, ErrorHandler errorHandler) {
     if (cell.getLocalDateTimeCellValue() == null) {
-      errorHandler.accept(cell, "Ungültiges Datum");
+      errorHandler.handleError(cell, "Ungültiges Datum");
       return true;
     }
     return false;
   }
 
-  private static boolean invalidFlag(Cell cell, BiConsumer<Cell, String> errorHandler) {
+  private static boolean invalidFlag(Cell cell, ErrorHandler errorHandler) {
     String cellValue = cellAsString(cell, errorHandler);
     if (cellValue != null
         && !cellValue.isBlank()
         && !Objects.equals(cellValue.toLowerCase(), "x")) {
-      errorHandler.accept(cell, "Ungültige Eingabe. Nur 'x' oder 'X' sind erlaubt.");
+      errorHandler.handleError(cell, "Ungültige Eingabe. Nur 'x' oder 'X' sind erlaubt.");
       return true;
     }
     return false;
   }
 
   private boolean anyValueInRange(
-      ColumnAccessor<C> col,
-      AddressColumns<C> addressColumns,
-      BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<C> col, AddressColumns<C> addressColumns, ErrorHandler errorHandler) {
     return anyValueInRange(
         col.getRange(addressColumns.street(), addressColumns.addressAddition()), errorHandler);
   }
 
-  protected static boolean anyValueInRange(
-      Stream<Cell> range, BiConsumer<Cell, String> errorHandler) {
+  protected static boolean anyValueInRange(Stream<Cell> range, ErrorHandler errorHandler) {
     return range
         .map(cell -> cellAsString(cell, true, false, errorHandler))
         .anyMatch(org.apache.commons.lang3.StringUtils::isNotBlank);
@@ -351,7 +373,7 @@ public abstract class RowReader<T extends RowValues, C extends XlsxColumn> {
   protected AddressData readAddressData(
       ColumnAccessor<C> col,
       AddressColumns<C> addressColumns,
-      BiConsumer<Cell, String> errorHandler,
+      ErrorHandler errorHandler,
       boolean mandatoryAddress) {
 
     if (anyValueInRange(col, addressColumns, errorHandler) || mandatoryAddress) {
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
index b8c03bc56..1053c367b 100644
--- a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/MultipartUtil.java
@@ -15,6 +15,7 @@ import jakarta.mail.internet.MimeMultipart;
 import jakarta.mail.util.ByteArrayDataSource;
 import java.nio.file.Path;
 import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.FileSystemResource;
 import org.springframework.core.io.Resource;
 import org.springframework.http.ResponseEntity;
@@ -26,8 +27,16 @@ public class MultipartUtil {
   private MultipartUtil() {}
 
   public static MultiValueMap<String, Object> toMultipartFormDataRequest(Path filePath) {
+    return toMultipartFormDataRequest(new FileSystemResource(filePath));
+  }
+
+  public static MultiValueMap<String, Object> toMultipartFormDataRequest(String classPathResource) {
+    return toMultipartFormDataRequest(new ClassPathResource(classPathResource));
+  }
+
+  public static MultiValueMap<String, Object> toMultipartFormDataRequest(Resource resource) {
     MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
-    body.add("file", new FileSystemResource(filePath));
+    body.add("file", resource);
     return body;
   }
 
diff --git a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
index 77b627743..90aa6acaf 100644
--- a/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
+++ b/backend/lib-xlsx-import/src/testFixtures/java/de/eshg/lib/xlsximport/XlsxAssertionTraits.java
@@ -9,14 +9,18 @@ import de.cronn.assertions.validationfile.junit5.JUnit5ValidationFileAssertions;
 import de.cronn.assertions.validationfile.normalization.ValidationNormalizer;
 import de.eshg.normalization.UuidNormalizer;
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.function.Consumer;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellType;
 import org.apache.poi.ss.usermodel.DateUtil;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.core.io.Resource;
 
@@ -50,12 +54,23 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
       StringBuilder sb = new StringBuilder();
       int sheetCounter = 1;
       int rowCounter = 1;
+      int numberOfColumns = 0;
       for (Sheet sheet : workbook) {
         append(sb, "-- sheet %d --".formatted(sheetCounter++));
-        for (Row row : sheet) {
+
+        append(sb, "-- row %d --".formatted(rowCounter++));
+        Iterator<Row> rowIterator = sheet.rowIterator();
+        Row headerRow = rowIterator.next();
+        for (Cell cell : headerRow) {
+          numberOfColumns++;
+          append(sb, getCellProperties(cell));
+        }
+
+        while (rowIterator.hasNext()) {
+          Row row = rowIterator.next();
           append(sb, "-- row %d --".formatted(rowCounter++));
-          for (Cell cell : row) {
-            append(sb, getCellProperties(cell));
+          for (int columnCounter = 0; columnCounter < numberOfColumns; columnCounter++) {
+            append(sb, getCellProperties(row.getCell(columnCounter)));
           }
         }
       }
@@ -63,12 +78,25 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     }
   }
 
+  default void withXlsxSheet(ImportResponse response, Consumer<Sheet> consumer) throws IOException {
+    byte[] content = response.file().getContentAsByteArray();
+    Files.write(getTempFile(".xlsx"), content);
+    try (InputStream inputStream = new ByteArrayInputStream(content);
+        XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+      XSSFSheet sheet = workbook.getSheetAt(0);
+      consumer.accept(sheet);
+    }
+  }
+
   private static void append(StringBuilder sb, String value) {
     sb.append(value);
     sb.append(System.lineSeparator());
   }
 
   private static String getCellProperties(Cell cell) {
+    if (cell == null) {
+      return "NULL;;";
+    }
     String cellType = cell.getCellType().toString();
     String cellValue = getCellValue(cell);
     String cellComment = getCellComment(cell);
@@ -76,7 +104,7 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     return "%s;%s;%s".formatted(cellType, cellValue, cellComment);
   }
 
-  private static String getCellValue(Cell cell) {
+  static String getCellValue(Cell cell) {
     if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
       return cell.getLocalDateTimeCellValue().toLocalDate().toString();
     } else if (cell.getCellType() == CellType.NUMERIC) {
@@ -86,7 +114,7 @@ public interface XlsxAssertionTraits extends JUnit5ValidationFileAssertions {
     }
   }
 
-  private static String getCellComment(Cell cell) {
+  static String getCellComment(Cell cell) {
     if (cell.getCellComment() != null) {
       return cell.getCellComment().getString().getString();
     }
diff --git a/backend/local-service-directory/build.gradle b/backend/local-service-directory/build.gradle
index 5548095a9..d0d10a32b 100644
--- a/backend/local-service-directory/build.gradle
+++ b/backend/local-service-directory/build.gradle
@@ -10,8 +10,7 @@ dependencies {
 
     implementation 'org.springframework.retry:spring-retry'
 
-    implementation libs.bundles.keycloak.client
-    compileOnly libs.bundles.keycloak.server
+    implementation libs.keycloak.client.admin.client
     implementation 'io.github.java-diff-utils:java-diff-utils:latest.release'
 
     testImplementation project(':lib-service-directory-admin-api')
diff --git a/backend/local-service-directory/gradle.lockfile b/backend/local-service-directory/gradle.lockfile
index d5c75a492..e7b22539e 100644
--- a/backend/local-service-directory/gradle.lockfile
+++ b/backend/local-service-directory/gradle.lockfile
@@ -3,7 +3,6 @@
 # This file is expected to be part of source control.
 ch.qos.logback:logback-classic:1.5.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 ch.qos.logback:logback-core:1.5.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.apicatalog:titanium-json-ld:1.3.3=compileClasspath
 com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -22,25 +21,16 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.ua-parser:uap-java:1.5.4=compileClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-com.google.zxing:core:3.4.0=compileClasspath
-com.google.zxing:javase:3.4.0=compileClasspath
 com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,testRuntimeClasspath
 com.googlecode.libphonenumber:libphonenumber:8.13.46=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.owasp-java-html-sanitizer:java10-shim:20240325.1=compileClasspath
-com.googlecode.owasp-java-html-sanitizer:java8-shim:20240325.1=compileClasspath
-com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1=compileClasspath
 com.ibm.async:asyncutil:0.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -54,11 +44,8 @@ com.tngtech.archunit:archunit-junit5-engine:1.3.0=testRuntimeClasspath
 com.tngtech.archunit:archunit-junit5:1.3.0=testRuntimeClasspath
 com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
-com.webauthn4j:webauthn4j-core:0.21.5.RELEASE=compileClasspath
-com.webauthn4j:webauthn4j-util:0.21.5.RELEASE=compileClasspath
 commons-codec:commons-codec:1.16.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-logging:commons-logging:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
 de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -76,8 +63,6 @@ io.prometheus:prometheus-metrics-exposition-formats:1.2.1=productionRuntimeClass
 io.prometheus:prometheus-metrics-model:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-shaded-protobuf:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.prometheus:prometheus-metrics-tracer-common:1.2.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-io.setl:rdf-urdna:1.1=compileClasspath
-io.smallrye.common:smallrye-common-annotation:2.2.0=compileClasspath
 io.swagger.core.v3:swagger-annotations-jakarta:2.2.25=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-annotations:2.2.25=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 io.swagger.core.v3:swagger-core-jakarta:2.2.25=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -85,12 +70,9 @@ io.swagger.core.v3:swagger-models-jakarta:2.2.25=productionRuntimeClasspath,runt
 jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.mail:jakarta.mail-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath
 jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.ws.rs:jakarta.ws.rs-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-jakarta.xml.soap:jakarta.xml.soap-api:3.0.2=compileClasspath
-javax.annotation:javax.annotation-api:1.3.2=compileClasspath
 junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy:1.14.19=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -100,7 +82,6 @@ net.logstash.logback:logstash-logback-encoder:8.0=productionRuntimeClasspath,run
 net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
 net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath
 net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath
-org.apache.commons:commons-collections4:4.4=compileClasspath
 org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath
 org.apache.commons:commons-lang3:3.14.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents.client5:httpclient5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -108,9 +89,9 @@ org.apache.httpcomponents.core5:httpcore5-h2:5.2.5=productionRuntimeClasspath,ru
 org.apache.httpcomponents.core5:httpcore5:5.2.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.31=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -142,15 +123,16 @@ org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt
 org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-api:5.10.5=testCompileClasspath,testRuntimeClasspath
 org.junit.jupiter:junit-jupiter-engine:5.10.5=testRuntimeClasspath
@@ -160,14 +142,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-model-storage-private:25.0.6=compileClasspath
-org.keycloak:keycloak-model-storage:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi-private:25.0.6=compileClasspath
-org.keycloak:keycloak-server-spi:25.0.6=compileClasspath
-org.keycloak:keycloak-services:25.0.6=compileClasspath
+org.keycloak:keycloak-admin-client:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -228,7 +204,6 @@ org.testcontainers:database-commons:1.19.8=testRuntimeClasspath
 org.testcontainers:jdbc:1.19.8=testRuntimeClasspath
 org.testcontainers:postgresql:1.19.8=testRuntimeClasspath
 org.testcontainers:testcontainers:1.19.8=testCompileClasspath,testRuntimeClasspath
-org.twitter4j:twitter4j-core:4.1.2=compileClasspath
 org.webjars:swagger-ui:5.17.14=testCompileClasspath,testRuntimeClasspath
 org.xmlunit:xmlunit-core:2.9.1=testCompileClasspath,testRuntimeClasspath
 org.yaml:snakeyaml:2.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
index 90d1cef42..1a084916a 100644
--- a/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
+++ b/backend/local-service-directory/src/main/java/de/eshg/lsd/keycloak/KeycloakProvisioning.java
@@ -35,7 +35,6 @@ import org.keycloak.representations.userprofile.config.UPAttribute;
 import org.keycloak.representations.userprofile.config.UPAttributePermissions;
 import org.keycloak.representations.userprofile.config.UPConfig;
 import org.keycloak.representations.userprofile.config.UPGroup;
-import org.keycloak.validate.validators.LengthValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
@@ -247,16 +246,16 @@ public class KeycloakProvisioning {
   private Map<String, Map<String, Object>> getCertificateValidatorConfig() {
     Map<String, Object> config =
         Map.of(
-            LengthValidator.KEY_MIN,
+            "min",
             "0",
             // we want to increase the limit as certificates are too long
-            LengthValidator.KEY_MAX,
+            "max",
             certificateAttributeMaxCharacters,
             // we don't want values be trimmed before checking the length
-            LengthValidator.KEY_TRIM_DISABLED,
+            "trim-disabled",
             true);
 
-    return Map.of(LengthValidator.ID, config);
+    return Map.of("length", config);
   }
 
   private String getPasswordPolicy() {
diff --git a/backend/measles-protection/openApi.yaml b/backend/measles-protection/openApi.yaml
index aa4d71fbc..94a929c4a 100644
--- a/backend/measles-protection/openApi.yaml
+++ b/backend/measles-protection/openApi.yaml
@@ -558,6 +558,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -2660,6 +2675,17 @@ components:
       required:
       - facility
       - id
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AddPersonFileStateRequest:
       type: object
       description: Request used for adding persons from non-external sources
@@ -3580,12 +3606,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMonetaryFine:
@@ -4086,6 +4108,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -4510,7 +4538,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -4778,6 +4808,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     MPFacilityType:
       type: string
       description: Type of the facility.
@@ -4844,13 +4888,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -4884,13 +4934,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5060,15 +5107,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -5674,6 +5715,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -5685,6 +5731,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
index f3f1cf76d..9e3fa2379 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/AccessRestrictionService.java
@@ -162,8 +162,6 @@ public class AccessRestrictionService {
     CreateManualProgressEntryRequest createManualProgressEntryRequest =
         new CreateManualProgressEntryRequest(
             ManualProgressEntryTypeDto.LETTER,
-            "Betretungsverbot",
-            null,
             "Ein Anschreiben über ein Betretungsverbot wurde %s.".formatted(producedHow),
             null);
 
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
index ce18073de..327adc7ea 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/OrganisationPortalController.java
@@ -15,8 +15,11 @@ import org.springframework.core.io.Resource;
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.ResponseEntity;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping(value = OrganisationPortalController.BASE_URL)
@@ -30,8 +33,8 @@ public class OrganisationPortalController {
 
   public OrganisationPortalController(
       OrganisationPortalService publicMeaslesProtectionService,
-      @Value("classpath:templates/documents/privacy_notice.pdf") Resource privacyNotice,
-      @Value("classpath:templates/documents/privacy_policy.pdf") Resource privacyPolicy) {
+      @Value("${de.eshg.measles-protection.privacy-notice-location}") Resource privacyNotice,
+      @Value("${de.eshg.measles-protection.privacy-policy-location}") Resource privacyPolicy) {
     this.publicMeaslesProtectionService = publicMeaslesProtectionService;
     this.privacyNotice = privacyNotice;
     this.privacyPolicy = privacyPolicy;
@@ -47,14 +50,12 @@ public class OrganisationPortalController {
 
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
-  @Transactional(readOnly = true)
   public ResponseEntity<Resource> getPrivacyNotice() {
     return getPrivacyDocument(privacyNotice);
   }
 
   @GetMapping(path = "/documents/privacy-policy")
   @Operation(summary = "Get the privacy-policy document.")
-  @Transactional(readOnly = true)
   public ResponseEntity<Resource> getPrivacyPolicy() {
     return getPrivacyDocument(privacyPolicy);
   }
diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
index 0a11771d8..24356a120 100644
--- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
+++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/pdf/coverletter/CoverLetterService.java
@@ -8,7 +8,6 @@ package de.eshg.measlesprotection.pdf.coverletter;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import java.io.ByteArrayOutputStream;
 import java.time.Clock;
@@ -38,8 +37,7 @@ public class CoverLetterService {
     byte[] bytes = createPdfFromTemplate(data);
     ZonedDateTime now = ZonedDateTime.now(clock);
     String name = name(data);
-    return FileFactory.createPdfWithMetaData(
-        filename(name, now), ProcedureFileType.PDF, bytes, pdfMetaData(now, name), false);
+    return FileFactory.createPdfWithMetaData(filename(name, now), bytes, pdfMetaData(now, name));
   }
 
   private static String name(CoverLetterData data) {
diff --git a/backend/measles-protection/src/main/resources/application.properties b/backend/measles-protection/src/main/resources/application.properties
index c75014b4b..7bb422ec0 100644
--- a/backend/measles-protection/src/main/resources/application.properties
+++ b/backend/measles-protection/src/main/resources/application.properties
@@ -16,3 +16,5 @@ spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.i
 
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[PROOF_SUBMISSION]=10m
 eshg.population.measles-protection-procedure=10
+de.eshg.measles-protection.privacy-notice-location=classpath:templates/documents/privacy_notice.pdf
+de.eshg.measles-protection.privacy-policy-location=classpath:templates/documents/privacy_policy.pdf
diff --git a/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml b/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml
new file mode 100644
index 000000000..2ef4593b8
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0028_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml b/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml
new file mode 100644
index 000000000..1b1ac8a9f
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0029_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml b/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 000000000..609846864
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0030_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml b/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml
new file mode 100644
index 000000000..fc254c7a0
--- /dev/null
+++ b/backend/measles-protection/src/main/resources/migrations/0031_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/migrations/changelog.xml b/backend/measles-protection/src/main/resources/migrations/changelog.xml
index db5a823ef..e1206ff33 100644
--- a/backend/measles-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/measles-protection/src/main/resources/migrations/changelog.xml
@@ -35,5 +35,9 @@
   <include file="migrations/0025_medical_registry_procedure_types.xml"/>
   <include file="migrations/0026_remove_key_document_type_enum.xml"/>
   <include file="migrations/0027_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0028_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0029_cemetery_sequence.xml"/>
+  <include file="migrations/0030_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0031_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx b/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
index 32ca04d4e..d77d18125 100644
--- a/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
+++ b/backend/measles-protection/src/main/resources/templates/coverletters/end-of-letter.ftlx
@@ -10,6 +10,6 @@
   </p>
   <p>Mit freundlichen Grüßen</p>
   <p>Im Auftrag</p>
-  <p>Gesundheitsamt der Stadt Frankfurt am Main</p>
+  <p>${departmentInfo.name()}</p>
   <p>Dieser Bescheid wurde maschinell erstellt und ist ohne Unterschrift gültig.</p>
 </div>
diff --git a/backend/medical-registry/build.gradle b/backend/medical-registry/build.gradle
index 1944e896f..3ed37f86c 100644
--- a/backend/medical-registry/build.gradle
+++ b/backend/medical-registry/build.gradle
@@ -6,6 +6,7 @@ dependencies {
     implementation project(':lib-base-client')
     implementation project(':lib-procedures')
     implementation project(':business-module-persistence-commons')
+    implementation project(':file-commons')
 
     implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
 
diff --git a/backend/medical-registry/openApi.yaml b/backend/medical-registry/openApi.yaml
index 211bf3e46..480a66218 100644
--- a/backend/medical-registry/openApi.yaml
+++ b/backend/medical-registry/openApi.yaml
@@ -387,6 +387,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -550,6 +565,102 @@ paths:
       summary: Update status of inbox procedure
       tags:
       - InboxProcedure
+  /medical-registry-entries:
+    post:
+      operationId: createProcedure
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                employeeList:
+                  type: string
+                  format: binary
+                identificationDocument:
+                  type: string
+                  format: binary
+                otherRelevantDocuments:
+                  type: string
+                  format: binary
+                procedure:
+                  $ref: "#/components/schemas/CreateProcedureRequest"
+                professionalLicenseCertificate:
+                  type: string
+                  format: binary
+                workPermit:
+                  type: string
+                  format: binary
+              required:
+              - identificationDocument
+              - procedure
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: string
+                format: uuid
+          description: OK
+      tags:
+      - MedicalRegistry
+  /medical-registry-entries/procedures:
+    get:
+      operationId: getProcedureOverview
+      parameters:
+      - description: Limit of returned procedures
+        in: query
+        name: pageSize
+        required: false
+        schema:
+          type: integer
+          format: int32
+          default: 50
+          maximum: 200
+          minimum: 1
+      - description: Offset used for pagination
+        in: query
+        name: pageNumber
+        required: false
+        schema:
+          type: integer
+          format: int32
+          default: 0
+          maximum: 2000
+          minimum: 0
+      - description: |
+          Filter logic:
+          - In case of `true` only procedures are returned where a certificate was requested.
+          - In case of `false` only procedures are returned where no certificate was requested
+          - If not submitted, no filtering takes place
+        in: query
+        name: certificateRequested
+        required: false
+        schema:
+          type: boolean
+      - description: |
+          Filter logic:
+          - If `procedureStatus` is submitted, only procedures are returned which have one of the submitted statuses.
+          - If not submitted, no filtering takes place
+        in: query
+        name: procedureStatus
+        required: false
+        schema:
+          type: array
+          items:
+            $ref: "#/components/schemas/ProcedureStatus"
+          uniqueItems: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetMedicalRegistryEntryOverview"
+          description: OK
+      summary: Get paginated and optionally filtered medical registry procedures.
+        Filtering is optional
+      tags:
+      - MedicalRegistry
   /medical-registry-entries/{procedureId}:
     delete:
       operationId: deleteProcedure
@@ -586,7 +697,9 @@ paths:
           content:
             '*/*':
               schema:
-                $ref: "#/components/schemas/GetMedicalRegistryProcedureResponse"
+                oneOf:
+                - $ref: "#/components/schemas/GetProcedureConfirmedResponse"
+                - $ref: "#/components/schemas/GetProcedureDraftResponse"
           description: OK
       summary: Get medical registry procedure by id.
       tags:
@@ -1402,6 +1515,21 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/medical-registry-entries/{procedureId}/open:
+    post:
+      operationId: openProcedure
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/population:
     post:
       operationId: populateDefaults
@@ -1599,6 +1727,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2138,14 +2277,31 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
+    CreateProcedureRequest:
+      type: object
+      properties:
+        consentToPrivacyPolicy:
+          type: boolean
+        employeesEmployed:
+          type: boolean
+        practice:
+          $ref: "#/components/schemas/Practice"
+        professional:
+          $ref: "#/components/schemas/Professional"
+        requestForWrittenConfirmation:
+          type: boolean
+        typeOfChange:
+          $ref: "#/components/schemas/TypeOfChange"
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - professional
+      - requestForWrittenConfirmation
+      - typeOfChange
     DataOrigin:
       type: string
       description: "A list of possible origins of Persons and Facility in the Central\
@@ -2409,6 +2565,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -2574,34 +2736,27 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/ManualProgressEntryHistory"
-    GetMedicalRegistryProcedureResponse:
+    GetMedicalRegistryEntryOverview:
       type: object
       properties:
-        consentToPrivacyPolicy:
-          type: boolean
-        employeesEmployed:
-          type: boolean
-        id:
-          type: string
-          format: uuid
-        practices:
+        medicalRegistryEntries:
           type: array
           items:
-            $ref: "#/components/schemas/Practice"
-        professional:
-          $ref: "#/components/schemas/Professional"
-        requestForWrittenConfirmation:
-          type: boolean
-        version:
+            $ref: "#/components/schemas/MedicalRegistryEntry"
+          maxItems: 200
+          minItems: 0
+        totalElements:
           type: integer
           format: int64
+          description: Total number of result elements for the given filter criteria
+        totalPages:
+          type: integer
+          format: int32
+          description: Total number of result pages for the given filter criteria
       required:
-      - consentToPrivacyPolicy
-      - employeesEmployed
-      - id
-      - professional
-      - requestForWrittenConfirmation
-      - version
+      - medicalRegistryEntries
+      - totalElements
+      - totalPages
     GetMetaDataHistoryResponse:
       type: object
       properties:
@@ -2626,6 +2781,71 @@ components:
       required:
       - approvalRequests
       - resolvedUsers
+    GetProcedureConfirmedResponse:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/GetProcedureResponse"
+      - type: object
+        properties:
+          consentToPrivacyPolicy:
+            type: boolean
+          employeesEmployed:
+            type: boolean
+          id:
+            type: string
+            format: uuid
+          practices:
+            type: array
+            items:
+              $ref: "#/components/schemas/Practice"
+          professional:
+            $ref: "#/components/schemas/Professional"
+          requestForWrittenConfirmation:
+            type: boolean
+          version:
+            type: integer
+            format: int64
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - id
+      - professional
+      - requestForWrittenConfirmation
+      - version
+    GetProcedureDraftResponse:
+      type: object
+      allOf:
+      - $ref: "#/components/schemas/GetProcedureResponse"
+      - type: object
+        properties:
+          consentToPrivacyPolicy:
+            type: boolean
+          employeesEmployed:
+            type: boolean
+          id:
+            type: string
+            format: uuid
+          practices:
+            type: array
+            items:
+              $ref: "#/components/schemas/Practice"
+          professional:
+            $ref: "#/components/schemas/Professional"
+          requestForWrittenConfirmation:
+            type: boolean
+          typeOfChange:
+            $ref: "#/components/schemas/TypeOfChange"
+          version:
+            type: integer
+            format: int64
+      required:
+      - consentToPrivacyPolicy
+      - employeesEmployed
+      - id
+      - professional
+      - requestForWrittenConfirmation
+      - typeOfChange
+      - version
     GetProcedureFileDetailsResponse:
       type: object
       properties:
@@ -2648,6 +2868,15 @@ components:
             $ref: "#/components/schemas/ProcedureMetric"
       required:
       - procedureMetrics
+    GetProcedureResponse:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+      required:
+      - '@type'
     GetProceduresResponse:
       type: object
       properties:
@@ -2716,7 +2945,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -2975,6 +3206,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3020,13 +3265,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3060,13 +3311,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3097,22 +3345,36 @@ components:
       - EMAIL
       - IMAGE
       - DOCUMENT
-    MedicalRegistryAddress:
+    MedicalRegistryEntry:
       type: object
       properties:
-        city:
+        address:
+          $ref: "#/components/schemas/ProfessionalAddress"
+        certificateRequested:
+          type: boolean
+        dateOfBirth:
           type: string
-        houseNumber:
+          format: date
+        firstName:
           type: string
-        postalCode:
+        id:
           type: string
-        street:
+          format: uuid
+        lastName:
           type: string
+        status:
+          $ref: "#/components/schemas/ProcedureStatus"
+        type:
+          $ref: "#/components/schemas/ProcedureType"
       required:
-      - city
-      - houseNumber
-      - postalCode
-      - street
+      - address
+      - certificateRequested
+      - dateOfBirth
+      - firstName
+      - id
+      - lastName
+      - status
+      - type
     MetaData:
       type: object
       discriminator:
@@ -3150,15 +3412,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -3356,17 +3612,17 @@ components:
       type: object
       properties:
         address:
-          $ref: "#/components/schemas/MedicalRegistryAddress"
+          $ref: "#/components/schemas/PracticeAddress"
         emailAddress:
           type: string
-          maxLength: 254
-          minLength: 6
         establishmentNumber:
           type: string
+          pattern: \d+
         healthInsuranceAuthorization:
           type: boolean
         institutionIdentifier:
           type: string
+          pattern: \d+
         name:
           type: string
           maxLength: 300
@@ -3382,8 +3638,35 @@ components:
           maxLength: 254
           minLength: 6
       required:
+      - address
+      - emailAddress
       - healthInsuranceAuthorization
       - name
+      - phoneNumber
+    PracticeAddress:
+      type: object
+      properties:
+        city:
+          type: string
+          maxLength: 50
+          minLength: 1
+        houseNumber:
+          type: string
+          maxLength: 11
+          minLength: 1
+        postalCode:
+          type: string
+          maxLength: 20
+          minLength: 1
+        street:
+          type: string
+          maxLength: 55
+          minLength: 1
+      required:
+      - city
+      - houseNumber
+      - postalCode
+      - street
     Procedure:
       type: object
       properties:
@@ -3529,7 +3812,7 @@ components:
       type: object
       properties:
         address:
-          $ref: "#/components/schemas/MedicalRegistryAddress"
+          $ref: "#/components/schemas/ProfessionalAddress"
         approbationGrantedOn:
           type: string
           format: date
@@ -3540,8 +3823,6 @@ components:
           format: date
         emailAddress:
           type: string
-          maxLength: 254
-          minLength: 6
         employmentStatus:
           $ref: "#/components/schemas/EmploymentStatus"
         employmentType:
@@ -3562,6 +3843,7 @@ components:
           minLength: 1
         lifetimeDoctorNumber:
           type: string
+          pattern: "\\d{9}"
         nameAtBirth:
           type: string
           maxLength: 40
@@ -3587,15 +3869,48 @@ components:
           maxLength: 119
           minLength: 1
       required:
+      - address
       - approbationGrantedOn
       - approbationIssuingAuthority
       - dateOfBirth
+      - emailAddress
       - employmentStatus
       - employmentType
       - firstName
+      - gender
       - lastName
+      - nameAtBirth
       - nationality
+      - phoneNumber
+      - placeOfBirth
       - professionalTitle
+    ProfessionalAddress:
+      type: object
+      properties:
+        city:
+          type: string
+          maxLength: 50
+          minLength: 1
+        country:
+          $ref: "#/components/schemas/CountryCode"
+        houseNumber:
+          type: string
+          maxLength: 11
+          minLength: 1
+        postalCode:
+          type: string
+          maxLength: 20
+          minLength: 1
+        street:
+          type: string
+          maxLength: 55
+          minLength: 1
+      required:
+      - city
+      - country
+      - houseNumber
+      - postalCode
+      - street
     ProfessionalTitle:
       type: string
       enum:
@@ -3721,6 +4036,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -3732,6 +4052,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -3917,6 +4238,17 @@ components:
       - SYSTEM_AUTOMATIC
       - EMPLOYEE
       - CITIZEN
+    TypeOfChange:
+      type: string
+      enum:
+      - NEW_REGISTRATION
+      - SECOND_PRACTICE
+      - RE_REGISTRATION
+      - CHANGE_OF_REGISTRATION
+      - CHANGE_OF_NAME
+      - RELOCATION
+      - DEREGISTRATION
+      - OTHER
     User:
       type: object
       properties:
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
index 6ee29f77a..20d986a02 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryController.java
@@ -5,23 +5,45 @@
 
 package de.eshg.medicalregistry;
 
+import static de.eshg.medicalregistry.mapper.ProcedureMapper.*;
+
 import de.cronn.commons.lang.StreamUtil;
+import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
-import de.eshg.medicalregistry.api.*;
+import de.eshg.file.common.FileType;
+import de.eshg.lib.procedure.model.GetProceduresPaginationOptions;
+import de.eshg.medicalregistry.api.CreateProcedureRequest;
+import de.eshg.medicalregistry.api.DeleteProcedureRequest;
+import de.eshg.medicalregistry.api.GetMedicalRegistryEntryOverview;
+import de.eshg.medicalregistry.api.GetMedicalRegistryProceduresFilterOptions;
+import de.eshg.medicalregistry.api.GetProcedureResponse;
+import de.eshg.medicalregistry.business.model.DocumentData;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
-import de.eshg.medicalregistry.domain.model.Practice;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntryChange;
 import de.eshg.medicalregistry.domain.model.Professional;
-import de.eshg.medicalregistry.mapper.PracticeMapper;
-import de.eshg.medicalregistry.mapper.ProfessionalMapper;
 import de.eshg.rest.service.security.config.BaseUrls;
-import io.swagger.v3.oas.annotations.Hidden;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.http.MediaType;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 
 @RestController
 @RequestMapping(MedicalRegistryController.BASE_URL)
@@ -36,12 +58,43 @@ public class MedicalRegistryController {
     this.medicalRegistryService = medicalRegistryService;
   }
 
-  @PostMapping
+  @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
   @Transactional
-  @Hidden // TODO currently for testing purposes only
-  public UUID createProcedure(@Valid @RequestBody CreateProcedureRequest procedure) {
-    MedicalRegistryEntry persistedProcedure = medicalRegistryService.createProcedure(procedure);
-    return persistedProcedure.getExternalId();
+  public UUID createProcedure(
+      @RequestPart(name = "procedure") @Valid CreateProcedureRequest request,
+      @RequestPart(name = "professionalLicenseCertificate", required = false)
+          MultipartFile professionalLicenseCertificate,
+      @RequestPart(name = "identificationDocument") MultipartFile identificationDocument,
+      @RequestPart(name = "workPermit", required = false) MultipartFile workPermit,
+      @RequestPart(name = "employeeList", required = false) MultipartFile employeeList,
+      @RequestPart(name = "otherRelevantDocuments", required = false)
+          MultipartFile otherRelevantDocuments)
+      throws IOException {
+
+    List<DocumentData> providedDocuments =
+        Stream.of(
+                new DocumentData(
+                    "Berufserlaubnisurkunde",
+                    "Upload Berufserlaubnisurkunde",
+                    professionalLicenseCertificate),
+                new DocumentData("Ausweis_Pass", "Upload Ausweis/Pass", identificationDocument),
+                new DocumentData("Arbeitserlaubnis", "Upload Arbeitserlaubnis", workPermit),
+                new DocumentData("Mitarbeiter_Liste", "Upload Mitarbeiter-Liste", employeeList),
+                new DocumentData(
+                    "Weitere_relevante_Dokumente",
+                    "Upload weiterer relevanter Dokumente",
+                    otherRelevantDocuments))
+            .filter(d -> d.getFile() != null)
+            .toList();
+
+    for (DocumentData document : providedDocuments) {
+      Validator.validateFileType(document.getFile(), FileType.JPEG);
+    }
+
+    MedicalRegistryEntryChange procedure =
+        medicalRegistryService.createProcedure(request, providedDocuments);
+
+    return procedure.getExternalId();
   }
 
   @GetMapping("/{procedureId}")
@@ -56,17 +109,12 @@ public class MedicalRegistryController {
     GetPersonFileStateResponse professionalDetails =
         medicalRegistryService.findProfessionalDetails(professional.getCentralFileStateId());
 
-    List<PracticeDto> practices =
-        medicalRegistryEntry.getRelatedFacilities().stream().map(this::mapToDto).toList();
-
-    return new GetProcedureResponse(
-        medicalRegistryEntry.getExternalId(),
-        medicalRegistryEntry.getVersion(),
-        ProfessionalMapper.mapToDto(professional, professionalDetails),
-        practices,
-        medicalRegistryEntry.isEmployeesEmployed(),
-        medicalRegistryEntry.isConsentToPrivacyPolicy(),
-        medicalRegistryEntry.isRequestForWrittenConfirmation());
+    Map<UUID, GetFacilityFileStateResponse> practiceDetails =
+        medicalRegistryEntry.getRelatedFacilities().stream()
+            .map(f -> medicalRegistryService.findPracticeDetails(f.getCentralFileStateId()))
+            .collect(Collectors.toMap(GetFacilityFileStateResponse::id, facility -> facility));
+
+    return mapToDto(medicalRegistryEntry, professionalDetails, practiceDetails);
   }
 
   @DeleteMapping("/{procedureId}")
@@ -83,8 +131,17 @@ public class MedicalRegistryController {
     medicalRegistryService.deleteProcedure(medicalRegistryEntry);
   }
 
-  private PracticeDto mapToDto(Practice practice) {
-    return PracticeMapper.mapToDto(
-        practice, medicalRegistryService.findPracticeDetails(practice.getCentralFileStateId()));
+  @GetMapping("/procedures")
+  @Transactional(readOnly = true)
+  @Operation(
+      summary =
+          "Get paginated and optionally filtered medical registry procedures. Filtering is optional")
+  public GetMedicalRegistryEntryOverview getProcedureOverview(
+      @Valid @ParameterObject @InlineParameterObject
+          GetProceduresPaginationOptions paginationOptions,
+      @Valid @ParameterObject @InlineParameterObject
+          GetMedicalRegistryProceduresFilterOptions filterOptions) {
+
+    return medicalRegistryService.getProceduresOverview(paginationOptions, filterOptions);
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
index 42a6362ec..a6425c893 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryService.java
@@ -5,38 +5,77 @@
 
 package de.eshg.medicalregistry;
 
-import static de.eshg.medicalregistry.mapper.ProfessionalMapper.*;
+import static de.eshg.lib.procedure.MapperHelper.mapEnumSet;
+import static de.eshg.lib.procedure.domain.model.Procedure_.CREATED_AT;
+import static de.eshg.lib.procedure.domain.model.Procedure_.PROCEDURE_STATUS;
+import static de.eshg.lib.procedure.domain.model.Procedure_.procedureStatus;
+import static de.eshg.medicalregistry.domain.model.MedicalRegistryEntry_.requestForWrittenConfirmation;
+import static de.eshg.medicalregistry.mapper.ProcedureMapper.mapToDomain;
+import static de.eshg.medicalregistry.mapper.ProfessionalMapper.mapToDomain;
+import static org.springframework.data.domain.PageRequest.ofSize;
+import static org.springframework.data.jpa.domain.Specification.allOf;
+import static org.springframework.data.jpa.domain.Specification.where;
 
 import de.cronn.commons.lang.StreamUtil;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.FacilityApi;
 import de.eshg.base.centralfile.PersonApi;
-import de.eshg.base.centralfile.api.DataOriginDto;
 import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
-import de.eshg.base.centralfile.api.facility.AddFacilityFileStateRequest;
 import de.eshg.base.centralfile.api.facility.AddFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.facility.ExternalAddFacilityFileStateRequest;
+import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto;
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
-import de.eshg.base.centralfile.api.person.AddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
+import de.eshg.base.centralfile.api.person.ExternalAddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.util.SetUtils;
 import de.eshg.lib.auditlog.AuditLogger;
 import de.eshg.lib.common.CountryCode;
+import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
+import de.eshg.lib.procedure.domain.model.File;
+import de.eshg.lib.procedure.domain.model.ImageMetaData;
+import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.TriggerType;
+import de.eshg.lib.procedure.file.FileFactory;
+import de.eshg.lib.procedure.mapping.ProcedureMapper;
+import de.eshg.lib.procedure.model.GetProceduresPaginationOptions;
 import de.eshg.lib.procedure.procedures.ProcedureDeletionService;
-import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.api.CreateProcedureRequest;
+import de.eshg.medicalregistry.api.GetMedicalRegistryEntryOverview;
+import de.eshg.medicalregistry.api.GetMedicalRegistryProceduresFilterOptions;
+import de.eshg.medicalregistry.api.MedicalRegistryEntryDto;
+import de.eshg.medicalregistry.api.PracticeAddressDto;
+import de.eshg.medicalregistry.api.PracticeDto;
+import de.eshg.medicalregistry.api.ProfessionalAddressDto;
+import de.eshg.medicalregistry.api.ProfessionalDto;
+import de.eshg.medicalregistry.business.model.DocumentData;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntryChange;
 import de.eshg.medicalregistry.domain.model.Practice;
 import de.eshg.medicalregistry.domain.model.Professional;
 import de.eshg.medicalregistry.domain.registry.MedicalRegistryEntryRepository;
+import de.eshg.medicalregistry.mapper.EntryMapper;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.validation.ValidationUtil;
+import java.io.IOException;
 import java.time.Clock;
-import java.util.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -97,21 +136,38 @@ public class MedicalRegistryService {
     return facilityApi.getFacilityFileState(externalId);
   }
 
-  public MedicalRegistryEntry createProcedure(CreateProcedureRequest request) {
-    CreateProcedureDto procedure = request.procedure();
-    ProfessionalDto professional = procedure.professional();
-    PracticeDto practice = procedure.practice();
-
-    MedicalRegistryEntry medicalRegistryEntry = new MedicalRegistryEntry();
-    medicalRegistryEntry.setConsentToPrivacyPolicy(procedure.consentToPrivacyPolicy());
-    medicalRegistryEntry.setEmployeesEmployed(procedure.employeesEmployed());
-    medicalRegistryEntry.setRequestForWrittenConfirmation(
-        procedure.requestForWrittenConfirmation());
+  public MedicalRegistryEntryChange createProcedure(
+      CreateProcedureRequest request, List<DocumentData> documents) throws IOException {
+    MedicalRegistryEntryChange medicalRegistryEntry = new MedicalRegistryEntryChange();
+    medicalRegistryEntry.setTypeOfChange(mapToDomain(request.typeOfChange()));
+    medicalRegistryEntry.setConsentToPrivacyPolicy(request.consentToPrivacyPolicy());
+    medicalRegistryEntry.setEmployeesEmployed(request.employeesEmployed());
+    medicalRegistryEntry.setRequestForWrittenConfirmation(request.requestForWrittenConfirmation());
+    medicalRegistryEntry.setProcedureType(ProcedureType.MEDICAL_REGISTRY_EMPLOYEE_DRAFT);
+    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger);
 
+    ProfessionalDto professional = request.professional();
     UUID personId = createPersonInCentralFile(professional);
+    medicalRegistryEntry.addRelatedPerson(buildProfessional(professional, personId));
+
+    PracticeDto practice = request.practice();
+    if (practice != null) {
+      UUID facilityId = createFacilityInCentralFile(practice, professional);
+      medicalRegistryEntry.addRelatedFacility(buildPractice(practice, facilityId));
+    }
 
+    addSystemProgressEntry(medicalRegistryEntry);
+
+    for (DocumentData document : documents) {
+      addSystemProgressEntryFile(medicalRegistryEntry, document);
+    }
+
+    return medicalRegistryEntryRepository.save(medicalRegistryEntry);
+  }
+
+  private Professional buildProfessional(ProfessionalDto professional, UUID centralFilePersonId) {
     Professional professionalEntity = new Professional();
-    professionalEntity.setCentralFileStateId(personId);
+    professionalEntity.setCentralFileStateId(centralFilePersonId);
     professionalEntity.setProfessionalTitle(mapToDomain(professional.professionalTitle()));
     professionalEntity.setFieldOfExpertise(professional.fieldOfExpertise());
     professionalEntity.setSpecialistTitle(professional.specialistTitle());
@@ -123,31 +179,26 @@ public class MedicalRegistryService {
     professionalEntity.setEmploymentType(mapToDomain(professional.employmentType()));
     professionalEntity.setEmploymentStatus(mapToDomain(professional.employmentStatus()));
     professionalEntity.setNationality(professional.nationality());
-    medicalRegistryEntry.addRelatedPerson(professionalEntity);
 
-    if (practice != null) {
-      UUID facilityExternalId = createFacilityInCentralFile(practice);
-
-      Practice practiceEntity = new Practice();
-      practiceEntity.setCentralFileStateId(facilityExternalId);
-      practiceEntity.setWebsite(practice.website());
-      practiceEntity.setInstitutionIdentifier(practice.institutionIdentifier());
-      practiceEntity.setEstablishmentNumber(practice.establishmentNumber());
-      practiceEntity.setHealthInsuranceAuthorization(practice.healthInsuranceAuthorization());
-      practiceEntity.setOpeningHours(practice.openingHours());
-      medicalRegistryEntry.addRelatedFacility(practiceEntity);
-    }
+    return professionalEntity;
+  }
 
-    medicalRegistryEntry.setProcedureType(ProcedureType.MEDICAL_REGISTRY_CITIZEN_DRAFT);
-    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger);
-    return medicalRegistryEntryRepository.save(medicalRegistryEntry);
+  private static Practice buildPractice(PracticeDto practice, UUID centralFileFacilityId) {
+    Practice practiceEntity = new Practice();
+    practiceEntity.setCentralFileStateId(centralFileFacilityId);
+    practiceEntity.setWebsite(practice.website());
+    practiceEntity.setInstitutionIdentifier(practice.institutionIdentifier());
+    practiceEntity.setEstablishmentNumber(practice.establishmentNumber());
+    practiceEntity.setHealthInsuranceAuthorization(practice.healthInsuranceAuthorization());
+    practiceEntity.setOpeningHours(practice.openingHours());
+
+    return practiceEntity;
   }
 
   private UUID createPersonInCentralFile(ProfessionalDto professional) {
     AddPersonFileStateResponse addPersonResponse =
-        personApi.addPersonFileState(
-            new AddPersonFileStateRequest(
-                null,
+        personApi.addPersonFromExternalSource(
+            new ExternalAddPersonFileStateRequest(
                 professional.title(),
                 null,
                 professional.gender(),
@@ -160,34 +211,37 @@ public class MedicalRegistryService {
                 toList(professional.emailAddress()),
                 toList(professional.phoneNumber()),
                 mapAddress(professional.address()),
-                null,
-                DataOriginDto.MANUAL));
+                null));
 
     return addPersonResponse.id();
   }
 
-  private UUID createFacilityInCentralFile(PracticeDto practice) {
+  private UUID createFacilityInCentralFile(PracticeDto practice, ProfessionalDto professional) {
     AddFacilityFileStateResponse addFacilityResponse =
-        facilityApi.addFacilityFileState(
-            new AddFacilityFileStateRequest(
-                null,
+        facilityApi.addFacilityFromExternalSource(
+            new ExternalAddFacilityFileStateRequest(
                 practice.name(),
                 toList(practice.emailAddress()),
                 toList(practice.phoneNumber()),
-                null,
+                List.of(mapContactPerson(professional)),
                 mapAddress(practice.address()),
-                null,
-                DataOriginDto.MANUAL,
                 null));
 
     return addFacilityResponse.id();
   }
 
-  private static DomesticAddressDto mapAddress(AddressDto address) {
-    if (address == null) {
-      return null;
-    }
+  private static DomesticAddressDto mapAddress(ProfessionalAddressDto address) {
+    return new DomesticAddressDto(
+        address.country(),
+        address.city(),
+        address.postalCode(),
+        null,
+        address.street(),
+        address.houseNumber(),
+        null);
+  }
 
+  private static DomesticAddressDto mapAddress(PracticeAddressDto address) {
     return new DomesticAddressDto(
         CountryCode.DE,
         address.city(),
@@ -198,6 +252,18 @@ public class MedicalRegistryService {
         null);
   }
 
+  private FacilityContactPersonDto mapContactPerson(ProfessionalDto professional) {
+    return new FacilityContactPersonDto(
+        professional.emailAddress(),
+        professional.phoneNumber(),
+        null,
+        professional.lastName(),
+        professional.firstName(),
+        professional.title(),
+        null,
+        professional.gender());
+  }
+
   public void deleteProcedure(MedicalRegistryEntry medicalRegistryEntry) {
     UUID professionalId =
         medicalRegistryEntry.getRelatedPersons().stream()
@@ -223,7 +289,102 @@ public class MedicalRegistryService {
     procedureDeletionService.deleteAndWriteToCemetery(medicalRegistryEntry.getExternalId());
   }
 
+  private static void addSystemProgressEntry(MedicalRegistryEntryChange medicalRegistryEntry) {
+    ProgressEntry progressEntry =
+        SystemProgressEntryFactory.createSystemProgressEntry(
+            medicalRegistryEntry.getTypeOfChange().name(), TriggerType.SYSTEM_AUTOMATIC);
+
+    medicalRegistryEntry.addProgressEntry(progressEntry);
+  }
+
+  private void addSystemProgressEntryFile(
+      MedicalRegistryEntryChange procedure, DocumentData document) throws IOException {
+    String description = document.getDescription();
+    File file = buildJpeg(document);
+    ProgressEntry progressEntry =
+        SystemProgressEntryFactory.createSystemProgressEntry(
+            procedure.getTypeOfChange().name(), description, TriggerType.SYSTEM_AUTOMATIC);
+    progressEntry.setFile(file);
+
+    procedure.addProgressEntry(progressEntry);
+  }
+
+  private File buildJpeg(DocumentData document) throws IOException {
+    ImageMetaData metaData = new ImageMetaData();
+    metaData.setCreatedDate(Instant.now(clock));
+
+    return FileFactory.createImageWithMetaData(
+        document.getFileName(), ProcedureFileType.JPEG, document.getFile().getBytes(), metaData);
+  }
+
   private static List<String> toList(String value) {
     return value == null ? List.of() : List.of(value);
   }
+
+  public GetMedicalRegistryEntryOverview getProceduresOverview(
+      GetProceduresPaginationOptions paginationOptions,
+      GetMedicalRegistryProceduresFilterOptions filterOptions) {
+    List<Specification<MedicalRegistryEntry>> specifications = new ArrayList<>();
+
+    if (filterOptions.procedureStatus() != null) {
+      Set<ProcedureStatus> domainProcedureStatus =
+          mapEnumSet(filterOptions.procedureStatus(), ProcedureMapper::toDomainType);
+
+      specifications.add(statusIsIn(domainProcedureStatus));
+    }
+
+    if (filterOptions.certificateRequested() != null) {
+      specifications.add(filterByCertificateRequested(filterOptions.certificateRequested()));
+    }
+
+    Page<MedicalRegistryEntry> page =
+        medicalRegistryEntryRepository.findAll(
+            where(allOf(specifications)),
+            ofSize(paginationOptions.pageSize())
+                .withPage(paginationOptions.pageNumber())
+                .withSort(
+                    Sort.by(Sort.Order.asc(PROCEDURE_STATUS))
+                        .and(Sort.by(Sort.Order.desc(CREATED_AT)))));
+
+    if (page.isEmpty()) {
+      return new GetMedicalRegistryEntryOverview(
+          page.getTotalPages(), page.getTotalElements(), List.of());
+    }
+    List<UUID> relatedPersonIds = collectRelatedPersonIds(page);
+    Map<UUID, AddPersonFileStateResponse> resolvedRelatedPerson =
+        personApi
+            .getPersonFileStates(new GetPersonFileStatesRequest(relatedPersonIds))
+            .personFileStates()
+            .stream()
+            .collect(Collectors.toMap(AddPersonFileStateResponse::id, person -> person));
+
+    List<MedicalRegistryEntryDto> entryDtos =
+        page.stream().map(entry -> EntryMapper.mapToDto(entry, resolvedRelatedPerson)).toList();
+    return new GetMedicalRegistryEntryOverview(
+        page.getTotalPages(), page.getTotalElements(), entryDtos);
+  }
+
+  private static List<UUID> collectRelatedPersonIds(
+      Page<MedicalRegistryEntry> medicalRegistryEntries) {
+    return medicalRegistryEntries.stream()
+        .map(
+            entry ->
+                entry.getRelatedPersons().stream()
+                    .collect(StreamUtil.toSingleElement())
+                    .getCentralFileStateId())
+        .toList();
+  }
+
+  private Specification<MedicalRegistryEntry> filterByCertificateRequested(
+      Boolean certificateRequested) {
+    return (root, query, criteriaBuilder) ->
+        criteriaBuilder.equal(root.get(requestForWrittenConfirmation), certificateRequested);
+  }
+
+  private Specification<MedicalRegistryEntry> statusIsIn(Set<ProcedureStatus> statuses) {
+    if (statuses == null) {
+      return null;
+    }
+    return (root, query, criteriaBuilder) -> root.get(procedureStatus).in(statuses);
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
index 733811f40..67ba2cc72 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/Validator.java
@@ -5,9 +5,14 @@
 
 package de.eshg.medicalregistry;
 
+import de.eshg.file.common.FileType;
+import de.eshg.file.common.FileTypeDetector;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
 import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
+import java.io.IOException;
+import org.springframework.web.multipart.MultipartFile;
 
 public final class Validator {
   private Validator() {}
@@ -19,4 +24,15 @@ public final class Validator {
               .formatted(procedure.getExternalId()));
     }
   }
+
+  public static void validateFileType(MultipartFile multipartFile, FileType allowedFileType)
+      throws IOException {
+    FileType actualFileType = FileTypeDetector.getSupportedFileTypeOrThrow(multipartFile);
+    if (actualFileType != allowedFileType) {
+      throw new BadRequestException(
+          ErrorCode.INVALID_FILE,
+          String.format(
+              "The file type of %s is not %s.", multipartFile.getName(), allowedFileType));
+    }
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
deleted file mode 100644
index 6a408d83c..000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/AddressDto.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.api;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = "MedicalRegistryAddress")
-public record AddressDto(
-    @NotNull String street,
-    @NotNull String houseNumber,
-    @NotNull String postalCode,
-    @NotNull String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
index d34826ada..e80013925 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateProcedureRequest.java
@@ -5,9 +5,13 @@
 
 package de.eshg.medicalregistry.api;
 
-import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 
-@Schema(name = "CreateMedicalRegistryProcedureRequest")
-public record CreateProcedureRequest(@Valid @NotNull CreateProcedureDto procedure) {}
+public record CreateProcedureRequest(
+    @NotNull TypeOfChangeDto typeOfChange,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid PracticeDto practice,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java
new file mode 100644
index 000000000..0ccd6768b
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryEntryOverview.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+public record GetMedicalRegistryEntryOverview(
+    @NotNull @Schema(description = "Total number of result pages for the given filter criteria")
+        int totalPages,
+    @NotNull @Schema(description = "Total number of result elements for the given filter criteria")
+        long totalElements,
+    @Valid @NotNull @Size(max = 200) List<MedicalRegistryEntryDto> medicalRegistryEntries) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java
new file mode 100644
index 000000000..93d46b025
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetMedicalRegistryProceduresFilterOptions.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import io.swagger.v3.oas.annotations.Parameter;
+import java.util.Set;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.BindParam;
+
+@ParameterObject
+public record GetMedicalRegistryProceduresFilterOptions(
+    @BindParam(CERTIFICATE_REQUESTED)
+        @Parameter(
+            description =
+                """
+        Filter logic:
+        - In case of `true` only procedures are returned where a certificate was requested.
+        - In case of `false` only procedures are returned where no certificate was requested
+        - If not submitted, no filtering takes place
+        """)
+        Boolean certificateRequested,
+    @BindParam(PROCEDURE_STATUS)
+        @Parameter(
+            description =
+                """
+        Filter logic:
+        - If `procedureStatus` is submitted, only procedures are returned which have one of the submitted statuses.
+        - If not submitted, no filtering takes place
+        """)
+        Set<ProcedureStatusDto> procedureStatus) {
+  public static final String CERTIFICATE_REQUESTED = "certificateRequested";
+  public static final String PROCEDURE_STATUS = "procedureStatus";
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java
new file mode 100644
index 000000000..3a9e8ac49
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureConfirmedResponse.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetProcedureConfirmedResponse(
+    @NotNull UUID id,
+    @NotNull long version,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid List<PracticeDto> practices,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation)
+    implements GetProcedureResponse {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java
new file mode 100644
index 000000000..d1fb3b60c
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureDraftResponse.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetProcedureDraftResponse(
+    @NotNull UUID id,
+    @NotNull long version,
+    @NotNull TypeOfChangeDto typeOfChange,
+    @NotNull @Valid ProfessionalDto professional,
+    @Valid List<PracticeDto> practices,
+    @NotNull boolean employeesEmployed,
+    @NotNull boolean consentToPrivacyPolicy,
+    @NotNull boolean requestForWrittenConfirmation)
+    implements GetProcedureResponse {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
index b0be2afda..77f363d1e 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/GetProcedureResponse.java
@@ -5,18 +5,29 @@
 
 package de.eshg.medicalregistry.api;
 
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import java.util.List;
 import java.util.UUID;
 
-@Schema(name = "GetMedicalRegistryProcedureResponse")
-public record GetProcedureResponse(
-    @NotNull UUID id,
-    @NotNull long version,
-    @NotNull @Valid ProfessionalDto professional,
-    @Valid List<PracticeDto> practices,
-    @NotNull boolean employeesEmployed,
-    @NotNull boolean consentToPrivacyPolicy,
-    @NotNull boolean requestForWrittenConfirmation) {}
+@JsonSubTypes({
+  @JsonSubTypes.Type(value = GetProcedureDraftResponse.class),
+  @JsonSubTypes.Type(value = GetProcedureConfirmedResponse.class)
+})
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
+public sealed interface GetProcedureResponse
+    permits GetProcedureDraftResponse, GetProcedureConfirmedResponse {
+  UUID id();
+
+  long version();
+
+  ProfessionalDto professional();
+
+  List<PracticeDto> practices();
+
+  boolean employeesEmployed();
+
+  boolean consentToPrivacyPolicy();
+
+  boolean requestForWrittenConfirmation();
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java
new file mode 100644
index 000000000..6ff051e33
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/MedicalRegistryEntryDto.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import de.eshg.lib.procedure.model.ProcedureTypeDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.UUID;
+
+@Schema(name = "MedicalRegistryEntry")
+public record MedicalRegistryEntryDto(
+    @NotNull UUID id,
+    @NotEmpty String lastName,
+    @NotEmpty String firstName,
+    @NotNull LocalDate dateOfBirth,
+    @NotNull @Valid ProfessionalAddressDto address,
+    @NotNull boolean certificateRequested,
+    @NotNull ProcedureStatusDto status,
+    @NotNull ProcedureTypeDto type) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java
new file mode 100644
index 000000000..fbece6d22
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeAddressDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "PracticeAddress")
+public record PracticeAddressDto(
+    @NotNull @Size(min = 1, max = 55) String street,
+    @NotNull @Size(min = 1, max = 11) String houseNumber,
+    @NotNull @Size(min = 1, max = 20) String postalCode,
+    @NotNull @Size(min = 1, max = 50) String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
index 0e72ef082..53de54a49 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeDto.java
@@ -5,23 +5,39 @@
 
 package de.eshg.medicalregistry.api;
 
+import de.eshg.CustomValidations.EmailAddressConstraint;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
 
 @Schema(name = "Practice")
 public record PracticeDto(
     @NotNull @Size(min = 1, max = 300) String name,
-    @Size(min = 6, max = 254) String emailAddress,
-    @Size(min = 1, max = 23) String phoneNumber,
-    @Valid AddressDto address,
+    @NotNull @EmailAddressConstraint String emailAddress,
+    @NotNull @Size(min = 1, max = 23) String phoneNumber,
+    @NotNull @Valid PracticeAddressDto address,
     @Size(min = 6, max = 254) String website,
-    String institutionIdentifier,
-    String establishmentNumber,
+    @Pattern(regexp = "\\d+") String institutionIdentifier,
+    @Pattern(regexp = "\\d+") String establishmentNumber,
     @NotNull boolean healthInsuranceAuthorization,
     String openingHours) {
-  public PracticeDto(String name, boolean healthInsuranceAuthorization) {
-    this(name, null, null, null, null, null, null, healthInsuranceAuthorization, null);
+  public PracticeDto(
+      String name,
+      String emailAddress,
+      String phoneNumber,
+      PracticeAddressDto address,
+      boolean healthInsuranceAuthorization) {
+    this(
+        name,
+        emailAddress,
+        phoneNumber,
+        address,
+        null,
+        null,
+        null,
+        healthInsuranceAuthorization,
+        null);
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java
new file mode 100644
index 000000000..dc5178df3
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalAddressDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import de.eshg.lib.common.CountryCode;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "ProfessionalAddress")
+public record ProfessionalAddressDto(
+    @NotNull CountryCode country,
+    @NotNull @Size(min = 1, max = 55) String street,
+    @NotNull @Size(min = 1, max = 11) String houseNumber,
+    @NotNull @Size(min = 1, max = 20) String postalCode,
+    @NotNull @Size(min = 1, max = 50) String city) {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
index 04b21e7a5..6b5ca9852 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalDto.java
@@ -5,26 +5,28 @@
 
 package de.eshg.medicalregistry.api;
 
+import de.eshg.CustomValidations.EmailAddressConstraint;
 import de.eshg.base.GenderDto;
 import de.eshg.lib.common.CountryCode;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
 import java.time.LocalDate;
 
 @Schema(name = "Professional")
 public record ProfessionalDto(
     @Size(min = 1, max = 119) String title,
-    GenderDto gender,
+    @NotNull GenderDto gender,
     @NotNull @Size(min = 1, max = 80) String firstName,
     @NotNull @Size(min = 1, max = 120) String lastName,
     @NotNull LocalDate dateOfBirth,
-    @Size(min = 1, max = 40) String nameAtBirth,
-    @Size(min = 1, max = 50) String placeOfBirth,
-    @Size(min = 6, max = 254) String emailAddress,
-    @Size(min = 1, max = 23) String phoneNumber,
-    @Valid AddressDto address,
+    @NotNull @Size(min = 1, max = 40) String nameAtBirth,
+    @NotNull @Size(min = 1, max = 50) String placeOfBirth,
+    @NotNull @EmailAddressConstraint String emailAddress,
+    @NotNull @Size(min = 1, max = 23) String phoneNumber,
+    @NotNull @Valid ProfessionalAddressDto address,
     @NotNull ProfessionalTitleDto professionalTitle,
     String fieldOfExpertise,
     String specialistTitle,
@@ -32,31 +34,38 @@ public record ProfessionalDto(
     String qualifications,
     @NotNull LocalDate approbationGrantedOn,
     @NotNull String approbationIssuingAuthority,
-    String lifetimeDoctorNumber,
+    @Pattern(regexp = "\\d{9}") String lifetimeDoctorNumber,
     @NotNull EmploymentTypeDto employmentType,
     @NotNull EmploymentStatusDto employmentStatus,
     @NotNull CountryCode nationality) {
   public ProfessionalDto(
-      @NotNull @Size(min = 1, max = 80) String firstName,
-      @NotNull @Size(min = 1, max = 120) String lastName,
-      @NotNull LocalDate dateOfBirth,
-      @NotNull ProfessionalTitleDto professionalTitle,
-      @NotNull LocalDate approbationGrantedOn,
-      @NotNull String approbationIssuingAuthority,
-      @NotNull EmploymentTypeDto employmentType,
-      @NotNull EmploymentStatusDto employmentStatus,
-      @NotNull CountryCode nationality) {
+      String title,
+      GenderDto gender,
+      String firstName,
+      String lastName,
+      LocalDate dateOfBirth,
+      String nameAtBirth,
+      String placeOfBirth,
+      String emailAddress,
+      String phoneNumber,
+      ProfessionalAddressDto address,
+      ProfessionalTitleDto professionalTitle,
+      LocalDate approbationGrantedOn,
+      String approbationIssuingAuthority,
+      EmploymentTypeDto employmentType,
+      EmploymentStatusDto employmentStatus,
+      CountryCode nationality) {
     this(
-        null,
-        null,
+        title,
+        gender,
         firstName,
         lastName,
         dateOfBirth,
-        null,
-        null,
-        null,
-        null,
-        null,
+        nameAtBirth,
+        placeOfBirth,
+        emailAddress,
+        phoneNumber,
+        address,
         professionalTitle,
         null,
         null,
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java
new file mode 100644
index 000000000..fb36ca235
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/TypeOfChangeDto.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "TypeOfChange")
+public enum TypeOfChangeDto {
+  NEW_REGISTRATION,
+  SECOND_PRACTICE,
+  RE_REGISTRATION,
+  CHANGE_OF_REGISTRATION,
+  CHANGE_OF_NAME,
+  RELOCATION,
+  DEREGISTRATION,
+  OTHER
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java
new file mode 100644
index 000000000..31d660a59
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/DocumentData.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.business.model;
+
+import org.springframework.web.multipart.MultipartFile;
+
+public class DocumentData {
+  private final String fileName;
+  private final String description;
+  private final MultipartFile file;
+
+  public DocumentData(String fileName, String description, MultipartFile file) {
+    this.fileName = fileName;
+    this.description = description;
+    this.file = file;
+  }
+
+  public String getFileName() {
+    return fileName;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public MultipartFile getFile() {
+    return file;
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
deleted file mode 100644
index 3e6d1a012..000000000
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/business/model/ProcedureDetailsData.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package de.eshg.medicalregistry.business.model;
-
-public class ProcedureDetailsData {}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
index 851b8eb33..c8fe4c23f 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/domain/model/TypeOfChange.java
@@ -6,7 +6,6 @@
 package de.eshg.medicalregistry.domain.model;
 
 public enum TypeOfChange {
-  TypeOfChange,
   NEW_REGISTRATION,
   SECOND_PRACTICE,
   RE_REGISTRATION,
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
index b6858f0a7..42839056e 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/AddressMapper.java
@@ -5,30 +5,43 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.medicalregistry.api.AddressDto;
+import de.eshg.medicalregistry.api.PracticeAddressDto;
+import de.eshg.medicalregistry.api.ProfessionalAddressDto;
 
 public final class AddressMapper {
   private AddressMapper() {}
 
-  public static AddressDto mapToDto(de.eshg.base.address.AddressDto addressDto) {
+  public static ProfessionalAddressDto mapToProfessionalAddressDto(AddressDto addressDto) {
     if (addressDto == null) {
       return null;
     }
 
-    if (addressDto instanceof DomesticAddressDto address) {
-      return mapToDto(address);
-    } else {
-      throw new IllegalArgumentException("Unexpected instance of Address");
-    }
+    DomesticAddressDto address = toDomesticAddressOrThrow(addressDto);
+    return new ProfessionalAddressDto(
+        address.country(),
+        address.street(),
+        address.houseNumber(),
+        address.postalCode(),
+        address.city());
   }
 
-  private static AddressDto mapToDto(DomesticAddressDto addressDto) {
+  public static PracticeAddressDto mapToPracticeAddressDto(AddressDto addressDto) {
     if (addressDto == null) {
       return null;
     }
 
-    return new AddressDto(
-        addressDto.street(), addressDto.houseNumber(), addressDto.postalCode(), addressDto.city());
+    DomesticAddressDto address = toDomesticAddressOrThrow(addressDto);
+    return new PracticeAddressDto(
+        address.street(), address.houseNumber(), address.postalCode(), address.city());
+  }
+
+  private static DomesticAddressDto toDomesticAddressOrThrow(AddressDto addressDto) {
+    if (addressDto instanceof DomesticAddressDto address) {
+      return address;
+    } else {
+      throw new IllegalArgumentException("Unexpected instance of Address");
+    }
   }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java
new file mode 100644
index 000000000..adfdd4b62
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/EntryMapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
+import de.eshg.lib.procedure.mapping.ProcedureMapper;
+import de.eshg.medicalregistry.api.MedicalRegistryEntryDto;
+import de.eshg.medicalregistry.domain.model.MedicalRegistryEntry;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.UUID;
+
+public class EntryMapper {
+  private EntryMapper() {}
+
+  public static MedicalRegistryEntryDto mapToDto(
+      MedicalRegistryEntry entry, Map<UUID, AddPersonFileStateResponse> relatedPersons) {
+    AddPersonFileStateResponse relatedPerson = getRelatedPersonOrThrow(entry, relatedPersons);
+    return new MedicalRegistryEntryDto(
+        entry.getExternalId(),
+        relatedPerson.lastName(),
+        relatedPerson.firstName(),
+        relatedPerson.dateOfBirth(),
+        AddressMapper.mapToProfessionalAddressDto(relatedPerson.contactAddress()),
+        entry.isRequestForWrittenConfirmation(),
+        ProcedureMapper.toInterfaceType(entry.getProcedureStatus()),
+        ProcedureMapper.toInterfaceType(entry.getProcedureType()));
+  }
+
+  private static AddPersonFileStateResponse getRelatedPersonOrThrow(
+      MedicalRegistryEntry entry, Map<UUID, AddPersonFileStateResponse> personMap) {
+
+    UUID relatedPersonId = entry.getRelatedPersons().getFirst().getCentralFileStateId();
+
+    return Optional.ofNullable(personMap.get(relatedPersonId))
+        .orElseThrow(() -> new NoSuchElementException("No matching person found"));
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
index 184262149..43473aae1 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/PracticeMapper.java
@@ -5,6 +5,7 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import static de.eshg.medicalregistry.mapper.AddressMapper.*;
 import static de.eshg.medicalregistry.util.MapperUtils.*;
 
 import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
@@ -21,7 +22,7 @@ public final class PracticeMapper {
         practiceDetails.name(),
         singleElementOrNull(practiceDetails.emailAddresses()),
         singleElementOrNull(practiceDetails.phoneNumbers()),
-        AddressMapper.mapToDto(practiceDetails.contactAddress()),
+        mapToPracticeAddressDto(practiceDetails.contactAddress()),
         practice.getWebsite(),
         practice.getInstitutionIdentifier(),
         practice.getEstablishmentNumber(),
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java
new file mode 100644
index 000000000..fa814fab9
--- /dev/null
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProcedureMapper.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package de.eshg.medicalregistry.mapper;
+
+import de.cronn.commons.lang.StreamUtil;
+import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse;
+import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
+import de.eshg.medicalregistry.api.*;
+import de.eshg.medicalregistry.domain.model.*;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public final class ProcedureMapper {
+  private ProcedureMapper() {}
+
+  public static GetProcedureResponse mapToDto(
+      MedicalRegistryEntry medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+    return switch (medicalRegistryEntry) {
+      case null -> null;
+      case MedicalRegistryEntryChange draft ->
+          mapToDraftDto(draft, professionalDetails, practiceDetails);
+      case MedicalRegistryEntry confirmed ->
+          mapToConfirmedDto(confirmed, professionalDetails, practiceDetails);
+    };
+  }
+
+  public static GetProcedureDraftResponse mapToDraftDto(
+      MedicalRegistryEntryChange medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+
+    return new GetProcedureDraftResponse(
+        medicalRegistryEntry.getExternalId(),
+        medicalRegistryEntry.getVersion(),
+        mapTypeOfChangeToDto(medicalRegistryEntry.getTypeOfChange()),
+        mapProfessional(medicalRegistryEntry.getRelatedPersons(), professionalDetails),
+        mapPractices(medicalRegistryEntry.getRelatedFacilities(), practiceDetails),
+        medicalRegistryEntry.isEmployeesEmployed(),
+        medicalRegistryEntry.isConsentToPrivacyPolicy(),
+        medicalRegistryEntry.isRequestForWrittenConfirmation());
+  }
+
+  public static GetProcedureConfirmedResponse mapToConfirmedDto(
+      MedicalRegistryEntry medicalRegistryEntry,
+      GetPersonFileStateResponse professionalDetails,
+      Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+
+    return new GetProcedureConfirmedResponse(
+        medicalRegistryEntry.getExternalId(),
+        medicalRegistryEntry.getVersion(),
+        mapProfessional(medicalRegistryEntry.getRelatedPersons(), professionalDetails),
+        mapPractices(medicalRegistryEntry.getRelatedFacilities(), practiceDetails),
+        medicalRegistryEntry.isEmployeesEmployed(),
+        medicalRegistryEntry.isConsentToPrivacyPolicy(),
+        medicalRegistryEntry.isRequestForWrittenConfirmation());
+  }
+
+  private static ProfessionalDto mapProfessional(
+      List<Professional> persons, GetPersonFileStateResponse professionalDetails) {
+    Professional professional = persons.stream().collect(StreamUtil.toSingleElement());
+    return ProfessionalMapper.mapToDto(professional, professionalDetails);
+  }
+
+  private static List<PracticeDto> mapPractices(
+      List<Practice> facilities, Map<UUID, GetFacilityFileStateResponse> practiceDetails) {
+    return facilities.stream()
+        .map(p -> PracticeMapper.mapToDto(p, practiceDetails.get(p.getCentralFileStateId())))
+        .toList();
+  }
+
+  public static TypeOfChange mapToDomain(TypeOfChangeDto typeOfChangeDto) {
+    if (typeOfChangeDto == null) {
+      return null;
+    }
+
+    return switch (typeOfChangeDto) {
+      case NEW_REGISTRATION -> TypeOfChange.NEW_REGISTRATION;
+      case SECOND_PRACTICE -> TypeOfChange.SECOND_PRACTICE;
+      case RE_REGISTRATION -> TypeOfChange.RE_REGISTRATION;
+      case CHANGE_OF_REGISTRATION -> TypeOfChange.CHANGE_OF_REGISTRATION;
+      case CHANGE_OF_NAME -> TypeOfChange.CHANGE_OF_NAME;
+      case RELOCATION -> TypeOfChange.RELOCATION;
+      case DEREGISTRATION -> TypeOfChange.DEREGISTRATION;
+      case OTHER -> TypeOfChange.OTHER;
+    };
+  }
+
+  private static TypeOfChangeDto mapTypeOfChangeToDto(TypeOfChange typeOfChange) {
+    if (typeOfChange == null) {
+      return null;
+    }
+
+    return switch (typeOfChange) {
+      case NEW_REGISTRATION -> TypeOfChangeDto.NEW_REGISTRATION;
+      case SECOND_PRACTICE -> TypeOfChangeDto.SECOND_PRACTICE;
+      case RE_REGISTRATION -> TypeOfChangeDto.RE_REGISTRATION;
+      case CHANGE_OF_REGISTRATION -> TypeOfChangeDto.CHANGE_OF_REGISTRATION;
+      case CHANGE_OF_NAME -> TypeOfChangeDto.CHANGE_OF_NAME;
+      case RELOCATION -> TypeOfChangeDto.RELOCATION;
+      case DEREGISTRATION -> TypeOfChangeDto.DEREGISTRATION;
+      case OTHER -> TypeOfChangeDto.OTHER;
+    };
+  }
+}
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
index 137efd8ff..1c15f20c9 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/mapper/ProfessionalMapper.java
@@ -5,8 +5,10 @@
 
 package de.eshg.medicalregistry.mapper;
 
+import static de.eshg.medicalregistry.mapper.AddressMapper.*;
 import static de.eshg.medicalregistry.util.MapperUtils.singleElementOrNull;
 
+import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
 import de.eshg.medicalregistry.api.*;
 import de.eshg.medicalregistry.domain.model.EmploymentStatus;
@@ -33,7 +35,7 @@ public final class ProfessionalMapper {
         professionalDetails.placeOfBirth(),
         singleElementOrNull(professionalDetails.emailAddresses()),
         singleElementOrNull(professionalDetails.phoneNumbers()),
-        AddressMapper.mapToDto(professionalDetails.contactAddress()),
+        mapToProfessionalAddressDto(professionalDetails.contactAddress()),
         mapToDto(professional.getProfessionalTitle()),
         professional.getFieldOfExpertise(),
         professional.getSpecialistTitle(),
@@ -206,4 +208,29 @@ public final class ProfessionalMapper {
       case EMPLOYEE -> EmploymentStatus.EMPLOYEE;
     };
   }
+
+  public static ProfessionalAddressDto mapToDto(de.eshg.base.address.AddressDto addressDto) {
+    if (addressDto == null) {
+      return null;
+    }
+
+    if (addressDto instanceof DomesticAddressDto address) {
+      return mapToDto(address);
+    } else {
+      throw new IllegalArgumentException("Unexpected instance of Address");
+    }
+  }
+
+  private static ProfessionalAddressDto mapToDto(DomesticAddressDto addressDto) {
+    if (addressDto == null) {
+      return null;
+    }
+
+    return new ProfessionalAddressDto(
+        addressDto.country(),
+        addressDto.street(),
+        addressDto.houseNumber(),
+        addressDto.postalCode(),
+        addressDto.city());
+  }
 }
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
index c4276f63d..d96b5715e 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperController.java
@@ -42,6 +42,12 @@ public class MedicalRegistryTestHelperController extends TestHelperController
     medicalRegistryTestHelperService.closeProcedure(procedureId);
   }
 
+  @Transactional
+  @PostExchange("/medical-registry-entries/{procedureId}/open")
+  public void openProcedure(@PathVariable("procedureId") UUID procedureId) {
+    medicalRegistryTestHelperService.openProcedure(procedureId);
+  }
+
   @Override
   public void clearAuditLogStorageDirectory() throws IOException {
     auditLogTestHelperService.clearAuditLogStorageDirectory();
diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
index 26ab255b4..151c9d35d 100644
--- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
+++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/testhelper/MedicalRegistryTestHelperService.java
@@ -53,4 +53,10 @@ public class MedicalRegistryTestHelperService extends DefaultTestHelperService {
         medicalRegistryEntryRepository.findByExternalId(procedureId).orElseThrow();
     medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.CLOSED, clock, auditLogger);
   }
+
+  public void openProcedure(UUID procedureId) {
+    MedicalRegistryEntry medicalRegistryEntry =
+        medicalRegistryEntryRepository.findByExternalId(procedureId).orElseThrow();
+    medicalRegistryEntry.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+  }
 }
diff --git a/backend/opendata/src/main/resources/application-health-department-frankfurt.properties b/backend/opendata/src/main/resources/application-health-department-frankfurt.properties
new file mode 100644
index 000000000..35488e0b1
--- /dev/null
+++ b/backend/opendata/src/main/resources/application-health-department-frankfurt.properties
@@ -0,0 +1 @@
+version.author=GA Frankfurt
\ No newline at end of file
diff --git a/backend/opendata/src/main/resources/application.properties b/backend/opendata/src/main/resources/application.properties
index 0c3a90a1a..dbef17751 100644
--- a/backend/opendata/src/main/resources/application.properties
+++ b/backend/opendata/src/main/resources/application.properties
@@ -7,6 +7,3 @@ spring.datasource.username=testuser
 spring.datasource.password=testpassword
 
 spring.jpa.hibernate.ddl-auto=create
-
-version.author=GA Frankfurt
-
diff --git a/backend/school-entry/openApi.yaml b/backend/school-entry/openApi.yaml
index 7a5b4bfc6..c2fb31d3f 100644
--- a/backend/school-entry/openApi.yaml
+++ b/backend/school-entry/openApi.yaml
@@ -491,6 +491,19 @@ paths:
           description: OK
       tags:
       - SchoolEntryCitizen
+  /citizen/school-entries/opening-hours:
+    get:
+      operationId: getOpeningHours
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetOpeningHoursResponse"
+          description: OK
+      summary: Get the official opening hours.
+      tags:
+      - SchoolEntryCitizen
   /config:
     get:
       operationId: getConfig
@@ -662,6 +675,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1570,6 +1598,25 @@ paths:
       summary: Upload a XLSX file to create multiple procedures.
       tags:
       - SchoolEntry
+  /school-entries/download/invitations:
+    post:
+      operationId: downloadInvitations
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DownloadInvitationsBulkRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                type: string
+                format: binary
+          description: OK
+      tags:
+      - SchoolEntry
   /school-entries/icd10-codes:
     get:
       operationId: searchIcd10Codes
@@ -3078,6 +3125,17 @@ components:
       required:
       - custodian
       - procedureVersion
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     AdditionalChildInfo:
       type: object
       properties:
@@ -4036,12 +4094,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMedicalReportRequest:
@@ -4180,6 +4234,8 @@ components:
           format: date
         schoolName:
           type: string
+        wasInDaycare:
+          type: boolean
     DecibelValue:
       type: string
       description: Decibel value that was audible for the respective ear.
@@ -4356,6 +4412,16 @@ components:
       - country
       - postalCode
       - street
+    DownloadInvitationsBulkRequest:
+      type: object
+      properties:
+        procedureIds:
+          type: array
+          items:
+            type: string
+            format: uuid
+      required:
+      - procedureIds
     EvaluationArticulationValue:
       type: string
       enum:
@@ -4674,6 +4740,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -5047,6 +5119,20 @@ components:
             - $ref: "#/components/schemas/ImageMetaDataHistory"
             - $ref: "#/components/schemas/MailMetaDataHistory"
             - $ref: "#/components/schemas/PdfMetaDataHistory"
+    GetOpeningHoursResponse:
+      type: object
+      properties:
+        de:
+          type: array
+          items:
+            type: string
+        en:
+          type: array
+          items:
+            type: string
+      required:
+      - de
+      - en
     GetProcedureApprovalRequestsResponse:
       type: object
       properties:
@@ -5151,7 +5237,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -5646,6 +5734,20 @@ components:
           type: boolean
         otherInterests:
           type: string
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     KnowledgeThinkingExamination:
       type: object
       properties:
@@ -5728,13 +5830,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -5768,13 +5876,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5904,15 +6009,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -6319,6 +6418,10 @@ components:
         deceased:
           type: string
           format: date
+        hasBeenClosed:
+          type: boolean
+        hasInformationBlock:
+          type: boolean
         id:
           type: string
           format: uuid
@@ -6363,6 +6466,8 @@ components:
       - child
       - createdAt
       - custodians
+      - hasBeenClosed
+      - hasInformationBlock
       - id
       - isDeceased
       - isDeletable
@@ -6893,9 +6998,8 @@ components:
     SchoolEntryFeature:
       type: string
       enum:
-      - CLOSE_PROCEDURE
-      - REOPEN_PROCEDURE
       - IMPORT_PAST_PROCEDURES
+      - BULK_DOWNLOAD_INVITATIONS
     SchoolEntryLabel:
       type: object
       description: Labels can be associated to a procedure. There are predefined labels
@@ -7184,6 +7288,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -7195,6 +7304,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java
new file mode 100644
index 000000000..40bae1d53
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/LabelService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry;
+
+import static de.eshg.schoolentry.population.CreateLabelsTask.INFORMATION_BLOCK_LABEL_NAME;
+import static de.eshg.schoolentry.population.CreateLabelsTask.SPECIAL_NEEDS_LABEL_NAME;
+
+import de.eshg.schoolentry.domain.model.Label;
+import de.eshg.schoolentry.domain.repository.LabelRepository;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Component
+@RequestScope
+public class LabelService {
+  private final LabelRepository labelRepository;
+  private final Map<String, Long> cachedLabelIds = new ConcurrentHashMap<>();
+
+  public LabelService(LabelRepository labelRepository) {
+    this.labelRepository = labelRepository;
+  }
+
+  public Label getSpecialNeedsLabel() {
+    return findSystemLabelOrThrow(SPECIAL_NEEDS_LABEL_NAME);
+  }
+
+  public Label getInformationBlockLabel() {
+    return findSystemLabelOrThrow(INFORMATION_BLOCK_LABEL_NAME);
+  }
+
+  private Label findSystemLabelOrThrow(String name) {
+    Long labelId = cachedLabelIds.computeIfAbsent(name, this::findLabelIdByNameUncached);
+    return labelRepository.getReferenceById(labelId);
+  }
+
+  private Long findLabelIdByNameUncached(String labelName) {
+    return labelRepository
+        .findByName(labelName)
+        .orElseThrow(
+            () ->
+                new IllegalStateException(
+                    "System-populated label %s is missing".formatted(labelName)))
+        .getId();
+  }
+
+  public boolean contains(List<UUID> externalLabelIds, String name) {
+    return labelRepository.existsByNameAndExternalIdIn(name, externalLabelIds);
+  }
+
+  public List<Label> findByExternalIds(List<UUID> externalLabelIds) {
+    return labelRepository.findAllByExternalIdInOrderById(externalLabelIds);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
index 10b8514bf..dfd6597aa 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/ProceduresHelper.java
@@ -114,7 +114,7 @@ public class ProceduresHelper {
         new SchoolEntryProcedureSpecification(
             ProcedureStatus.OPEN,
             ProcedureMapper.mapToDomain(filterParameters.procedureTypeFilter()),
-            filterParameters.schoolIdFilter(),
+            contactClient.getContactAliases(filterParameters.schoolIdFilter()),
             ProcedureMapper.mapIntegerToYear(filterParameters.schoolYearFilter()),
             getDayOfAppointmentAsInstant(filterParameters.dayOfAppointmentFilter()),
             filterParameters.hasAppointmentFilter(),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
index 0ce93087e..c98f3f9e8 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenController.java
@@ -11,21 +11,23 @@ import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.schoolentry.api.citizen.AddCitizenAnamnesisRequest;
-import de.eshg.schoolentry.api.citizen.GetCitizenFreeAppointmentsResponse;
-import de.eshg.schoolentry.api.citizen.GetCitizenProcedureResponse;
+import de.eshg.schoolentry.api.citizen.*;
 import de.eshg.schoolentry.api.citizen.GetCitizenProcedureResponse.CitizenChildDto;
-import de.eshg.schoolentry.api.citizen.UpdateCitizenAppointmentRequest;
+import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.UUID;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
 import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.oauth2.jwt.Jwt;
@@ -47,17 +49,28 @@ public class SchoolEntryCitizenController {
   private final Resource privacyNotice;
   private final Resource privacyPolicy;
 
+  private final SchoolEntryProperties schoolEntryProperties;
+
   public SchoolEntryCitizenController(
       SchoolEntryCitizenService schoolEntryCitizenService,
       PersonApi personApi,
       Validator validator,
-      @Value("${de.eshg.schoolentry.privacy-notice-location}") Resource privacyNotice,
-      @Value("${de.eshg.schoolentry.privacy-policy-location}") Resource privacyPolicy) {
+      SchoolEntryProperties schoolEntryProperties) {
     this.schoolEntryCitizenService = schoolEntryCitizenService;
     this.personApi = personApi;
     this.validator = validator;
-    this.privacyNotice = privacyNotice;
-    this.privacyPolicy = privacyPolicy;
+    this.schoolEntryProperties = schoolEntryProperties;
+    this.privacyNotice = toResource(schoolEntryProperties.getPrivacyNoticeLocation());
+    this.privacyPolicy = toResource(schoolEntryProperties.getPrivacyPolicyLocation());
+  }
+
+  private static Resource toResource(URI documentLocation) {
+    try {
+      UrlResource urlResource = new UrlResource(documentLocation);
+      return urlResource;
+    } catch (MalformedURLException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   @GetMapping
@@ -138,9 +151,19 @@ public class SchoolEntryCitizenController {
           "submitting citizen anamnesis is not allowed as there were already edits to the anamnesis");
     }
 
+    validator.validateCitizenAnamnesis(request.anamnesis());
+
     schoolEntryCitizenService.addCitizenAnamnesis(schoolEntryProcedure, request.anamnesis());
   }
 
+  @GetMapping(path = "/opening-hours")
+  @Operation(summary = "Get the official opening hours.")
+  @Transactional(readOnly = true)
+  public GetOpeningHoursResponse getOpeningHours() {
+    SchoolEntryProperties.OpeningHours openingHours = schoolEntryProperties.getOpeningHours();
+    return new GetOpeningHoursResponse(openingHours.de(), openingHours.en());
+  }
+
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
   @Transactional(readOnly = true)
@@ -156,19 +179,15 @@ public class SchoolEntryCitizenController {
   }
 
   private static ResponseEntity<Resource> getPrivacyDocument(Resource privacyDocument) {
+    String filename = privacyDocument.getFilename();
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            fileAttachment(privacyDocument.getFilename()).toString())
-        .header(HttpHeaders.CONTENT_TYPE, "application/pdf")
+            ContentDisposition.attachment()
+                .filename(filename, StandardCharsets.UTF_8)
+                .build()
+                .toString())
+        .contentType(MediaType.APPLICATION_PDF)
         .body(privacyDocument);
   }
-
-  private static ContentDisposition fileAttachment(String filename) {
-    return file(filename, ContentDisposition.attachment());
-  }
-
-  private static ContentDisposition file(String filename, ContentDisposition.Builder builder) {
-    return builder.name("file").filename(filename).build();
-  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
index d2fa4d37a..ab706cbe1 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryCitizenService.java
@@ -53,6 +53,7 @@ public class SchoolEntryCitizenService {
   private final SchoolEntryProcedureRepository schoolEntryProcedureRepository;
   private final SchoolEntryService schoolEntryService;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final ProgressEntryUtil progressEntryUtil;
   private final DepartmentClient departmentClient;
   private final ContactClient contactClient;
 
@@ -62,6 +63,7 @@ public class SchoolEntryCitizenService {
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
       SchoolEntryService schoolEntryService,
       AppointmentBlockSlotUtil appointmentBlockSlotUtil,
+      ProgressEntryUtil progressEntryUtil,
       DepartmentClient departmentClient,
       ContactClient contactClient) {
     this.clock = clock;
@@ -69,6 +71,7 @@ public class SchoolEntryCitizenService {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.schoolEntryService = schoolEntryService;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
+    this.progressEntryUtil = progressEntryUtil;
     this.departmentClient = departmentClient;
     this.contactClient = contactClient;
   }
@@ -131,7 +134,7 @@ public class SchoolEntryCitizenService {
     schoolEntryProcedure.setAppointmentChangesByCitizen(
         schoolEntryProcedure.getAppointmentChangesByCitizen() + 1);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         schoolEntryProcedure, APPOINTMENT_RESCHEDULED_BY_CITIZEN, TriggerType.CITIZEN);
     schoolEntryProcedure
         .getTaskOfType(TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION)
@@ -157,7 +160,7 @@ public class SchoolEntryCitizenService {
     Anamnesis citizenAnamnesisAsDomainModel =
         AnamnesisMapper.mapCitizenAnamnesisToDomain(anamnesis);
     schoolEntryService.copyValues(citizenAnamnesisAsDomainModel, procedure.getAnamnesis());
-    ProgressEntryUtil.addProgressEntry(procedure, ANAMNESIS_ADDED_BY_CITIZEN, TriggerType.CITIZEN);
+    progressEntryUtil.addProgressEntry(procedure, ANAMNESIS_ADDED_BY_CITIZEN, TriggerType.CITIZEN);
   }
 
   public AppointmentAddressDto getAppointmentAddress(SchoolEntryProcedure procedure) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
index 8b751aba5..167f74243 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryController.java
@@ -48,6 +48,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.Min;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.time.Clock;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -91,6 +92,7 @@ public class SchoolEntryController {
   private final SchoolEntryFeatureToggle featureToggle;
   private final AppointmentBlockProperties appointmentBlockProperties;
   private final SchoolEntryProperties schoolEntryProperties;
+  private final ProgressEntryUtil progressEntryUtil;
 
   public SchoolEntryController(
       SchoolEntryService schoolEntryService,
@@ -103,7 +105,8 @@ public class SchoolEntryController {
       @Value("classpath:templates/import/SchoolListTemplate.xlsx") Resource schoolListTemplate,
       SchoolEntryFeatureToggle featureToggle,
       AppointmentBlockProperties appointmentBlockProperties,
-      SchoolEntryProperties schoolEntryProperties) {
+      SchoolEntryProperties schoolEntryProperties,
+      ProgressEntryUtil progressEntryUtil) {
     this.schoolEntryService = schoolEntryService;
     this.importService = importService;
     this.medicalReportGenerator = medicalReportGenerator;
@@ -115,6 +118,7 @@ public class SchoolEntryController {
     this.featureToggle = featureToggle;
     this.appointmentBlockProperties = appointmentBlockProperties;
     this.schoolEntryProperties = schoolEntryProperties;
+    this.progressEntryUtil = progressEntryUtil;
   }
 
   @PostMapping
@@ -182,8 +186,6 @@ public class SchoolEntryController {
   public ProcedureDetailsDto closeProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody CloseProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.CLOSE_PROCEDURE);
-
     SchoolEntryProcedure procedure =
         schoolEntryService.findProcedureByExternalIdForUpdate(procedureId, request.version());
     Validator.validateSchoolInfoLetterCreated(procedure);
@@ -197,7 +199,6 @@ public class SchoolEntryController {
   public ProcedureDetailsDto reopenProcedure(
       @PathVariable("procedureId") UUID procedureId,
       @Valid @RequestBody ReopenProcedureRequest request) {
-    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.REOPEN_PROCEDURE);
     SchoolEntryProcedure procedure =
         schoolEntryService.reopenProcedure(procedureId, request.version());
     return augmentAndMap(procedure);
@@ -584,12 +585,15 @@ public class SchoolEntryController {
     ProcedureDetailsData procedureDetailsData = schoolEntryService.augmentWithDetails(procedure);
 
     Pdf pdf = medicalReportGenerator.generateMedicalReport(procedureDetailsData.child(), request);
-    ProgressEntryUtil.addProgressEntry(procedure, MEDICAL_REPORT_GENERATED, pdf);
+    progressEntryUtil.addProgressEntry(procedure, MEDICAL_REPORT_GENERATED, pdf);
 
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            ContentDisposition.attachment().filename(pdf.getFileName()).build().toString())
+            ContentDisposition.attachment()
+                .filename(pdf.getFileName(), StandardCharsets.UTF_8)
+                .build()
+                .toString())
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF_VALUE)
         .body(new ByteArrayResource(pdf.getFileContent().getContent()));
   }
@@ -607,14 +611,17 @@ public class SchoolEntryController {
     Pdf pdf =
         schoolInfoLetterGenerator.generateSchoolInfoLetter(
             procedure, procedureDetailsData, request);
-    ProgressEntryUtil.addProgressEntry(procedure, SCHOOL_INFO_LETTER_GENERATED, pdf);
+    progressEntryUtil.addProgressEntry(procedure, SCHOOL_INFO_LETTER_GENERATED, pdf);
     procedure.setschoolInfoLetterCreatedAt(Instant.now(clock));
     TaskUtil.closeOptionalTaskOfType(procedure, TaskType.PERFORM_SCHOOL_ENTRY_EXAMINATION);
 
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
-            ContentDisposition.attachment().filename(pdf.getFileName()).build().toString())
+            ContentDisposition.attachment()
+                .filename(pdf.getFileName(), StandardCharsets.UTF_8)
+                .build()
+                .toString())
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF_VALUE)
         .body(new ByteArrayResource(pdf.getFileContent().getContent()));
   }
@@ -669,6 +676,24 @@ public class SchoolEntryController {
         pagedProcedures.totalNumberOfProcedures());
   }
 
+  @PostMapping("/download/invitations")
+  @Transactional(readOnly = true)
+  public ResponseEntity<Resource> downloadInvitations(
+      @Valid @RequestBody DownloadInvitationsBulkRequest request) throws IOException {
+    featureToggle.assertNewFeatureIsEnabled(SchoolEntryFeature.BULK_DOWNLOAD_INVITATIONS);
+    return ResponseEntity.ok()
+        .header(
+            HttpHeaders.CONTENT_DISPOSITION,
+            ContentDisposition.attachment()
+                .filename("Einladungen.zip", StandardCharsets.UTF_8)
+                .build()
+                .toString())
+        .header(HttpHeaders.CONTENT_TYPE, CustomMediaTypes.ZIP_VALUE)
+        .body(
+            new ByteArrayResource(
+                schoolEntryService.zipInvitationsForProcedures(request.procedureIds())));
+  }
+
   private void assertLocationModeNotSet() {
     if (appointmentBlockProperties.getLocationSelectionMode() != LocationSelectionMode.NONE) {
       throw ExceptionUtil.badRequestExceptionUnsupportedLocationMode();
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
index 683e12774..32209fabb 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/SchoolEntryService.java
@@ -37,9 +37,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.business.model.*;
-import de.eshg.schoolentry.client.ChildUpdate;
 import de.eshg.schoolentry.client.PersonClient;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.*;
 import de.eshg.schoolentry.domain.repository.*;
@@ -56,14 +54,20 @@ import de.eshg.schoolentry.util.ProgressEntryUtil;
 import de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType;
 import de.eshg.schoolentry.util.TaskUtil;
 import de.eshg.validation.ValidationUtil;
+import jakarta.persistence.criteria.Path;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.time.*;
 import java.util.*;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
 import org.springframework.web.client.HttpClientErrorException;
@@ -90,17 +94,17 @@ public class SchoolEntryService {
   private final AppointmentBlockProperties appointmentBlockProperties;
   private final Clock clock;
   private final SchoolEntryProperties schoolEntryProperties;
-  private final LabelRepository labelRepository;
+  private final LabelService labelService;
   private final CitizenAccessCodeUserApi citizenAccessCodeUserApi;
   private final InvitationGenerator invitationGenerator;
   private final PercentileCalculationService percentileCalculationService;
   private final Validator validator;
   private final AuditLogger auditLogger;
   private final ProcedureSearchService<SchoolEntryProcedure> procedureSearchService;
-  private final SchoolEntryFeatureToggle schoolEntryFeatureToggle;
   private final ProceduresHelper proceduresHelper;
   private final ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+  private final ProgressEntryUtil progressEntryUtil;
   private final TaskUtil taskUtil;
 
   public SchoolEntryService(
@@ -114,7 +118,7 @@ public class SchoolEntryService {
       VaccinationStatusRepository vaccinationStatusRepository,
       AnamnesisRepository anamnesisRepository,
       WaitingRoomRepository waitingRoomRepository,
-      LabelRepository labelRepository,
+      LabelService labelService,
       PersonClient personClient,
       ContactClient contactClient,
       AppointmentBlockSlotUtil appointmentBlockSlotUtil,
@@ -128,10 +132,10 @@ public class SchoolEntryService {
       Validator validator,
       AuditLogger auditLogger,
       ProcedureSearchService<SchoolEntryProcedure> procedureSearchService,
-      SchoolEntryFeatureToggle schoolEntryFeatureToggle,
       ProceduresHelper proceduresHelper,
       ProcedureDeletionService<SchoolEntryProcedure> procedureDeletionService,
       ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper,
+      ProgressEntryUtil progressEntryUtil,
       TaskUtil taskUtil) {
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.personRepository = personRepository;
@@ -143,7 +147,7 @@ public class SchoolEntryService {
     this.vaccinationStatusRepository = vaccinationStatusRepository;
     this.anamnesisRepository = anamnesisRepository;
     this.waitingRoomRepository = waitingRoomRepository;
-    this.labelRepository = labelRepository;
+    this.labelService = labelService;
     this.personClient = personClient;
     this.contactClient = contactClient;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
@@ -157,10 +161,10 @@ public class SchoolEntryService {
     this.validator = validator;
     this.auditLogger = auditLogger;
     this.procedureSearchService = procedureSearchService;
-    this.schoolEntryFeatureToggle = schoolEntryFeatureToggle;
     this.proceduresHelper = proceduresHelper;
     this.procedureDeletionService = procedureDeletionService;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+    this.progressEntryUtil = progressEntryUtil;
     this.taskUtil = taskUtil;
   }
 
@@ -192,10 +196,7 @@ public class SchoolEntryService {
   }
 
   public List<SchoolEntryProcedure> createProceduresFromDataImport(
-      List<ImportPastProcedureData> pastProcedures,
-      UUID schoolId,
-      UUID locationId,
-      Year schoolYear) {
+      List<ImportPastProcedureData> pastProcedures, UUID schoolId, Year schoolYear) {
     List<ImportProcedureData> procedureData =
         pastProcedures.stream().map(ImportPastProcedureData::procedureData).toList();
     List<SchoolEntryProcedure> result = new ArrayList<>();
@@ -215,6 +216,10 @@ public class SchoolEntryService {
           mapAnamnesisData(pastProcedureData.anamnesisData(), procedure.examinationDate());
       VaccinationStatus vaccinationStatus =
           mapVaccinationStatus(pastProcedureData.vaccinationStatusData());
+      EyeExaminationResult eyeExaminationResult = pastProcedureData.eyeExaminationResult();
+      HearingTestResult hearingTestResult = pastProcedureData.hearingTestResult();
+      SopessExaminationResult sopessExaminationResult = pastProcedureData.sopessExaminationData();
+      DevelopmentScreening developmentScreening = pastProcedureData.developmentScreeningData();
 
       SchoolEntryProcedure schoolEntryProcedure =
           saveSchoolEntryProcedure(
@@ -222,13 +227,17 @@ public class SchoolEntryService {
               procedureIds.custodianIds(),
               procedureType,
               schoolId,
-              locationId,
+              null,
               schoolYear,
               procedure.isEntryLevel(),
               procedure.examinationDate(),
               ProcedureStatus.CLOSED,
               anamnesis,
-              vaccinationStatus);
+              vaccinationStatus,
+              eyeExaminationResult,
+              hearingTestResult,
+              sopessExaminationResult,
+              developmentScreening);
 
       if (procedure.isEarlyExamination()) {
         Assert.notNull(specialNeedsLabel, "specialNeedsLabel must be fetched at this point");
@@ -276,7 +285,11 @@ public class SchoolEntryService {
               procedure.examinationDate(),
               initialProcedureStatus,
               new Anamnesis(),
-              new VaccinationStatus());
+              new VaccinationStatus(),
+              new EyeExaminationResult(),
+              new HearingTestResult(),
+              new SopessExaminationResult(),
+              new DevelopmentScreening());
 
       if (procedure.isEarlyExamination()) {
         Assert.notNull(specialNeedsLabel, "specialNeedsLabel must be fetched at this point");
@@ -295,14 +308,14 @@ public class SchoolEntryService {
 
   private Label fetchSpecialNeedsLabelIfNecessary(List<ImportProcedureData> procedureData) {
     if (procedureData.stream().anyMatch(ImportProcedureData::isEarlyExamination)) {
-      return getSpecialNeedsLabel();
+      return labelService.getSpecialNeedsLabel();
     }
     return null;
   }
 
   private Label fetchInformationBlockLabelIfNecessary(List<ImportProcedureData> procedureData) {
     if (procedureData.stream().anyMatch(ImportProcedureData::hasInformationBlock)) {
-      return getInformationBlockLabel();
+      return labelService.getInformationBlockLabel();
     }
     return null;
   }
@@ -324,6 +337,7 @@ public class SchoolEntryService {
     anamnesis.setNationalitySecondParent(
         getCountryCode(importAnamnesisData.nationalitySecondParent()));
     anamnesis.setHasMigrationBackground(importAnamnesisData.hasMigrationBackground());
+    anamnesis.setWasInDaycare(mapToWasInDaycare(importAnamnesisData.daycareValue()));
     anamnesis.setInDaycareSince(
         approximateInDaycareSince(importAnamnesisData.daycareValue(), examinationDate));
     anamnesis.setPreliminaryCourse(importAnamnesisData.preliminaryCourse());
@@ -343,6 +357,7 @@ public class SchoolEntryService {
     anamnesis.setU7a(mapBooleanOrNull(importAnamnesisData.u7a()));
     anamnesis.setU8(mapBooleanOrNull(importAnamnesisData.u8()));
     anamnesis.setU9(mapBooleanOrNull(importAnamnesisData.u9()));
+    anamnesis.setInGermanySince(importAnamnesisData.inGermanySince());
     return anamnesis;
   }
 
@@ -377,9 +392,16 @@ public class SchoolEntryService {
     return AnamnesisMapper.mapToDomain(CountryCodeDto.getCountryGroup(group));
   }
 
+  private static Boolean mapToWasInDaycare(int daycareValue) {
+    return switch (daycareValue) {
+      case 0 -> false;
+      case 1, 2, 3 -> true;
+      default -> null;
+    };
+  }
+
   private static LocalDate approximateInDaycareSince(int daycareValue, LocalDate examinationDate) {
     return switch (daycareValue) {
-        // TODO ISSUE-6120 map 0 (child hasn't been in daycare)
       case 1 -> examinationDate.minus(Period.ofMonths(9));
       case 2 -> examinationDate.minus(Period.ofMonths(27));
       case 3 -> examinationDate.minus(Period.ofMonths(45));
@@ -408,23 +430,6 @@ public class SchoolEntryService {
     };
   }
 
-  private Label getSpecialNeedsLabel() {
-    return findSystemLabelOrThrow(SPECIAL_NEEDS_LABEL_NAME);
-  }
-
-  private Label getInformationBlockLabel() {
-    return findSystemLabelOrThrow(INFORMATION_BLOCK_LABEL_NAME);
-  }
-
-  private Label findSystemLabelOrThrow(String labelName) {
-    return labelRepository
-        .findByName(labelName)
-        .orElseThrow(
-            () ->
-                new IllegalStateException(
-                    "System-populated label %s is missing".formatted(labelName)));
-  }
-
   private SchoolEntryProcedure saveSchoolEntryProcedure(
       UUID childIdFromCentralFile,
       List<UUID> custodianIdsFromCentralFile,
@@ -436,7 +441,11 @@ public class SchoolEntryService {
       LocalDate examinationDate,
       ProcedureStatus initialProcedureStatus,
       Anamnesis anamnesis,
-      VaccinationStatus vaccinationStatus) {
+      VaccinationStatus vaccinationStatus,
+      EyeExaminationResult eyeExaminationResult,
+      HearingTestResult hearingTestResult,
+      SopessExaminationResult sopessExaminationResult,
+      DevelopmentScreening developmentScreening) {
     SchoolEntryProcedure schoolEntryProcedure = new SchoolEntryProcedure();
     schoolEntryProcedure.updateProcedureStatus(initialProcedureStatus, clock, auditLogger);
     schoolEntryProcedure.setProcedureType(type);
@@ -451,10 +460,10 @@ public class SchoolEntryService {
       buildParent(custodianId, schoolEntryProcedure);
     }
 
-    schoolEntryProcedure.setHearingTestResult(new HearingTestResult());
-    schoolEntryProcedure.setEyeExaminationResult(new EyeExaminationResult());
-    schoolEntryProcedure.setSopessExaminationResult(new SopessExaminationResult());
-    schoolEntryProcedure.setDevelopmentScreeningResult(new DevelopmentScreening());
+    schoolEntryProcedure.setHearingTestResult(hearingTestResult);
+    schoolEntryProcedure.setEyeExaminationResult(eyeExaminationResult);
+    schoolEntryProcedure.setSopessExaminationResult(sopessExaminationResult);
+    schoolEntryProcedure.setDevelopmentScreeningResult(developmentScreening);
     schoolEntryProcedure.setVaccinationStatus(vaccinationStatus);
     schoolEntryProcedure.setAnamnesis(anamnesis);
     schoolEntryProcedure.setWaitingRoom(new WaitingRoom());
@@ -523,6 +532,12 @@ public class SchoolEntryService {
       AppointmentType appointmentType,
       UUID locationId) {
 
+    if (procedure.hasBeenClosed()) {
+      log.info(
+          "Returning an empty list of free appointments, because the procedure has been closed before.");
+      return List.of();
+    }
+
     UUID procedureLocationId = getAppointmentLocation(procedure);
     if (appointmentBlockProperties.getLocationSelectionMode() != LocationSelectionMode.NONE
         && procedureLocationId == null
@@ -571,8 +586,7 @@ public class SchoolEntryService {
 
     boolean isSpecialNeedsLabelInRequest =
         requestedLabelIds != null
-            && labelRepository.existsByNameAndExternalIdIn(
-                SPECIAL_NEEDS_LABEL_NAME, requestedLabelIds);
+            && labelService.contains(requestedLabelIds, SPECIAL_NEEDS_LABEL_NAME);
     log.debug("Check for special needs label in request returned {}", isSpecialNeedsLabelInRequest);
 
     boolean procedureHasSpecialNeedsLabelInPersistence =
@@ -614,7 +628,7 @@ public class SchoolEntryService {
     Pdf invitation =
         invitationGenerator.generateInvitation(
             accessCode, childData, start, getAppointmentLocation(procedure));
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         procedure,
         APPOINTMENT_MODIFIED,
         "Termin %s zu Vorgang zugewiesen"
@@ -727,6 +741,30 @@ public class SchoolEntryService {
     }
   }
 
+  public byte[] zipInvitationsForProcedures(List<UUID> procedureIds) throws IOException {
+    List<File> files =
+        schoolEntryProcedureRepository.findInvitationLettersForProcedures(
+            procedureIds, APPOINTMENT_MODIFIED.name());
+
+    if (procedureIds.size() != files.size()) {
+      throw new BadRequestException(
+          "Unexpected number of invitations (possible causes: procedures not found, duplicate procedure ids, procedures without appointment)");
+    }
+
+    try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ZipOutputStream zip = new ZipOutputStream(out)) {
+      for (int i = 0; i < files.size(); i++) {
+        File file = files.get(i);
+        ZipEntry nextEntry = new ZipEntry("%02d_%s".formatted(i + 1, file.getFileName()));
+        zip.putNextEntry(nextEntry);
+        zip.write(file.getFileContent().getContent());
+        zip.closeEntry();
+      }
+      zip.finish();
+      return out.toByteArray();
+    }
+  }
+
   private ProcedurePageSpec createPageSpec(
       ProcedurePaginationAndSortParameters paginationAndSortParameters) {
     return ProcedureMapper.mapToPageSpec(
@@ -791,11 +829,17 @@ public class SchoolEntryService {
         procedure.getDeceased(),
         procedure.getSchoolYear(),
         procedure.getProcedureStatus(),
-        isProcedureDeletable(procedure),
+        procedure.isDeletable(),
         procedure.getCreatedAt(),
         procedure.getModifiedAt(),
         procedure.getWaitingRoom(),
-        procedure.getschoolInfoLetterCreatedAt());
+        procedure.getschoolInfoLetterCreatedAt(),
+        hasInformationBlock(procedure),
+        procedure.hasBeenClosed());
+  }
+
+  private static boolean hasInformationBlock(SchoolEntryProcedure procedure) {
+    return procedure.hasLabel(INFORMATION_BLOCK_LABEL_NAME);
   }
 
   private SchoolDto getSchool(SchoolEntryProcedure procedure) {
@@ -816,16 +860,6 @@ public class SchoolEntryService {
     return new LocationDto(locationId, contact.name());
   }
 
-  private boolean isProcedureDeletable(SchoolEntryProcedure procedure) {
-    return procedure.getAppointment() == null
-        && !procedure.getAnamnesis().hasEdits()
-        && !procedure.getVaccinationStatus().hasEdits()
-        && !procedure.getEyeExaminationResult().hasEdits()
-        && !procedure.getHearingTestResult().hasEdits()
-        && !procedure.getSopessExaminationResult().hasEdits()
-        && !procedure.getDevelopmentScreeningResult().hasEdits();
-  }
-
   public SchoolEntryProcedure updateProcedure(
       SchoolEntryProcedure procedure, UpdateProcedureRequest request) {
 
@@ -854,6 +888,10 @@ public class SchoolEntryService {
     }
     if (appointment != null
         && hasAppointmentChanged(procedure, appointment.start(), appointment.end())) {
+      if (procedure.hasBeenClosed()) {
+        throw new BadRequestException(
+            "An appointment cannot be updated, when the procedure has been closed before.");
+      }
       AppointmentType appointmentType =
           computeAppointmentType(procedure, requestedType, requestedLabelIds);
       updateAppointment(appointment.start(), appointment.end(), procedure, appointmentType);
@@ -871,7 +909,7 @@ public class SchoolEntryService {
         procedure.getSchoolYear(),
         ProcedureMapper.mapIntegerToYear(request.schoolYear()));
 
-    ProgressEntryUtil.addProgressEntry(procedure, PROCEDURE_MODIFIED);
+    progressEntryUtil.addProgressEntry(procedure, PROCEDURE_MODIFIED);
 
     schoolEntryProcedureRepository.flush();
     return procedure;
@@ -883,18 +921,18 @@ public class SchoolEntryService {
       log.info("Modifying procedure type {} to {}", persistedType, requestedType);
       Validator.validateUpdateProcedureType(procedure, requestedType);
       procedure.setProcedureType(requestedType);
-      ProgressEntryUtil.addProgressEntry(procedure, PROCEDURE_TYPE_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, PROCEDURE_TYPE_MODIFIED);
     }
   }
 
   private void updateLabels(
       SchoolEntryProcedure procedure, List<UUID> persistedLabelIds, List<UUID> requestedLabelIds) {
     if (!CollectionUtils.isEqualCollection(requestedLabelIds, persistedLabelIds)) {
-      List<Label> labels = labelRepository.findAllByExternalIdInOrderById(requestedLabelIds);
+      List<Label> labels = labelService.findByExternalIds(requestedLabelIds);
       Validator.validateLabelsExist(
           requestedLabelIds, labels.stream().map(Label::getExternalId).toList());
       procedure.setLabels(labels);
-      ProgressEntryUtil.addProgressEntry(procedure, LABELS_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, LABELS_MODIFIED);
     }
   }
 
@@ -919,7 +957,7 @@ public class SchoolEntryService {
       }
       log.info("Modifying school {} to {}", procedure.getSchoolId(), requestedSchoolId);
       procedure.setSchoolId(requestedSchoolId);
-      ProgressEntryUtil.addProgressEntry(procedure, SCHOOL_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, SCHOOL_MODIFIED);
     }
   }
 
@@ -1031,7 +1069,7 @@ public class SchoolEntryService {
 
     if (!updatedFileStateId.equals(currentFileStateId)) {
       child.setCentralFileStateId(updatedFileStateId);
-      ProgressEntryUtil.addProgressEntry(procedure, CHILD_MODIFIED);
+      progressEntryUtil.addProgressEntry(procedure, CHILD_MODIFIED);
       personRepository.flush();
     }
   }
@@ -1048,7 +1086,7 @@ public class SchoolEntryService {
           case PARENT -> CUSTODIAN_SYNCED_WITH_CENTRAL_FILE;
           default -> throw new IllegalStateException("Unknown person type");
         };
-    ProgressEntryUtil.addProgressEntry(procedure, progressEntryType);
+    progressEntryUtil.addProgressEntry(procedure, progressEntryType);
 
     personRepository.flush();
     return procedure;
@@ -1072,7 +1110,7 @@ public class SchoolEntryService {
     }
     buildParent(centralFileId, procedure);
 
-    ProgressEntryUtil.addProgressEntry(procedure, CUSTODIAN_ADDED);
+    progressEntryUtil.addProgressEntry(procedure, CUSTODIAN_ADDED);
     schoolEntryProcedureRepository.flush();
   }
 
@@ -1082,7 +1120,7 @@ public class SchoolEntryService {
 
     if (!newCentralFileStateId.equals(centralFileStateId)) {
       person.setCentralFileStateId(newCentralFileStateId);
-      ProgressEntryUtil.addProgressEntry(person.getProcedure(), CUSTODIAN_MODIFIED);
+      progressEntryUtil.addProgressEntry(person.getProcedure(), CUSTODIAN_MODIFIED);
       schoolEntryProcedureRepository.flush();
     }
   }
@@ -1099,7 +1137,7 @@ public class SchoolEntryService {
             .orElseThrow(notFoundException(Person.class, centralFileStateId));
     procedure.getRelatedPersons().remove(person);
 
-    ProgressEntryUtil.addProgressEntry(procedure, CUSTODIAN_REMOVED);
+    progressEntryUtil.addProgressEntry(procedure, CUSTODIAN_REMOVED);
     schoolEntryProcedureRepository.flush();
   }
 
@@ -1262,16 +1300,33 @@ public class SchoolEntryService {
 
   public Map<PersonKeyAttributes, List<ProcedureWithChildData>> searchForMergeCandidates(
       Set<PersonKeyAttributes> searchAttributes) {
+    Specification<SchoolEntryProcedure> openProcedures =
+        (root, query, criteriaBuilder) ->
+            criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.OPEN);
     Map<PersonKeyAttributes, List<SchoolEntryProcedure>> proceduresByPersons =
-        procedureSearchService.searchOpenProceduresByPersons(searchAttributes, PersonType.PATIENT);
+        procedureSearchService.searchProceduresByPersons(
+            searchAttributes, PersonType.PATIENT, openProcedures, Person.class);
     return personClient.augmentWithChildData(proceduresByPersons);
   }
 
+  public Map<PersonKeyAttributes, List<SchoolEntryProcedure>>
+      searchForMergeCandidatesForPastProcedures(
+          Set<PersonKeyAttributes> searchAttributes, Year schoolYear) {
+    Specification<SchoolEntryProcedure> proceduresWithNoOrEqualSchoolYear =
+        (root, query, criteriaBuilder) -> {
+          Path<Year> schoolYearPath = root.get(SchoolEntryProcedure_.schoolYear);
+          return criteriaBuilder.or(
+              schoolYearPath.isNull(), criteriaBuilder.equal(schoolYearPath, schoolYear));
+        };
+    return procedureSearchService.searchProceduresByPersons(
+        searchAttributes, PersonType.PATIENT, proceduresWithNoOrEqualSchoolYear, Person.class);
+  }
+
   void updateHearingTestResult(
       HearingTestResult persistedHearingTestResult, HearingTestResult newHearingTestResult) {
     copyValues(newHearingTestResult, persistedHearingTestResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedHearingTestResult.getProcedure(), HEARING_TEST_MODIFIED);
 
     hearingTestResultRepository.flush();
@@ -1289,7 +1344,7 @@ public class SchoolEntryService {
       EyeExaminationResult newEyeExaminationResult) {
     copyValues(newEyeExaminationResult, persistedEyeExaminationResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedEyeExaminationResult.getProcedure(), EYE_EXAMINATION_MODIFIED);
 
     eyeExaminationResultRepository.flush();
@@ -1316,7 +1371,7 @@ public class SchoolEntryService {
       SopessExaminationResult newSopessExaminationResult) {
     copyValues(newSopessExaminationResult, persistedSopessExaminationResult);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedSopessExaminationResult.getProcedure(), SOPESS_EXAMINATION_MODIFIED);
 
     sopessExaminationResultRepository.flush();
@@ -1385,7 +1440,7 @@ public class SchoolEntryService {
       persistedDevelopmentScreeningResult.setBmiPercentile(dto.getBmiPercentile());
     }
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedDevelopmentScreeningResult.getProcedure(), DEVELOPMENT_SCREENING_MODIFIED);
 
     developmentScreeningResultRepository.flush();
@@ -1434,7 +1489,7 @@ public class SchoolEntryService {
       VaccinationStatus persistedVaccinationStatus, VaccinationStatus newVaccinationStatus) {
     copyValues(newVaccinationStatus, persistedVaccinationStatus);
 
-    ProgressEntryUtil.addProgressEntry(
+    progressEntryUtil.addProgressEntry(
         persistedVaccinationStatus.getProcedure(), VACCINATION_STATUS_MODIFIED);
 
     vaccinationStatusRepository.flush();
@@ -1468,7 +1523,7 @@ public class SchoolEntryService {
   void updateAnamnesis(Anamnesis persistedAnamnesis, Anamnesis newAnamnesis) {
     copyValues(newAnamnesis, persistedAnamnesis);
 
-    ProgressEntryUtil.addProgressEntry(persistedAnamnesis.getProcedure(), ANAMNESIS_MODIFIED);
+    progressEntryUtil.addProgressEntry(persistedAnamnesis.getProcedure(), ANAMNESIS_MODIFIED);
 
     anamnesisRepository.flush();
   }
@@ -1503,6 +1558,7 @@ public class SchoolEntryService {
     toAnamnesis.setResponsiblePhysician(fromAnamnesis.getResponsiblePhysician());
     toAnamnesis.setNumberOfSiblings(fromAnamnesis.getNumberOfSiblings());
     toAnamnesis.setSiblingsBirthYears(fromAnamnesis.getSiblingsBirthYears());
+    toAnamnesis.setWasInDaycare(fromAnamnesis.getWasInDaycare());
     toAnamnesis.setInDaycareSince(fromAnamnesis.getInDaycareSince());
     toAnamnesis.setDaycareName(fromAnamnesis.getDaycareName());
     toAnamnesis.setSchoolName(fromAnamnesis.getSchoolName());
@@ -1567,50 +1623,19 @@ public class SchoolEntryService {
       return List.of();
     }
 
-    List<UUID> procedureIds = mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
-
     try {
-      Assert.isTrue(
-          !StreamUtil.hasDuplicates(procedureIds.stream()),
-          "Merge data contains duplicated procedure IDs");
-
-      Map<UUID, SchoolEntryProcedure> procedures =
-          schoolEntryProcedureRepository
-              .findByExternalIdsForUpdate(procedureIds)
-              .collect(StreamUtil.toLinkedHashMap(SchoolEntryProcedure::getExternalId));
-
-      List<ChildUpdate> childUpdates = new ArrayList<>();
-      for (MergeProcedureData mergeData : mergeDataList) {
-        UUID procedureId = mergeData.procedureId();
-        SchoolEntryProcedure procedure =
-            Optional.ofNullable(procedures.get(procedureId))
-                .orElseThrow(procedureNotFoundException(procedureId));
+      List<ResolvedMergeProcedureData> resolvedMergeDataList = resolveMergeData(mergeDataList);
+      List<UUID> failedProcedureIds = personClient.updateChildren(resolvedMergeDataList);
 
-        childUpdates.add(
-            new ChildUpdate(
-                procedure,
-                mergeData.placeOfBirth(),
-                mergeData.countryOfBirth(),
-                mergeData.phoneNumber()));
-      }
+      resolvedMergeDataList.removeIf(
+          mergeData -> failedProcedureIds.contains(mergeData.procedure().getExternalId()));
 
-      List<UUID> failedProcedureIds = personClient.updateChildren(childUpdates);
+      createCustodiansInBulk(resolvedMergeDataList);
 
-      for (MergeProcedureData mergeData : mergeDataList) {
-        UUID procedureId = mergeData.procedureId();
-
-        SchoolEntryProcedure procedure =
-            Optional.ofNullable(procedures.get(procedureId))
-                .orElseThrow(procedureNotFoundException(procedureId));
-
-        if (failedProcedureIds.contains(procedureId)) {
-          log.debug("Skipping merge of procedure {}. Child update failed", procedureId);
-          continue;
-        }
-
-        mergeDataForProcedure(procedure, mergeData, schoolId, locationId, schoolYear);
-        updateProcedureTypeWithSuggestion(procedure);
-        addProgressEntryForMerge(procedure, importType);
+      for (ResolvedMergeProcedureData mergeData : resolvedMergeDataList) {
+        mergeDataForProcedure(mergeData, schoolId, locationId, schoolYear);
+        updateProcedureTypeWithSuggestion(mergeData.procedure());
+        addProgressEntryForMerge(mergeData.procedure(), importType);
       }
 
       schoolEntryProcedureRepository.flush();
@@ -1618,39 +1643,101 @@ public class SchoolEntryService {
       return failedProcedureIds;
     } catch (Exception e) {
       log.error("Error during merge of data.", e);
-      return procedureIds;
+      return mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
+    }
+  }
+
+  private List<ResolvedMergeProcedureData> resolveMergeData(
+      List<MergeProcedureData> mergeDataList) {
+    List<ResolvedMergeProcedureData> merges;
+    List<UUID> procedureIds = mergeDataList.stream().map(MergeProcedureData::procedureId).toList();
+
+    Assert.isTrue(
+        !StreamUtil.hasDuplicates(procedureIds.stream()),
+        "Merge data contains duplicated procedure IDs");
+
+    Map<UUID, SchoolEntryProcedure> procedures =
+        schoolEntryProcedureRepository
+            .findByExternalIdsForUpdate(procedureIds)
+            .collect(StreamUtil.toLinkedHashMap(SchoolEntryProcedure::getExternalId));
+
+    merges =
+        mergeDataList.stream()
+            .map(
+                mergeProcedureData -> {
+                  SchoolEntryProcedure procedure =
+                      getProcedureOrThrow(procedures, mergeProcedureData.procedureId());
+                  return new ResolvedMergeProcedureData(
+                      procedure,
+                      mergeProcedureData.placeOfBirth(),
+                      mergeProcedureData.countryOfBirth(),
+                      mergeProcedureData.custodians(),
+                      mergeProcedureData.phoneNumber(),
+                      mergeProcedureData.isEntryLevel(),
+                      mergeProcedureData.isEarlyExamination());
+                })
+            .collect(StreamUtil.toModifiableList());
+    return merges;
+  }
+
+  private static List<ImportCustodianDataWithProcedure> collectCustodiansWithProcedure(
+      List<ResolvedMergeProcedureData> merges) {
+    return merges.stream()
+        .flatMap(
+            mergeData ->
+                mergeData.custodians().stream()
+                    .map(
+                        custodian ->
+                            new ImportCustodianDataWithProcedure(custodian, mergeData.procedure())))
+        .toList();
+  }
+
+  private void createCustodiansInBulk(List<ResolvedMergeProcedureData> mergeDataList) {
+    List<ImportCustodianDataWithProcedure> custodiansWithProcedure =
+        collectCustodiansWithProcedure(mergeDataList);
+    if (custodiansWithProcedure.isEmpty()) {
+      return;
+    }
+    List<ImportCustodianData> custodians =
+        custodiansWithProcedure.stream().map(ImportCustodianDataWithProcedure::custodian).toList();
+    List<UUID> custodianIds = personClient.createCustodiansInCentralFile(custodians);
+    Assert.isTrue(
+        custodiansWithProcedure.size() == custodianIds.size(),
+        () ->
+            "Unexpected number of created custodian. Expected %d but got %d"
+                .formatted(custodiansWithProcedure.size(), custodianIds.size()));
+    for (int i = 0; i < custodianIds.size(); i++) {
+      ImportCustodianDataWithProcedure custodian = custodiansWithProcedure.get(i);
+      buildParent(custodianIds.get(i), custodian.procedure());
     }
   }
 
-  private static void addProgressEntryForMerge(
-      SchoolEntryProcedure procedure, ImportType importType) {
+  private static SchoolEntryProcedure getProcedureOrThrow(
+      Map<UUID, SchoolEntryProcedure> procedures, UUID procedureId) {
+    return Optional.ofNullable(procedures.get(procedureId))
+        .orElseThrow(procedureNotFoundException(procedureId));
+  }
+
+  private void addProgressEntryForMerge(SchoolEntryProcedure procedure, ImportType importType) {
     SchoolEntrySystemProgressEntryType progressEntryType =
         switch (importType) {
           case CITIZEN_LIST -> MERGED_DATA_FROM_CITIZEN_LIST;
           case SCHOOL_LIST -> MERGED_DATA_FROM_SCHOOL_LIST;
           case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
         };
-    ProgressEntryUtil.addProgressEntry(procedure, progressEntryType);
+    progressEntryUtil.addProgressEntry(procedure, progressEntryType);
   }
 
   private void mergeDataForProcedure(
-      SchoolEntryProcedure procedure,
-      MergeProcedureData mergeData,
-      UUID schoolId,
-      UUID locationId,
-      Year schoolYear) {
-    if (mergeData.custodians() != null && !mergeData.custodians().isEmpty()) {
-      personClient
-          .createCustodiansInCentralFile(mergeData.custodians())
-          .forEach(custodianId -> buildParent(custodianId, procedure));
-    }
+      ResolvedMergeProcedureData mergeData, UUID schoolId, UUID locationId, Year schoolYear) {
+    SchoolEntryProcedure procedure = mergeData.procedure();
 
     if (mergeData.isEntryLevel() != null) {
       procedure.setEntryLevel(mergeData.isEntryLevel());
     }
 
     if (mergeData.isEarlyExamination() != null && mergeData.isEarlyExamination()) {
-      Label specialNeedsLabel = getSpecialNeedsLabel();
+      Label specialNeedsLabel = labelService.getSpecialNeedsLabel();
 
       List<Label> labels = procedure.getLabels();
       if (!labels.contains(specialNeedsLabel)) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
index ecdea080d..5635a6dd2 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/Validator.java
@@ -22,6 +22,8 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.AnamnesisDto;
+import de.eshg.schoolentry.api.anamnesis.DaycareAndSchoolInfoDto;
+import de.eshg.schoolentry.api.citizen.CitizenAnamnesisDto;
 import de.eshg.schoolentry.business.model.ChildData;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
@@ -508,6 +510,12 @@ public class Validator {
 
   public void validateAnamnesis(AnamnesisDto anamnesis) {
     validateDateTodayOrPast(anamnesis.migrationBackground().inGermanySince());
+    validateDayCareInfoConsistency(anamnesis.daycareAndSchoolInfo());
+  }
+
+  public void validateCitizenAnamnesis(CitizenAnamnesisDto anamnesis) {
+    validateDateTodayOrPast(anamnesis.migrationBackground().inGermanySince());
+    validateDayCareInfoConsistency(anamnesis.daycareAndSchoolInfo());
   }
 
   void validateDateTodayOrPast(LocalDate date) {
@@ -519,6 +527,14 @@ public class Validator {
     }
   }
 
+  void validateDayCareInfoConsistency(DaycareAndSchoolInfoDto daycareInfo) {
+    if (!Boolean.TRUE.equals(daycareInfo.wasInDaycare())
+        && (daycareInfo.inDaycareSince() != null || daycareInfo.daycareName() != null)) {
+      throw new BadRequestException(
+          "In daycare info provided despite child not having been in daycare");
+    }
+  }
+
   void validateAppointmentChanges(SchoolEntryProcedure schoolEntryProcedure) {
     if (schoolEntryProcedure.getAppointmentChangesByCitizen() >= MAX_ALLOWED_APPOINTMENT_CHANGES) {
       throw new BadRequestException(
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java
new file mode 100644
index 000000000..3d81e4dcb
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/DownloadInvitationsBulkRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.api;
+
+import jakarta.validation.constraints.NotEmpty;
+import java.util.List;
+import java.util.UUID;
+
+public record DownloadInvitationsBulkRequest(@NotEmpty List<UUID> procedureIds) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
index 7ad0e4ec1..1d4c8f95e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/ProcedureDetailsDto.java
@@ -37,5 +37,7 @@ public record ProcedureDetailsDto(
     @NotNull Instant createdAt,
     @NotNull Instant modifiedAt,
     @Valid WaitingRoomDto waitingRoom,
-    Instant schoolInfoLetterCreatedAt)
+    Instant schoolInfoLetterCreatedAt,
+    @NotNull boolean hasInformationBlock,
+    @NotNull boolean hasBeenClosed)
     implements ProcedureBaseDto {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
index 659069f46..0c9d20ea9 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/anamnesis/DaycareAndSchoolInfoDto.java
@@ -10,8 +10,8 @@ import java.time.LocalDate;
 
 @Schema(name = "DaycareAndSchoolInfo")
 public record DaycareAndSchoolInfoDto(
-    LocalDate inDaycareSince, String daycareName, String schoolName) {
+    Boolean wasInDaycare, LocalDate inDaycareSince, String daycareName, String schoolName) {
   public DaycareAndSchoolInfoDto() {
-    this(null, null, null);
+    this(null, null, null, null);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
index 5bf224e5c..73e57f78e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenAnamnesisDto.java
@@ -50,4 +50,20 @@ public record CitizenAnamnesisDto(
         new InterestsAndSportsInfoDto(),
         null);
   }
+
+  public CitizenAnamnesisDto(DaycareAndSchoolInfoDto daycareAndSchoolInfo) {
+    this(
+        new CitizenMigrationBackgroundDto(),
+        null,
+        null,
+        new PromotionBeforeSchoolEntryDto(),
+        new CitizenAdditionalChildInfoDto(),
+        daycareAndSchoolInfo,
+        new FamilyHistoryInfoDto(),
+        new DevelopmentInfoDto(),
+        new IllnessAndAccidentInfoDto(),
+        new PromotionTherapyAndAidInfoDto(),
+        new InterestsAndSportsInfoDto(),
+        null);
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
index c88fc2e5a..86f3e4854 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/CitizenMigrationBackgroundDto.java
@@ -24,4 +24,8 @@ public record CitizenMigrationBackgroundDto(
     @Schema(description = "Country of birth of the second parent", example = "DEU")
         CountryCodeDto countryOfBirthSecondParent,
     @Schema(description = "Date from which the child lives in Germany", example = "2000-01-01")
-        LocalDate inGermanySince) {}
+        LocalDate inGermanySince) {
+  public CitizenMigrationBackgroundDto() {
+    this(null, null, null, null, null, null, null);
+  }
+}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java
new file mode 100644
index 000000000..67a7e23c6
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/citizen/GetOpeningHoursResponse.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.api.citizen;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+public record GetOpeningHoursResponse(@NotNull List<String> de, @NotNull List<String> en) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
index fe770dabc..d7de99f92 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportAnamnesisData.java
@@ -5,6 +5,8 @@
 
 package de.eshg.schoolentry.business.model;
 
+import java.time.LocalDate;
+
 public record ImportAnamnesisData(
     int siblings,
     int nationalityChild,
@@ -30,4 +32,5 @@ public record ImportAnamnesisData(
     Boolean u7,
     Boolean u7a,
     Boolean u8,
-    Boolean u9) {}
+    Boolean u9,
+    LocalDate inGermanySince) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java
new file mode 100644
index 000000000..c7354018c
--- /dev/null
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportCustodianDataWithProcedure.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.schoolentry.business.model;
+
+import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+
+public record ImportCustodianDataWithProcedure(
+    ImportCustodianData custodian, SchoolEntryProcedure procedure) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
index 4c8e60882..dc183ea5e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportPastProcedureData.java
@@ -5,7 +5,16 @@
 
 package de.eshg.schoolentry.business.model;
 
+import de.eshg.schoolentry.domain.model.DevelopmentScreening;
+import de.eshg.schoolentry.domain.model.EyeExaminationResult;
+import de.eshg.schoolentry.domain.model.HearingTestResult;
+import de.eshg.schoolentry.domain.model.SopessExaminationResult;
+
 public record ImportPastProcedureData(
     ImportProcedureData procedureData,
     ImportAnamnesisData anamnesisData,
-    ImportVaccinationStatusData vaccinationStatusData) {}
+    ImportVaccinationStatusData vaccinationStatusData,
+    EyeExaminationResult eyeExaminationResult,
+    HearingTestResult hearingTestResult,
+    SopessExaminationResult sopessExaminationData,
+    DevelopmentScreening developmentScreeningData) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
index 050a3403a..aaf8ed2af 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ImportVaccinationStatusData.java
@@ -6,19 +6,19 @@
 package de.eshg.schoolentry.business.model;
 
 public record ImportVaccinationStatusData(
-    int vaccinationScheme,
-    int tetanus,
-    int diphteria,
-    int pertussis,
-    int polio,
-    int hib,
-    int hepatitisB,
-    int mmr,
-    int varicella,
-    int meningococcusC,
-    int pneumococcus,
-    int hepatitisA,
-    int tbe,
-    int rota,
-    int meningococcusB,
+    Integer vaccinationScheme,
+    Integer tetanus,
+    Integer diphteria,
+    Integer pertussis,
+    Integer polio,
+    Integer hib,
+    Integer hepatitisB,
+    Integer mmr,
+    Integer varicella,
+    Integer meningococcusC,
+    Integer pneumococcus,
+    Integer hepatitisA,
+    Integer tbe,
+    Integer rota,
+    Integer meningococcusB,
     Boolean perkombiHbv) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
index 561166337..47e11c4d2 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ProcedureDetailsData.java
@@ -39,4 +39,6 @@ public record ProcedureDetailsData(
     Instant createdAt,
     Instant modifiedAt,
     WaitingRoom waitingRoom,
-    Instant schoolInfoLetterCreatedAt) {}
+    Instant schoolInfoLetterCreatedAt,
+    boolean hasInformationBlock,
+    boolean hasBeenClosed) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
similarity index 53%
rename from backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java
rename to backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
index c7ec17661..ddbd787a7 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/ChildUpdate.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/business/model/ResolvedMergeProcedureData.java
@@ -3,13 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.schoolentry.client;
+package de.eshg.schoolentry.business.model;
 
 import de.eshg.lib.common.CountryCode;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import java.util.List;
 
-public record ChildUpdate(
+public record ResolvedMergeProcedureData(
     SchoolEntryProcedure procedure,
     String placeOfBirth,
     CountryCode countryOfBirth,
-    String phoneNumber) {}
+    List<ImportCustodianData> custodians,
+    String phoneNumber,
+    Boolean isEntryLevel,
+    Boolean isEarlyExamination) {}
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
index 3f691b497..8335dd063 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/client/PersonClient.java
@@ -113,9 +113,9 @@ public class PersonClient {
     return id;
   }
 
-  public List<UUID> createCustodiansInCentralFile(List<ImportCustodianData> custodianData) {
+  public List<UUID> createCustodiansInCentralFile(List<ImportCustodianData> custodians) {
     List<AddPersonFileStateRequest> requests =
-        mapToPersonFileStateRequest(custodianData, DataOrigin.DATA_IMPORT);
+        mapToPersonFileStateRequest(custodians, DataOrigin.DATA_IMPORT);
     AddPersonFileStatesResponse response =
         personApi.addPersonFileStates(new AddPersonFileStatesRequest(requests));
     return response.personFileStateIds();
@@ -405,19 +405,24 @@ public class PersonClient {
         fileStateId, PersonMapper.mapToPersonDetailsDto(request));
   }
 
-  public List<UUID> updateChildren(List<ChildUpdate> childUpdates) {
-    if (childUpdates.isEmpty()) {
+  public List<UUID> updateChildren(List<ResolvedMergeProcedureData> mergeDataList) {
+    if (mergeDataList.isEmpty()) {
       return List.of();
     }
 
-    Map<UUID, AddPersonFileStateResponse> existingFileStates = getPersonFileStates(childUpdates);
+    List<UUID> childIds =
+        mergeDataList.stream()
+            .map(ResolvedMergeProcedureData::procedure)
+            .map(SchoolEntryProcedure::getChildIdFromCentralFile)
+            .toList();
+    Map<UUID, AddPersonFileStateResponse> existingFileStates = getPersonFileStates(childIds);
 
     record BulkUpdateRequest(UUID childIdFromCentralFile, PersonDetailsDto update, long version) {}
 
     List<BulkUpdateRequest> updates = new ArrayList<>();
     Map<UUID, SchoolEntryProcedure> updatedProceduresByChildId = new LinkedHashMap<>();
 
-    for (ChildUpdate childUpdate : childUpdates) {
+    for (ResolvedMergeProcedureData childUpdate : mergeDataList) {
       SchoolEntryProcedure procedure = childUpdate.procedure();
       UUID childIdFromCentralFile = procedure.getChildIdFromCentralFile();
       AddPersonFileStateResponse child = existingFileStates.get(childIdFromCentralFile);
@@ -493,16 +498,9 @@ public class PersonClient {
     return resolveProcedureIds(failedPersonIds, updatedProceduresByChildId);
   }
 
-  private Map<UUID, AddPersonFileStateResponse> getPersonFileStates(
-      List<ChildUpdate> childUpdates) {
-    List<UUID> childIds =
-        childUpdates.stream()
-            .map(ChildUpdate::procedure)
-            .map(SchoolEntryProcedure::getChildIdFromCentralFile)
-            .toList();
-
+  private Map<UUID, AddPersonFileStateResponse> getPersonFileStates(List<UUID> personFileStateIds) {
     return personApi
-        .getPersonFileStates(new GetPersonFileStatesRequest(childIds))
+        .getPersonFileStates(new GetPersonFileStatesRequest(personFileStateIds))
         .personFileStates()
         .stream()
         .collect(StreamUtil.toLinkedHashMap(AddPersonFileStateResponse::id));
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
index 75c2817a8..d96b64ba8 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryFeature.java
@@ -6,7 +6,6 @@
 package de.eshg.schoolentry.config;
 
 public enum SchoolEntryFeature {
-  CLOSE_PROCEDURE,
-  REOPEN_PROCEDURE,
   IMPORT_PAST_PROCEDURES,
+  BULK_DOWNLOAD_INVITATIONS,
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
index a876f9589..72f5c5cab 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/config/SchoolEntryProperties.java
@@ -7,9 +7,12 @@ package de.eshg.schoolentry.config;
 
 import de.eshg.testhelper.ResettableProperties;
 import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import java.net.URI;
 import java.time.MonthDay;
 import java.time.Period;
+import java.util.List;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.validation.annotation.Validated;
 
@@ -23,6 +26,9 @@ public final class SchoolEntryProperties implements ResettableProperties {
   private boolean maxDateOfBirthForRegularSchoolEntryIsInclusive;
   private @NotNull Integer maxNumberOfImportRows = 10_000;
   private boolean directProcedureTypeAssignmentOnImport;
+  private @NotNull URI privacyNoticeLocation;
+  private @NotNull URI privacyPolicyLocation;
+  private @NotNull SchoolEntryProperties.OpeningHours openingHours;
 
   public Period getBulkCreateAppointmentsMinLeadTime() {
     return bulkCreateAppointmentsMinLeadTime;
@@ -75,6 +81,32 @@ public final class SchoolEntryProperties implements ResettableProperties {
     this.directProcedureTypeAssignmentOnImport = directProcedureTypeAssignmentOnImport;
   }
 
+  public URI getPrivacyNoticeLocation() {
+    return privacyNoticeLocation;
+  }
+
+  public void setPrivacyNoticeLocation(URI privacyNoticeLocation) {
+    this.privacyNoticeLocation = privacyNoticeLocation;
+  }
+
+  public URI getPrivacyPolicyLocation() {
+    return privacyPolicyLocation;
+  }
+
+  public void setPrivacyPolicyLocation(URI privacyPolicyLocation) {
+    this.privacyPolicyLocation = privacyPolicyLocation;
+  }
+
   public record Citizens(
       @NotNull Period freeAppointmentsMinLeadTime, @NotNull Period freeAppointmentsMaxLeadTime) {}
+
+  public SchoolEntryProperties.OpeningHours getOpeningHours() {
+    return openingHours;
+  }
+
+  public void setOpeningHours(SchoolEntryProperties.OpeningHours openingHours) {
+    this.openingHours = openingHours;
+  }
+
+  public record OpeningHours(@NotEmpty List<String> de, @NotEmpty List<String> en) {}
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
index fc1b3c0c3..1c28538be 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/Anamnesis.java
@@ -100,6 +100,7 @@ public class Anamnesis extends GenericEntity<Long> implements ValidatableEntity
 
   private String responsiblePhysician;
   private LocalDate inDaycareSince;
+  private Boolean wasInDaycare;
   private String daycareName;
   private String schoolName;
   private Boolean spectaclesInFamily;
@@ -632,4 +633,12 @@ public class Anamnesis extends GenericEntity<Long> implements ValidatableEntity
   public void setNumberOfSiblings(Integer numberOfSiblings) {
     this.numberOfSiblings = numberOfSiblings;
   }
+
+  public Boolean getWasInDaycare() {
+    return wasInDaycare;
+  }
+
+  public void setWasInDaycare(Boolean wasInDaycare) {
+    this.wasInDaycare = wasInDaycare;
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
index ca952acec..87aa81ffe 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/model/SchoolEntryProcedure.java
@@ -351,4 +351,14 @@ public class SchoolEntryProcedure
                         systemProgressEntry.getSystemProgressEntryType(),
                         BasicSystemProgressEntryType.CLOSED.name()));
   }
+
+  public boolean isDeletable() {
+    return getAppointment() == null
+        && !getAnamnesis().hasEdits()
+        && !getVaccinationStatus().hasEdits()
+        && !getEyeExaminationResult().hasEdits()
+        && !getHearingTestResult().hasEdits()
+        && !getSopessExaminationResult().hasEdits()
+        && !getDevelopmentScreeningResult().hasEdits();
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
index 916d24653..5c4a61d68 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/repository/SchoolEntryProcedureRepository.java
@@ -5,6 +5,7 @@
 
 package de.eshg.schoolentry.domain.repository;
 
+import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure_;
@@ -71,4 +72,22 @@ public interface SchoolEntryProcedureRepository extends ProcedureRepository<Scho
   @Query(
       "select p.externalId from SchoolEntryProcedure p where p.procedureStatus = de.eshg.lib.procedure.domain.model.ProcedureStatus.CLOSED order by p.id")
   List<UUID> findExternalIdsOfClosedProcedures();
+
+  @Query(
+      """
+      select f from SchoolEntryProcedure p
+      join p.progressEntries pe
+      join pe.file f
+      where p.externalId in :procedureIds
+      and p.appointment is not null
+      and pe.id = (
+        select max(spe.id) from SystemProgressEntry spe
+        where spe.procedureId = p.id
+        and spe.systemProgressEntryType = :systemProgressEntryType
+      )
+      order by f.fileName, f.id
+      """)
+  List<File> findInvitationLettersForProcedures(
+      @Param("procedureIds") List<UUID> procedureIds,
+      @Param("systemProgressEntryType") String systemProgressEntryType);
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
index adebbb306..0712b007f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/domain/specification/SchoolEntryProcedureSpecification.java
@@ -33,7 +33,7 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
 
   private final ProcedureStatus procedureStatusFilter;
   private final ProcedureType procedureTypeFilter;
-  private final UUID schoolIdFilter;
+  private final ArrayList<UUID> schoolIdFilter;
   private final Year schoolYearFilter;
   private final Instant dayOfAppointmentFilter;
   private final Boolean hasAppointmentFilter;
@@ -45,7 +45,7 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
   public SchoolEntryProcedureSpecification(
       ProcedureStatus procedureStatusFilter,
       ProcedureType procedureTypeFilter,
-      UUID schoolIdFilter,
+      ArrayList<UUID> schoolIdFilter,
       Year schoolYearFilter,
       Instant dayOfAppointmentFilter,
       Boolean hasAppointmentFilter,
@@ -82,9 +82,8 @@ public class SchoolEntryProcedureSpecification implements Specification<SchoolEn
               root.get(SchoolEntryProcedure_.procedureType), procedureTypeFilter));
     }
 
-    if (schoolIdFilter != null) {
-      conjunctions.add(
-          criteriaBuilder.equal(root.get(SchoolEntryProcedure_.schoolId), schoolIdFilter));
+    if (schoolIdFilter != null && !schoolIdFilter.isEmpty()) {
+      conjunctions.add(root.get(SchoolEntryProcedure_.schoolId).in(schoolIdFilter));
     }
 
     if (schoolYearFilter != null) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
index 9b55b5538..ee95c2cfb 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenListRowReader.java
@@ -11,6 +11,7 @@ import de.eshg.base.GenderDto;
 import de.eshg.base.SalutationDto;
 import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.business.model.ImportChildData;
@@ -18,8 +19,6 @@ import de.eshg.schoolentry.business.model.ImportCustodianData;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BiConsumer;
-import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
 public class CitizenListRowReader extends RowReader<CitizenListRowValues, CitizenListColumn> {
@@ -63,7 +62,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   @Override
   protected CitizenListRowValues read(ColumnAccessor<CitizenListColumn> col) {
     CitizenListRowValues result = new CitizenListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     if (col.hasColumn(INFORMATION_BLOCK)) {
@@ -77,7 +76,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   }
 
   private ImportChildData readChildData(
-      ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<CitizenListColumn> col, ErrorHandler errorHandler) {
     String lastName = cellAsString(col, LAST_NAME, errorHandler);
     String firstName = cellAsString(col, FIST_NAME, errorHandler);
     AddressData addressData = readAddressData(col, CHILD_ADDRESS_COLUMNS, errorHandler, true);
@@ -90,7 +89,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   }
 
   private List<ImportCustodianData> readCustodiansData(
-      ColumnAccessor<CitizenListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<CitizenListColumn> col, ErrorHandler errorHandler) {
     List<ImportCustodianData> custodians = new ArrayList<>();
 
     for (CustodianColumns custodian : CUSTODIAN_COLUMNS) {
@@ -114,7 +113,7 @@ public class CitizenListRowReader extends RowReader<CitizenListRowValues, Citize
   private static boolean anyValueInRange(
       ColumnAccessor<CitizenListColumn> col,
       CustodianColumns custodianColumns,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     return anyValueInRange(
         col.getRange(custodianColumns.lastName(), custodianColumns.gender()), errorHandler);
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
index 77f916362..b577125fc 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/CitizenOrSchoolListImporter.java
@@ -5,24 +5,51 @@
 
 package de.eshg.schoolentry.importer;
 
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGE_FAILED;
+import static de.eshg.schoolentry.importer.ImportType.CITIZEN_LIST;
+import static de.eshg.schoolentry.importer.ImportType.SCHOOL_LIST;
+
+import de.eshg.base.GenderDto;
+import de.eshg.base.address.AddressDto;
+import de.eshg.base.address.DomesticAddressDto;
+import de.eshg.base.address.PostboxAddressDto;
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.XlsxColumn;
+import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.business.model.DataOrigin;
 import de.eshg.schoolentry.business.model.ImportProcedureData;
 import de.eshg.schoolentry.business.model.MergeProcedureData;
+import de.eshg.schoolentry.business.model.ProcedureWithChildData;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import java.time.Year;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
+import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
 
 public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C extends XlsxColumn>
-    extends SchoolEntryImporter<T, C> {
+    extends SchoolEntryImporter<T, C, ProcedureWithChildData> {
 
+  private static final Logger log = LoggerFactory.getLogger(CitizenOrSchoolListImporter.class);
   private final RowValueMapper<T> rowValueMapper;
+  private final ImportType importType;
+  private final UUID locationId;
+  private final SchoolEntryProperties schoolEntryProperties;
 
   public CitizenOrSchoolListImporter(
       XSSFSheet sheet,
@@ -35,17 +62,98 @@ public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C exten
       Year schoolYear,
       SchoolEntryService schoolEntryService,
       SchoolEntryProperties schoolEntryProperties) {
-    super(
-        sheet,
-        rowReader,
-        feedbackColumnAccessor,
-        importType,
-        schoolId,
-        locationId,
-        schoolYear,
-        schoolEntryService,
-        schoolEntryProperties);
+    super(sheet, rowReader, feedbackColumnAccessor, schoolId, schoolYear, schoolEntryService);
+    Assert.isTrue(
+        EnumSet.of(SCHOOL_LIST, CITIZEN_LIST).contains(importType), "Unexpected import type");
+    this.importType = importType;
     this.rowValueMapper = rowValueMapper;
+    this.locationId = locationId;
+    this.schoolEntryProperties = schoolEntryProperties;
+  }
+
+  @Override
+  protected void evaluateActionForValidRow(
+      Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+    List<ProcedureWithChildData> procedures =
+        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
+    if (procedures.isEmpty()) {
+      validRows.importableRows().add(value);
+      stats.countCreated();
+    } else if (procedures.size() > 1
+        || procedures.getFirst().procedure().getProcedureType() != procedureTypeToMergeWith()) {
+      writeStatusAndReferenceId(
+          row, DUPLICATE_IN_ASSET, procedures.getFirst().procedure().getExternalId());
+      stats.countMergeFailed();
+    } else {
+      Assert.isTrue(
+          !schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport(),
+          "Procedures of a draft type should not exist when direct procedure type assignment is enabled.");
+      ProcedureWithChildData procedure = procedures.getFirst();
+      UUID procedureId = procedure.procedure().getExternalId();
+      if (mergeCandidateMatchesImportValues(procedure, value)) {
+        if (validRows.mergeableRows().stream()
+            .anyMatch(mergeableRow -> mergeableRow.getProcedureId().equals(procedureId))) {
+          log.error("Procedure ID {} already found in a previous mergeable row", procedureId);
+          writeStatusAndReferenceId(row, MERGE_FAILED, procedureId);
+          stats.countMergeFailed();
+        } else {
+          value.setProcedureId(procedureId);
+          validRows.mergeableRows().add(value);
+          writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedureId);
+          stats.countMerged();
+        }
+      } else {
+        writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedure.procedure().getExternalId());
+        stats.countMergeFailed();
+      }
+    }
+  }
+
+  private ProcedureType procedureTypeToMergeWith() {
+    if (importType == CITIZEN_LIST) {
+      return ProcedureType.DRAFT_SCHOOL_IMPORT;
+    } else {
+      return ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT;
+    }
+  }
+
+  private boolean mergeCandidateMatchesImportValues(
+      ProcedureWithChildData mergeCandidate, T values) {
+    AddressDto address = mergeCandidate.child().address();
+    if (address instanceof PostboxAddressDto) {
+      return false;
+    }
+    DomesticAddressDto domesticAddressDto = (DomesticAddressDto) address;
+    AddressData importAddress = values.getChild().address();
+    boolean commonFieldsMatch =
+        Objects.equals(domesticAddressDto.street(), importAddress.street())
+            && Objects.equals(domesticAddressDto.city(), importAddress.city())
+            && Objects.equals(domesticAddressDto.houseNumber(), importAddress.houseNumber())
+            && Objects.equals(domesticAddressDto.postalCode(), importAddress.postalCode())
+            && Objects.equals(domesticAddressDto.addressAddition(), importAddress.addressAddition())
+            && Objects.equals(
+                mergeCandidate.child().gender(),
+                Optional.ofNullable(values.getChild().gender()).orElse(GenderDto.NOT_SPECIFIED))
+            && (mergeCandidate.procedure().getSchoolYear() == null
+                || Objects.equals(mergeCandidate.procedure().getSchoolYear(), schoolYear));
+
+    if (!commonFieldsMatch) {
+      return false;
+    }
+
+    if (importType == SCHOOL_LIST) {
+      return (mergeCandidate.procedure().getSchoolId() == null
+              || Objects.equals(mergeCandidate.procedure().getSchoolId(), schoolId))
+          && (mergeCandidate.procedure().getLocationId() == null
+              || Objects.equals(mergeCandidate.procedure().getLocationId(), locationId));
+    } else {
+      return (mergeCandidate.child().placeOfBirth() == null
+              || Objects.equals(
+                  mergeCandidate.child().placeOfBirth(), values.getChild().placeOfBirth()))
+          && (mergeCandidate.child().countryOfBirth() == null
+              || Objects.equals(
+                  mergeCandidate.child().countryOfBirth(), values.getChild().countryOfBirth()));
+    }
   }
 
   @Override
@@ -63,4 +171,10 @@ public class CitizenOrSchoolListImporter<T extends SchoolEntryRowValues, C exten
     return schoolEntryService.mergeProcedures(
         mergeData, importType, schoolId, locationId, schoolYear);
   }
+
+  @Override
+  protected Map<PersonKeyAttributes, List<ProcedureWithChildData>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes) {
+    return schoolEntryService.searchForMergeCandidates(childKeyAttributes);
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
index bce4938e6..392f70527 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportService.java
@@ -12,6 +12,8 @@ import de.eshg.lib.xlsximport.XlsxNormalizer;
 import de.eshg.lib.xlsximport.model.ImportResult;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.config.SchoolEntryProperties;
+import de.eshg.schoolentry.domain.repository.Icd10CodeRepository;
+import de.eshg.schoolentry.domain.repository.Icd10GroupRepository;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import java.io.IOException;
 import java.time.Year;
@@ -26,14 +28,20 @@ public class ImportService {
   private final SchoolEntryService schoolEntryService;
   private final SchoolEntryProperties schoolEntryProperties;
   private final ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper;
+  private final Icd10CodeRepository icd10CodeRepository;
+  private final Icd10GroupRepository icd10GroupRepository;
 
   public ImportService(
       SchoolEntryService schoolEntryService,
       SchoolEntryProperties schoolEntryProperties,
-      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper) {
+      ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper,
+      Icd10CodeRepository icd10CodeRepository,
+      Icd10GroupRepository icd10GroupRepository) {
     this.schoolEntryService = schoolEntryService;
     this.schoolEntryProperties = schoolEntryProperties;
     this.procedureTypeAssignmentHelper = procedureTypeAssignmentHelper;
+    this.icd10CodeRepository = icd10CodeRepository;
+    this.icd10GroupRepository = icd10GroupRepository;
   }
 
   public ImportResult processSheetAndPersistProcedures(
@@ -43,7 +51,7 @@ public class ImportService {
     try (XlsxNormalizer xlsxNormalizer = new XlsxNormalizer()) {
       XSSFSheet normalizedSheet = xlsxNormalizer.normalize(sheet);
 
-      SchoolEntryImporter<? extends SchoolEntryRowValues, ? extends XlsxColumn>
+      SchoolEntryImporter<? extends SchoolEntryRowValues, ? extends XlsxColumn, ?>
           schoolEntryImporter =
               switch (importType) {
                 case CITIZEN_LIST -> {
@@ -84,14 +92,15 @@ public class ImportService {
                           PastProcedureListColumn.values(), normalizedSheet);
                   yield new PastProcedureListImporter(
                       normalizedSheet,
-                      new PastProcedureListRowReader(normalizedSheet, actualColumns),
+                      new PastProcedureListRowReader(
+                          normalizedSheet,
+                          actualColumns,
+                          icd10CodeRepository,
+                          icd10GroupRepository),
                       new FeedbackColumnAccessor(actualColumns),
-                      importType,
                       schoolId,
-                      locationId,
                       schoolYear,
-                      schoolEntryService,
-                      schoolEntryProperties);
+                      schoolEntryService);
                 }
               };
 
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
index aa6adee94..e19f7fe38 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/ImportType.java
@@ -6,18 +6,8 @@
 package de.eshg.schoolentry.importer;
 
 public enum ImportType {
-  CITIZEN_LIST(true),
-  SCHOOL_LIST(true),
-  PAST_PROCEDURE_LIST(false),
+  CITIZEN_LIST,
+  SCHOOL_LIST,
+  PAST_PROCEDURE_LIST,
   ;
-
-  private final boolean supportsMerge;
-
-  ImportType(boolean supportsMerge) {
-    this.supportsMerge = supportsMerge;
-  }
-
-  public boolean supportsMerge() {
-    return supportsMerge;
-  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
index 4b98fa08a..f8d78bbce 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListColumn.java
@@ -60,8 +60,68 @@ public enum PastProcedureListColumn implements XlsxColumn {
   ROTA("Rota"),
   MENINGOCOCCUS_B("MenB"),
   PERKOMBI_HBV("PerkombiHBV"),
+  EYE_EXAMINATION("VISCH"),
+  LANG_EXAMINATION("VISTR"),
+  ISHIHARA_EXAMINATION("FARB"),
+  HEARING_TEST("AUDIO"),
+  SYSTOLE("RRSYS"),
+  DIASTOLE("RRDIA"),
+  HEIGHT("GROE"),
+  WEIGHT("GEWI"),
+  NUTRITIONAL_CONDITION("EZ"),
+  SKIN("DERM"),
+  MUSCULATOR_SKELETON("MUSK"),
+  EAR_NOSE_THROAT("HNO"),
+  RESPIRATORY_CARDIOVASCULAR("AHK"),
+  ABDOMEN("ABD"),
+  NEUROLOGY("NEU"),
+  METABOLISM("ENDO"),
+  JUMP_COUNT("KOORD"),
+  VISUO_MOTOR("VISMOT"),
+  GROSS_MOTOR("GROMO"),
+  FINE_MOTOR("FEIMO"),
+  PRIMARY_LANGUAGE("ESPR"),
+  IN_GERMANY_SINCE("WOHND"),
+  FAMILY_LANGUAGE("FAMSPR"),
+  GERMAN_PRIMARY_CARER("SPRBP"),
+  GERMAN_CHILD("SPRDEU"),
+  ARTICULATION("DYS"),
+  PSEUDOWORDS("PSWOE"),
+  PREPOSITIONS("PRAEP"),
+  PLURALS("PLUR"),
+  SPEECH_RESULT("SPR"),
+  VISUAL_PERCEPTION_POINTS("VISPER"),
+  AUDITIVE_PROCESSING("AUDWA"),
+  VISUAL_PERCEPTION_RESULT("VISWA"),
+  COUNTING("ZAEHL"),
+  QUANTITY_KNOWLEDGE("MENG"),
+  SELECTIVE_ATTENTION("SELAUFM"),
+  KNOWLEDGE_THINKING("WISSDE"),
+  PSYCHOLOGICAL_BEHAVIOUR("PSYVER"),
+  CHRONIC_DISEASE("CHKR"),
+  CHRONIC_DISEASE_ICD10_1("DIAGCH1"),
+  CHRONIC_DISEASE_ICD10_2("DIAGCH2"),
+  CHRONIC_DISEASE_ICD10_3("DIAGCH3"),
+  DISABILITY("BEHI"),
+  DISABILITY_TYPE("BEHIART"),
+  DISABILITY_ICD10_1("DIAGB1"),
+  DISABILITY_ICD10_2("DIAGB2"),
+  DISABILITY_ICD10_3("DIAGB3"),
+  VACCINATION_ADVICE("IMPF"),
+  RE_INTRODUCTION("WSPR"),
+  SCHOOL_COUNSELING("SCHB"),
+  INFO_LETTER("INFO"),
+  MOTOR_PROMOTION("MOTO"),
+  LANGUAGE_ADVICE("SPRF"),
+  NUTRITIONAL_ADVICE("ERNB"),
+  EDUCATIONAL_ADVICE("ERZB"),
+  SOCIAL_SERVICE("SOZD"),
+  OTHER_SUPPORT("SOHI"),
+  EXTRA_EFFORT("MEHR"),
+  SCHOOL_RECOMMENDATION("SCHULEMPF"),
   STATUS(STATUS_COLUMN_HEADER, Necessity.ADD_IF_MISSING, STATUS_COLUMN_HEADER_WIDTH),
   PROCEDURE_ID(PROCEDURE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
+  REFERENCE_ID(REFERENCE_COLUMN_HEADER, Necessity.ADD_IF_MISSING, PROCEDURE_COLUMN_WIDTH),
   ;
 
   private final String header;
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
index e2c34a36b..8516146f0 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListImporter.java
@@ -5,43 +5,74 @@
 
 package de.eshg.schoolentry.importer;
 
+import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
+import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
+
+import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.schoolentry.SchoolEntryService;
 import de.eshg.schoolentry.business.model.ImportPastProcedureData;
-import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import java.time.Year;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
+import java.util.function.Predicate;
+import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 
 public class PastProcedureListImporter
-    extends SchoolEntryImporter<PastProcedureListRowValues, PastProcedureListColumn> {
+    extends SchoolEntryImporter<
+        PastProcedureListRowValues, PastProcedureListColumn, SchoolEntryProcedure> {
 
   private final PastProcedureListRowValueMapper rowValueMapper;
+  private final List<UUID> mergeCandidatesToBeDeleted;
 
   public PastProcedureListImporter(
       XSSFSheet sheet,
       RowReader<PastProcedureListRowValues, PastProcedureListColumn> rowReader,
       FeedbackColumnAccessor feedbackColumnAccessor,
-      ImportType importType,
       UUID schoolId,
-      UUID locationId,
       Year schoolYear,
-      SchoolEntryService schoolEntryService,
-      SchoolEntryProperties schoolEntryProperties) {
-    super(
-        sheet,
-        rowReader,
-        feedbackColumnAccessor,
-        importType,
-        schoolId,
-        locationId,
-        schoolYear,
-        schoolEntryService,
-        schoolEntryProperties);
+      SchoolEntryService schoolEntryService) {
+    super(sheet, rowReader, feedbackColumnAccessor, schoolId, schoolYear, schoolEntryService);
     this.rowValueMapper = new PastProcedureListRowValueMapper();
+    this.mergeCandidatesToBeDeleted = new ArrayList<>();
+  }
+
+  @Override
+  protected void evaluateActionForValidRow(
+      Row row,
+      PastProcedureListRowValues value,
+      Map<PersonKeyAttributes, List<SchoolEntryProcedure>> mergeCandidates) {
+
+    List<SchoolEntryProcedure> candidates =
+        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
+
+    if (candidates.isEmpty()) {
+      validRows.importableRows().add(value);
+      stats.countCreated();
+    } else {
+      Optional<SchoolEntryProcedure> firstNonDeletableCandidate =
+          candidates.stream().filter(Predicate.not(SchoolEntryProcedure::isDeletable)).findFirst();
+
+      if (firstNonDeletableCandidate.isEmpty()) {
+        List<UUID> mergeCandidateIds =
+            candidates.stream().map(SchoolEntryProcedure::getExternalId).toList();
+        mergeCandidatesToBeDeleted.addAll(mergeCandidateIds);
+        validRows.mergeableRows().add(value);
+        writeStatusAndReferenceId(row, MERGED_SUCCESSFULLY, mergeCandidateIds.getFirst());
+        stats.countMerged();
+      } else {
+        writeStatusAndReferenceId(
+            row, DUPLICATE_IN_ASSET, firstNonDeletableCandidate.get().getExternalId());
+        stats.countMergeFailed();
+      }
+    }
   }
 
   @Override
@@ -50,14 +81,24 @@ public class PastProcedureListImporter
 
     List<ImportPastProcedureData> importData =
         importableRows.stream().map(rowValueMapper::mapValuesToImportData).toList();
-    return schoolEntryService.createProceduresFromDataImport(
-        importData, schoolId, locationId, schoolYear);
+    return schoolEntryService.createProceduresFromDataImport(importData, schoolId, schoolYear);
   }
 
   @Override
   protected List<UUID> mergeProceduresAndGetFailedProcedureIds(
       List<PastProcedureListRowValues> mergeableRows) {
-    throw new UnsupportedOperationException(
-        "Merge is not yet supported for past procedure list import.");
+    if (!mergeCandidatesToBeDeleted.isEmpty()) {
+      schoolEntryService.deleteProcedures(mergeCandidatesToBeDeleted);
+    }
+    List<SchoolEntryProcedure> createdProcedures = createProcedures(mergeableRows);
+    writeProcedureIdsInSheet(mergeableRows, createdProcedures, MERGED_SUCCESSFULLY);
+    return List.of();
+  }
+
+  @Override
+  protected Map<PersonKeyAttributes, List<SchoolEntryProcedure>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes) {
+    return schoolEntryService.searchForMergeCandidatesForPastProcedures(
+        childKeyAttributes, schoolYear);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
index 7e23fb710..021299131 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowReader.java
@@ -5,69 +5,22 @@
 
 package de.eshg.schoolentry.importer;
 
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ADDRESS_ADDITION;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.BIRTH_WEIGHT;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.CHILD_LANGUAGE_SCREENING;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.CITY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.COUNTRY_OF_BIRTH_P1;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.COUNTRY_OF_BIRTH_P2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DATE_OF_BIRTH;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DAYCARE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.DIPHTERIA;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.EARLY_SUPPORT;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ERGO_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.EXAMINATION_DATE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.FIRST_NAME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.GENDER;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HEPATITIS_A;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HEPATITIS_B;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HIB;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.HOUSE_NUMBER;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.INTEGRATION_PLACE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.LAST_NAME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MENINGOCOCCUS_B;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MENINGOCOCCUS_C;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MIGRATION_BACKGROUND;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.MMR;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_CHILD;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_P1;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.NATIONALITY_P2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PERKOMBI_HBV;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PERTUSSIS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PHYSIO_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PNEUMOCOCCUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.POLIO;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.POSTAL_CODE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PRELIMINARY_COURSE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PROCEDURE_ID;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.PROCEDURE_TYPE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.ROTA;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.SIBLINGS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.SPEECH_THERAPY;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.STATUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.STREET;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.TBE;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.TETANUS;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U2;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U3;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U4;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U5;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U6;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U7;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U7A;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U8;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.U9;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.VACCINATION_SCHEME;
-import static de.eshg.schoolentry.importer.PastProcedureListColumn.VARICELLA;
+import static de.eshg.schoolentry.importer.PastProcedureListColumn.*;
 
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
-import de.eshg.schoolentry.business.model.ImportAnamnesisData;
-import de.eshg.schoolentry.business.model.ImportChildData;
-import de.eshg.schoolentry.business.model.ImportVaccinationStatusData;
-import java.util.List;
+import de.eshg.schoolentry.business.model.*;
+import de.eshg.schoolentry.domain.model.*;
+import de.eshg.schoolentry.domain.repository.Icd10CodeRepository;
+import de.eshg.schoolentry.domain.repository.Icd10GroupRepository;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
 import java.util.function.BiConsumer;
+import java.util.stream.Stream;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.springframework.data.domain.Range;
@@ -75,14 +28,24 @@ import org.springframework.data.domain.Range;
 public class PastProcedureListRowReader
     extends RowReader<PastProcedureListRowValues, PastProcedureListColumn> {
 
-  public PastProcedureListRowReader(Sheet sheet, List<PastProcedureListColumn> actualColumns) {
+  private final Icd10CodeRepository icd10CodeRepository;
+  private final Icd10GroupRepository icd10GroupRepository;
+  public static final String DATE_FORMAT = "^\\d{2}\\.\\d{4}$";
+
+  public PastProcedureListRowReader(
+      Sheet sheet,
+      List<PastProcedureListColumn> actualColumns,
+      Icd10CodeRepository icd10CodeRepository,
+      Icd10GroupRepository icd10GroupRepository) {
     super(sheet, actualColumns);
+    this.icd10CodeRepository = icd10CodeRepository;
+    this.icd10GroupRepository = icd10GroupRepository;
   }
 
   @Override
   protected PastProcedureListRowValues read(ColumnAccessor<PastProcedureListColumn> col) {
     PastProcedureListRowValues result = new PastProcedureListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     result.setProcedureType(readProcedureType(col, errorHandler));
@@ -91,22 +54,26 @@ public class PastProcedureListRowReader
     result.setProcedureId(readProcedureId(col, PROCEDURE_ID, errorHandler));
     result.setAnamnesisData(readAnamnesisData(col, errorHandler));
     result.setVaccinationStatusData(readVaccinationStatusData(col, errorHandler));
+    result.setEyeExaminationResult(readEyeExaminationData(col, errorHandler));
+    result.setHearingTestData(readHearingTestData(col, errorHandler));
+    result.setSopessExaminationData(readSopessExaminationData(col, errorHandler));
+    result.setDevelopmentScreeningData(readDevelopmentScreeningData(col, errorHandler));
     return result;
   }
 
   private ImportAnamnesisData readAnamnesisData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportAnamnesisData(
-        cellAsInt(col, SIBLINGS, errorHandler),
+        readIntegerInRange(col, SIBLINGS, errorHandler, 0, 15, 99),
         cellAsInt(col, NATIONALITY_CHILD, errorHandler),
         cellAsInt(col, NATIONALITY_P1, errorHandler),
         cellAsInt(col, COUNTRY_OF_BIRTH_P1, errorHandler),
         cellAsInt(col, NATIONALITY_P2, errorHandler),
         cellAsInt(col, COUNTRY_OF_BIRTH_P2, errorHandler),
         cellAsBoolean(col, MIGRATION_BACKGROUND, errorHandler),
-        cellAsInt(col, DAYCARE, errorHandler),
+        readIntegerInRange(col, DAYCARE, errorHandler, 0, 3, 9),
         cellAsBooleanWithFallbackFalse(col, PRELIMINARY_COURSE, errorHandler),
-        readBirthWeight(col, errorHandler),
+        readIntegerInRange(col, BIRTH_WEIGHT, errorHandler, 300, 6000, 9999),
         cellAsBooleanWithFallbackFalse(col, INTEGRATION_PLACE, errorHandler),
         cellAsBooleanWithFallbackFalse(col, EARLY_SUPPORT, errorHandler),
         cellAsBooleanWithFallbackFalse(col, ERGO_THERAPY, errorHandler),
@@ -121,11 +88,12 @@ public class PastProcedureListRowReader
         cellAsBooleanOrNull(col, U7, errorHandler),
         cellAsBooleanOrNull(col, U7A, errorHandler),
         cellAsBooleanOrNull(col, U8, errorHandler),
-        cellAsBooleanOrNull(col, U9, errorHandler));
+        cellAsBooleanOrNull(col, U9, errorHandler),
+        readInGermanySince(col, errorHandler));
   }
 
   private ImportVaccinationStatusData readVaccinationStatusData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportVaccinationStatusData(
         readVaccinationScheme(col, errorHandler),
         readNumberOfVaccinations(col, TETANUS, errorHandler),
@@ -145,8 +113,178 @@ public class PastProcedureListRowReader
         cellAsBooleanOrNull(col, PERKOMBI_HBV, errorHandler));
   }
 
+  private EyeExaminationResult readEyeExaminationData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    EyeExaminationResult eyeExaminationResult = new EyeExaminationResult();
+    eyeExaminationResult.setEyeExamination(
+        readExaminationResult(col, EYE_EXAMINATION, errorHandler));
+    eyeExaminationResult.setLangExamination(
+        readExaminationResult(col, LANG_EXAMINATION, errorHandler));
+    eyeExaminationResult.setIshiharaExamination(
+        readExaminationResult(col, ISHIHARA_EXAMINATION, errorHandler));
+    return eyeExaminationResult;
+  }
+
+  private HearingTestResult readHearingTestData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    HearingTestResult hearingTestResult = new HearingTestResult();
+    hearingTestResult.setExaminationResult(readExaminationResult(col, HEARING_TEST, errorHandler));
+    return hearingTestResult;
+  }
+
+  private SopessExaminationResult readSopessExaminationData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    SopessExaminationResult sopessExaminationResult = new SopessExaminationResult();
+
+    Integer articulationSum = readIntegerInRange(col, ARTICULATION, errorHandler, 0, 10, 99);
+    if (articulationSum != null) {
+      setAllArticulationValues(articulationSum, sopessExaminationResult);
+    }
+
+    sopessExaminationResult.setJumpCount(
+        readIntegerInRange(col, JUMP_COUNT, errorHandler, 0, 30, 99));
+    sopessExaminationResult.setVisuoMotor(
+        readIntegerInRange(col, VISUO_MOTOR, errorHandler, 0, 12, 99));
+    sopessExaminationResult.setGrossMotorSkills(
+        readSopessExaminationResultValue(
+            col,
+            GROSS_MOTOR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterGrossMotorSkills,
+            sopessExaminationResult));
+    sopessExaminationResult.setFineMotorSkills(
+        readSopessExaminationResultValue(
+            col,
+            FINE_MOTOR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterFineMotorSkills,
+            sopessExaminationResult));
+    sopessExaminationResult.setPrimaryLanguage(readPrimaryLanguageValue(col, errorHandler));
+    sopessExaminationResult.setFamilyLanguage(readFamilyLanguageValue(col, errorHandler));
+    sopessExaminationResult.setGermanKnowledgePrimaryCarer(
+        readLanguageKnowledgeValue(col, errorHandler));
+    sopessExaminationResult.setGermanKnowledgeChild(readGermanKnowledgeValue(col, errorHandler));
+    sopessExaminationResult.setPseudowordPoints(
+        readIntegerInRange(col, PSEUDOWORDS, errorHandler, 0, 6, 9));
+    sopessExaminationResult.setPrepositionPoints(
+        readIntegerInRange(col, PREPOSITIONS, errorHandler, 0, 8, 9));
+    sopessExaminationResult.setPluralPoints(
+        readIntegerInRange(col, PLURALS, errorHandler, 0, 7, 9));
+    sopessExaminationResult.setSpeechResult(
+        readSopessExaminationResultValue(
+            col,
+            SPEECH_RESULT,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterSpeech,
+            sopessExaminationResult));
+    sopessExaminationResult.setVisualPerceptionPoints(
+        readIntegerInRange(col, VISUAL_PERCEPTION_POINTS, errorHandler, 0, 15, 99));
+    sopessExaminationResult.setAuditiveProcessingResult(
+        readSopessExaminationResultValue(
+            col,
+            AUDITIVE_PROCESSING,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterAuditiveProcessing,
+            sopessExaminationResult));
+    sopessExaminationResult.setVisualPerceptionResult(
+        readSopessExaminationResultValue(
+            col,
+            VISUAL_PERCEPTION_RESULT,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterVisualPerception,
+            sopessExaminationResult));
+    sopessExaminationResult.setCountingPoints(
+        readIntegerInRange(col, COUNTING, errorHandler, 0, 20, 99));
+    sopessExaminationResult.setQuantityKnowledgePoints(
+        readIntegerInRange(col, QUANTITY_KNOWLEDGE, errorHandler, 0, 16, 99));
+    sopessExaminationResult.setSelectiveAttentionPoints(
+        readIntegerInRange(col, SELECTIVE_ATTENTION, errorHandler, 0, 29, 99));
+    sopessExaminationResult.setKnowledgeThinkingResult(
+        readSopessExaminationResultValue(
+            col,
+            KNOWLEDGE_THINKING,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterKnowledgeThinking,
+            sopessExaminationResult));
+    sopessExaminationResult.setPsychologicalBehaviorResult(
+        readSopessExaminationResultValue(
+            col,
+            PSYCHOLOGICAL_BEHAVIOUR,
+            errorHandler,
+            SopessExaminationResult::setDoctorLetterPsychologicalBehavior,
+            sopessExaminationResult));
+    return sopessExaminationResult;
+  }
+
+  private void setAllArticulationValues(
+      Integer articulationSum, SopessExaminationResult sopessExaminationResult) {
+    List<BiConsumer<SopessExaminationResult, ArticulationValue>> articulationSetters =
+        List.of(
+            SopessExaminationResult::setLettersSAndZPoints,
+            SopessExaminationResult::setFormationSchPoints,
+            SopessExaminationResult::setLettersTAndDPoints,
+            SopessExaminationResult::setFormationChPoints,
+            SopessExaminationResult::setLettersGAndKPoints,
+            SopessExaminationResult::setLettersLAndNPoints,
+            SopessExaminationResult::setLetterRPoints,
+            SopessExaminationResult::setLetterFAndFormationPfPoints,
+            SopessExaminationResult::setLetterBPoints,
+            SopessExaminationResult::setFormationsTrDrKrGrPoints);
+
+    if (articulationSum == 0) {
+      for (int i = 0; i < articulationSetters.size(); i++) {
+        articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.INCONSPICUOUS);
+      }
+    } else {
+      for (int i = 0; i < articulationSetters.size(); i++) {
+        if (i < articulationSum) {
+          articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.CONSPICUOUS);
+        } else {
+          articulationSetters.get(i).accept(sopessExaminationResult, ArticulationValue.UNKNOWN);
+        }
+      }
+    }
+  }
+
+  private DevelopmentScreening readDevelopmentScreeningData(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    DevelopmentScreening developmentScreening = new DevelopmentScreening();
+    developmentScreening.setSystole(readIntegerInRange(col, SYSTOLE, errorHandler, 50, 250, 999));
+    developmentScreening.setDiastole(readIntegerInRange(col, DIASTOLE, errorHandler, 50, 250, 999));
+    developmentScreening.setHeight(readDoubleInRange(col, HEIGHT, errorHandler, 0.7, 1.6, 9.9));
+    developmentScreening.setWeight(readDoubleInRange(col, WEIGHT, errorHandler, 8.0, 80.0, 99.9));
+    developmentScreening.setNutritionalCondition(
+        readExaminationWithDiagnosis(col, NUTRITIONAL_CONDITION, errorHandler));
+    developmentScreening.setSkin(readExaminationWithDiagnosis(col, SKIN, errorHandler));
+    developmentScreening.setMusculatureSkeleton(
+        readExaminationWithDiagnosis(col, MUSCULATOR_SKELETON, errorHandler));
+    developmentScreening.setEarNoseThroat(
+        readExaminationWithDiagnosis(col, EAR_NOSE_THROAT, errorHandler));
+    developmentScreening.setRespiratoryCardiovascular(
+        readExaminationWithDiagnosis(col, RESPIRATORY_CARDIOVASCULAR, errorHandler));
+    developmentScreening.setAbdomen(readExaminationWithDiagnosis(col, ABDOMEN, errorHandler));
+    developmentScreening.setNeurology(readExaminationWithDiagnosis(col, NEUROLOGY, errorHandler));
+    developmentScreening.setMetabolism(readExaminationWithDiagnosis(col, METABOLISM, errorHandler));
+    developmentScreening.setChronicDisease(readChronicDisease(col, errorHandler));
+    developmentScreening.setDisability(readDisability(col, errorHandler));
+    developmentScreening.setDisabilityType(readDisabilityType(col, errorHandler));
+    developmentScreening.setVaccinationAdvice(cellAsBoolean(col, VACCINATION_ADVICE, errorHandler));
+    developmentScreening.setReIntroduction(cellAsBoolean(col, RE_INTRODUCTION, errorHandler));
+    developmentScreening.setSchoolCounselling(cellAsBoolean(col, SCHOOL_COUNSELING, errorHandler));
+    developmentScreening.setInfoLetter(cellAsBoolean(col, INFO_LETTER, errorHandler));
+    developmentScreening.setMotorPromotion(cellAsBoolean(col, MOTOR_PROMOTION, errorHandler));
+    developmentScreening.setLanguageAdvice(cellAsBoolean(col, LANGUAGE_ADVICE, errorHandler));
+    developmentScreening.setNutritionalAdvice(cellAsBoolean(col, NUTRITIONAL_ADVICE, errorHandler));
+    developmentScreening.setEducationalAdvice(cellAsBoolean(col, EDUCATIONAL_ADVICE, errorHandler));
+    developmentScreening.setSocialService(cellAsBoolean(col, SOCIAL_SERVICE, errorHandler));
+    developmentScreening.setOtherSupport(cellAsBoolean(col, OTHER_SUPPORT, errorHandler));
+    developmentScreening.setExtraEffort(cellAsBoolean(col, EXTRA_EFFORT, errorHandler));
+    developmentScreening.setSchoolRecommendation(readSchoolRecommendation(col, errorHandler));
+    return developmentScreening;
+  }
+
   private ImportChildData readChildData(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     return new ImportChildData(
         cellAsString(col, FIRST_NAME, errorHandler),
         cellAsString(col, LAST_NAME, errorHandler),
@@ -161,39 +299,38 @@ public class PastProcedureListRowReader
   }
 
   private ProcedureType readProcedureType(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     Cell cell = col.get(PROCEDURE_TYPE);
     String string = cellAsString(cell, errorHandler);
 
     return switch (string) {
+      case null -> null;
       case "Regel" -> ProcedureType.REGULAR_EXAMINATION;
       case "Kann" -> ProcedureType.CAN_CHILD;
       case "Eingangsstufe" -> ProcedureType.ENTRY_LEVEL;
       default -> {
-        errorHandler.accept(cell, "Ungültiger Wert");
+        errorHandler.handleError(cell, "Ungültiger Wert");
         yield null;
       }
     };
   }
 
-  private int readBirthWeight(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
-    Cell cell = col.get(BIRTH_WEIGHT);
-    Integer value = cellAsInt(col, BIRTH_WEIGHT, errorHandler);
-    Range<Integer> validRange = Range.closed(300, 6000);
-    if (!validRange.contains(value) && !value.equals(9999)) {
-      errorHandler.accept(
-          cell,
-          "Ungültiger Wert (Erwartet: Wert zwischen 300 und 6000 sowie 9999. Tatsächlich: %s)"
-              .formatted(value));
+  private LocalDate readInGermanySince(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(IN_GERMANY_SINCE);
+    String value = cellAsString(col, IN_GERMANY_SINCE, errorHandler);
+    if (value == null || !value.matches(DATE_FORMAT)) {
+      errorHandler.handleError(
+          cell, "Ungültiger Wert (Erwartet: MM.YYYY. Tatsächlich: %s)".formatted(value));
+      return null;
     }
-    return value;
+    return YearMonth.parse(value, DateTimeFormatter.ofPattern("MM.yyyy")).atDay(1);
   }
 
   private boolean cellAsBooleanWithFallbackFalse(
       ColumnAccessor<PastProcedureListColumn> col,
       PastProcedureListColumn column,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     Boolean value = cellAsBooleanOrNull(col, column, errorHandler);
     if (value == null) {
       return false;
@@ -201,32 +338,389 @@ public class PastProcedureListRowReader
     return value;
   }
 
-  private int readVaccinationScheme(
-      ColumnAccessor<PastProcedureListColumn> col, BiConsumer<Cell, String> errorHandler) {
+  private Integer readVaccinationScheme(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
     Cell cell = col.get(VACCINATION_SCHEME);
     Integer value = cellAsInt(col, VACCINATION_SCHEME, errorHandler);
     return switch (value) {
       case 2, 3, 9 -> value;
       case null, default -> {
-        errorHandler.accept(
+        errorHandler.handleError(
             cell, "Ungültiger Wert (Erwartet: 2, 3 oder 9. Tatsächlich: %s)".formatted(value));
         yield value;
       }
     };
   }
 
-  private int readNumberOfVaccinations(
+  private Integer readNumberOfVaccinations(
       ColumnAccessor<PastProcedureListColumn> col,
       PastProcedureListColumn column,
-      BiConsumer<Cell, String> errorHandler) {
+      ErrorHandler errorHandler) {
     Cell cell = col.get(column);
     Integer value = cellAsInt(col, column, errorHandler);
     Range<Integer> validRange = Range.closed(0, 9);
-    if (!validRange.contains(value)) {
-      errorHandler.accept(
+    if (value == null || !validRange.contains(value)) {
+      errorHandler.handleError(
           cell,
           "Ungültiger Wert (Erwartet: Wert zwischen 0 und 9. Tatsächlich: %s)".formatted(value));
     }
     return value;
   }
+
+  private Integer readIntegerInRange(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      int min,
+      int max,
+      int unknownValue) {
+    Cell cell = col.get(column);
+    Integer value = cellAsInt(col, column, errorHandler);
+    Range<Integer> validRange = Range.closed(min, max);
+    if (value == null || (!validRange.contains(value) && value != unknownValue)) {
+      errorHandler.handleError(
+          cell,
+          "Ungültiger Wert (Erwartet: Wert zwischen %d und %d sowie %d. Tatsächlich: %s)"
+              .formatted(min, max, unknownValue, value));
+    }
+    return value;
+  }
+
+  private Double readDoubleInRange(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      double min,
+      double max,
+      double unknownValue) {
+    Cell cell = col.get(column);
+    Double value = cellAsDouble(col, column, errorHandler);
+    Range<Double> validRange = Range.closed(min, max);
+    if (value == null || (!validRange.contains(value) && value != unknownValue)) {
+      errorHandler.handleError(
+          cell,
+          "Ungültiger Wert (Erwartet: Wert zwischen %f und %f sowie %f. Tatsächlich: %s)"
+              .formatted(min, max, unknownValue, value));
+    }
+    return value;
+  }
+
+  private ExaminationResult readExaminationResult(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I", "B", "U" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        yield examinationResult;
+      }
+      case "A" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        examinationResult.setDoctorLetter(DoctorLetterValue.NO_REPLY);
+        yield examinationResult;
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: I, B, A oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private ExaminationWithDiagnosis readExaminationWithDiagnosis(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I", "B", "U" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        yield getExaminationWithDiagnosis(examinationResult);
+      }
+      case "A" -> {
+        ExaminationResult examinationResult = new ExaminationResult();
+        examinationResult.setValue(mapToExaminationResultValue(value));
+        examinationResult.setDoctorLetter(DoctorLetterValue.NO_REPLY);
+        yield getExaminationWithDiagnosis(examinationResult);
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: I, B, A oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private static ExaminationWithDiagnosis getExaminationWithDiagnosis(
+      ExaminationResult examinationResult) {
+    ExaminationWithDiagnosis examinationWithDiagnosis = new ExaminationWithDiagnosis();
+    examinationWithDiagnosis.setResult(examinationResult);
+    return examinationWithDiagnosis;
+  }
+
+  private ExaminationResultValue mapToExaminationResultValue(String stringValue) {
+    return switch (stringValue) {
+      case "I" -> ExaminationResultValue.OK;
+      case "B" -> ExaminationResultValue.KNOWN;
+      case "A" -> ExaminationResultValue.DOCTOR_LETTER;
+      case "U" -> ExaminationResultValue.UNKNOWN;
+      default -> throw new IllegalArgumentException("Only I, B, A and U are allowed");
+    };
+  }
+
+  private SopessExaminationResultValue readSopessExaminationResultValue(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      ErrorHandler errorHandler,
+      BiConsumer<SopessExaminationResult, DoctorLetterValue> doctorLetterValueSetter,
+      SopessExaminationResult sopessExaminationResult) {
+    Cell cell = col.get(column);
+    String value = cellAsString(col, column, errorHandler);
+    return switch (value) {
+      case "I" -> SopessExaminationResultValue.OK;
+      case "B" -> SopessExaminationResultValue.KNOWN;
+      case "G" -> SopessExaminationResultValue.BORDERLINE;
+      case "U" -> SopessExaminationResultValue.UNKNOWN;
+      case "A" -> {
+        doctorLetterValueSetter.accept(sopessExaminationResult, DoctorLetterValue.NO_REPLY);
+        yield SopessExaminationResultValue.DOCTOR_LETTER;
+      }
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: I, B, A, G oder U. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private PrimaryLanguageValue readPrimaryLanguageValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(PRIMARY_LANGUAGE);
+    String value = cellAsInt(col, PRIMARY_LANGUAGE, errorHandler).toString();
+    return switch (value) {
+      case "1" -> PrimaryLanguageValue.GERMAN;
+      case "2" -> PrimaryLanguageValue.OTHER;
+      case "9" -> PrimaryLanguageValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: 1, 2 oder 9. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private LanguageKnowledgeValue readLanguageKnowledgeValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(GERMAN_PRIMARY_CARER);
+    String value = cellAsInt(col, GERMAN_PRIMARY_CARER, errorHandler).toString();
+    return switch (value) {
+      case "1" -> LanguageKnowledgeValue.RUDIMENTARY;
+      case "2" -> LanguageKnowledgeValue.FAULTY;
+      case "3" -> LanguageKnowledgeValue.FAULTLESS;
+      case "9" -> LanguageKnowledgeValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Wert zwischen 1 und 3 sowie 9. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private GermanKnowledgeValue readGermanKnowledgeValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(GERMAN_CHILD);
+    String value = cellAsInt(col, GERMAN_CHILD, errorHandler).toString();
+    return switch (value) {
+      case "1" -> GermanKnowledgeValue.NO_GERMAN;
+      case "2" -> GermanKnowledgeValue.BAD;
+      case "3" -> GermanKnowledgeValue.FLUID_WITH_MAJOR_ERRORS;
+      case "4" -> GermanKnowledgeValue.FLUID_WITH_MINOR_ERRORS;
+      case "5" -> GermanKnowledgeValue.FAULTLESS;
+      case "9" -> GermanKnowledgeValue.UNKNOWN;
+      default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Wert zwischen 1 und 5 sowie 9. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private FamilyLanguageValue readFamilyLanguageValue(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(FAMILY_LANGUAGE);
+    String value = cellAsString(col, FAMILY_LANGUAGE, errorHandler);
+    return switch (value) {
+      case "\"00\"" -> FamilyLanguageValue.GERMAN;
+      case "\"01\"" -> FamilyLanguageValue.TURKISH;
+      case "\"02\"" -> FamilyLanguageValue.KURDISH;
+      case "\"03\"" -> FamilyLanguageValue.RUSSIAN;
+      case "\"04\"" -> FamilyLanguageValue.POLISH;
+      case "\"05\"" -> FamilyLanguageValue.ARABIC;
+      case "\"06\"" -> FamilyLanguageValue.FARSI_DARI;
+      case "\"07\"" -> FamilyLanguageValue.SERBO_CROATIAN;
+      case "\"08\"" -> FamilyLanguageValue.ROMAN;
+      case "\"09\"" -> FamilyLanguageValue.BULGARIAN;
+      case "\"10\"" -> FamilyLanguageValue.PASHTU;
+      case "\"11\"" -> FamilyLanguageValue.TIGRINIA;
+      case "\"12\"" -> FamilyLanguageValue.BERBERIAN;
+      case "\"13\"" -> FamilyLanguageValue.AMHARIAN;
+      case "\"14\"" -> FamilyLanguageValue.ARAMEAN;
+      case "\"15\"" -> FamilyLanguageValue.ITALIAN;
+      case "\"16\"" -> FamilyLanguageValue.SPANISH;
+      case "\"17\"" -> FamilyLanguageValue.GREEK;
+      case "\"18\"" -> FamilyLanguageValue.PORTUGUESE;
+      case "\"19\"" -> FamilyLanguageValue.ENGLISH;
+      case "\"20\"" -> FamilyLanguageValue.FRENCH;
+      case "\"21\"" -> FamilyLanguageValue.URDU;
+      case "\"22\"" -> FamilyLanguageValue.OTHER_EUROPEAN_LANGUAGES;
+      case "\"23\"" -> FamilyLanguageValue.OTHER_ASIAN_LANGUAGES;
+      case "\"24\"" -> FamilyLanguageValue.OTHER_AFRICAN_LANGUAGES;
+      case "\"25\"" -> FamilyLanguageValue.OTHER_LANGUAGES;
+      case "\"99\"" -> FamilyLanguageValue.UNKNOWN;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: \"00\"-\"25\" sowie \"99\". Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private HandicapWithDiagnosis readChronicDisease(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    return readHandicapWithDiagnosis(
+        col,
+        CHRONIC_DISEASE,
+        CHRONIC_DISEASE_ICD10_1,
+        CHRONIC_DISEASE_ICD10_2,
+        CHRONIC_DISEASE_ICD10_3,
+        errorHandler);
+  }
+
+  private HandicapWithDiagnosis readDisability(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    return readHandicapWithDiagnosis(
+        col, DISABILITY, DISABILITY_ICD10_1, DISABILITY_ICD10_2, DISABILITY_ICD10_3, errorHandler);
+  }
+
+  private HandicapWithDiagnosis readHandicapWithDiagnosis(
+      ColumnAccessor<PastProcedureListColumn> col,
+      PastProcedureListColumn column,
+      PastProcedureListColumn diagnosis1,
+      PastProcedureListColumn diagnosis2,
+      PastProcedureListColumn diagnosis3,
+      ErrorHandler errorHandler) {
+    Cell cell = col.get(column);
+    Cell cellDiagnosis1 = col.get(diagnosis1);
+    Cell cellDiagnosis2 = col.get(diagnosis2);
+    Cell cellDiagnosis3 = col.get(diagnosis3);
+    boolean value = cellAsBoolean(col, column, errorHandler);
+    String icd10Code1 = cellAsString(col, diagnosis1, true, false, errorHandler);
+    String icd10Code2 = cellAsString(col, diagnosis2, true, false, errorHandler);
+    String icd10Code3 = cellAsString(col, diagnosis3, true, false, errorHandler);
+    List<String> icd10Codes =
+        Stream.of(icd10Code1, icd10Code2, icd10Code3)
+            .filter(Objects::nonNull)
+            .sorted(String::compareTo)
+            .toList();
+
+    if (value && icd10Codes.isEmpty()) {
+      errorHandler.handleError(cell, "Ungültiger Wert (Diagnosen erwartet, wenn Ja angegeben)");
+      return null;
+    }
+
+    Map<Cell, String> icd10CodesByCells = new LinkedHashMap<>();
+    icd10CodesByCells.put(cellDiagnosis1, icd10Code1);
+    icd10CodesByCells.put(cellDiagnosis2, icd10Code2);
+    icd10CodesByCells.put(cellDiagnosis3, icd10Code3);
+
+    boolean errorInICD10 = false;
+
+    for (Map.Entry<Cell, String> icd10CodeByCell : icd10CodesByCells.entrySet()) {
+      String icd10Code = icd10CodeByCell.getValue();
+      if (icd10Code != null
+          && !icd10CodeRepository.existsByCodeWithoutDot(icd10Code)
+          && !icd10GroupRepository.existsByGroupStartAndGroupEnd(icd10Code)) {
+        errorHandler.handleError(
+            icd10CodeByCell.getKey(),
+            "Ungültiger Wert (ICD-10 Code %s existiert nicht)".formatted(icd10Code));
+        errorInICD10 = true;
+      }
+    }
+
+    if (!value && !icd10Codes.isEmpty()) {
+      errorHandler.handleError(cell, "Ungültiger Wert (Ja erwartet, da Diagnosen angegeben)");
+      return null;
+    }
+
+    HandicapWithDiagnosis handicapWithDiagnosis = new HandicapWithDiagnosis();
+    handicapWithDiagnosis.setResult(value);
+    if (!errorInICD10) {
+      handicapWithDiagnosis.setIcd10Codes(icd10Codes);
+    }
+    return handicapWithDiagnosis;
+  }
+
+  private DisabilityType readDisabilityType(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(DISABILITY_TYPE);
+    String value = cellAsString(col, DISABILITY_TYPE, true, false, errorHandler);
+    boolean disabilityValue = cellAsBooleanWithFallbackFalse(col, DISABILITY, errorHandler);
+
+    if (!disabilityValue) {
+      if (value != null) {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Kein Wert erwartet, wenn kein BEHI vorliegt)");
+      }
+      return null;
+    } else if (value == null || value.isBlank()) {
+      errorHandler.handleError(
+          cell, "Ungültiger Wert (K, G, S oder M erwartet, wenn BEHI vorliegt)");
+      return null;
+    }
+
+    return switch (value) {
+      case "K" -> DisabilityType.PHYSICAL;
+      case "G" -> DisabilityType.MENTAL;
+      case "S" -> DisabilityType.EMOTIONAL;
+      case "M" -> DisabilityType.MULTIPLE;
+      default -> {
+        errorHandler.handleError(
+            cell, "Ungültiger Wert (Erwartet: K, G, S oder M. Tatsächlich: %s)".formatted(value));
+        yield null;
+      }
+    };
+  }
+
+  private SchoolRecommendation readSchoolRecommendation(
+      ColumnAccessor<PastProcedureListColumn> col, ErrorHandler errorHandler) {
+    Cell cell = col.get(SCHOOL_RECOMMENDATION);
+    String value = cellAsString(col, SCHOOL_RECOMMENDATION, errorHandler);
+    return switch (value) {
+      case "Nein" -> SchoolRecommendation.NO;
+      case "ZURK" -> SchoolRecommendation.BACK_REGULAR;
+      case "ZUEK" -> SchoolRecommendation.BACK_ENTRY_LEVEL;
+      case "BEKK" -> SchoolRecommendation.CONCERNS_EARLY_ENROLMENT;
+      case "BFZ" -> SchoolRecommendation.ADVICE_CENTER;
+      case null, default -> {
+        errorHandler.handleError(
+            cell,
+            "Ungültiger Wert (Erwartet: Nein, ZURK, ZUEK, BEKK oder BFZ. Tatsächlich: %s)"
+                .formatted(value));
+        yield null;
+      }
+    };
+  }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
index d6f83fc9f..bf84e27bc 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValueMapper.java
@@ -21,6 +21,10 @@ public class PastProcedureListRowValueMapper {
             false,
             false),
         values.getAnamnesisData(),
-        values.getVaccinationStatusData());
+        values.getVaccinationStatusData(),
+        values.getEyeExaminationResult(),
+        values.getHearingTestData(),
+        values.getSopessExaminationData(),
+        values.getDevelopmentScreeningData());
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
index 304ff00fb..842d817e2 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/PastProcedureListRowValues.java
@@ -8,6 +8,10 @@ package de.eshg.schoolentry.importer;
 import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.schoolentry.business.model.ImportAnamnesisData;
 import de.eshg.schoolentry.business.model.ImportVaccinationStatusData;
+import de.eshg.schoolentry.domain.model.DevelopmentScreening;
+import de.eshg.schoolentry.domain.model.EyeExaminationResult;
+import de.eshg.schoolentry.domain.model.HearingTestResult;
+import de.eshg.schoolentry.domain.model.SopessExaminationResult;
 import java.time.LocalDate;
 import java.util.Objects;
 
@@ -21,6 +25,14 @@ public final class PastProcedureListRowValues extends SchoolEntryRowValues {
 
   private ImportVaccinationStatusData vaccinationStatusData;
 
+  private EyeExaminationResult eyeExaminationResult;
+
+  private HearingTestResult hearingTestData;
+
+  private SopessExaminationResult sopessExaminationData;
+
+  private DevelopmentScreening developmentScreeningData;
+
   public ProcedureType getProcedureType() {
     return procedureType;
   }
@@ -53,6 +65,38 @@ public final class PastProcedureListRowValues extends SchoolEntryRowValues {
     this.vaccinationStatusData = vaccinationStatusData;
   }
 
+  public EyeExaminationResult getEyeExaminationResult() {
+    return eyeExaminationResult;
+  }
+
+  public void setEyeExaminationResult(EyeExaminationResult eyeExaminationResult) {
+    this.eyeExaminationResult = eyeExaminationResult;
+  }
+
+  public HearingTestResult getHearingTestData() {
+    return hearingTestData;
+  }
+
+  public void setHearingTestData(HearingTestResult hearingTestData) {
+    this.hearingTestData = hearingTestData;
+  }
+
+  public SopessExaminationResult getSopessExaminationData() {
+    return sopessExaminationData;
+  }
+
+  public void setSopessExaminationData(SopessExaminationResult sopessExaminationData) {
+    this.sopessExaminationData = sopessExaminationData;
+  }
+
+  public DevelopmentScreening getDevelopmentScreeningData() {
+    return developmentScreeningData;
+  }
+
+  public void setDevelopmentScreeningData(DevelopmentScreening developmentScreeningData) {
+    this.developmentScreeningData = developmentScreeningData;
+  }
+
   @Override
   boolean isDuplicateRow(Object other) {
     return (other instanceof PastProcedureListRowValues pastProcedureListRowValues)
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
index 76d2299de..b2b7947bd 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolEntryImporter.java
@@ -5,38 +5,26 @@
 
 package de.eshg.schoolentry.importer;
 
-import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_IN_ASSET;
 import static de.eshg.lib.xlsximport.ImportStatus.DUPLICATE_WITHIN_LIST;
 import static de.eshg.lib.xlsximport.ImportStatus.ERROR_INPUT_DATA;
 import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_PREVIOUSLY;
 import static de.eshg.lib.xlsximport.ImportStatus.IMPORTED_SUCCESSFULLY;
 import static de.eshg.lib.xlsximport.ImportStatus.INVALID_PROCEDURE_ID;
-import static de.eshg.lib.xlsximport.ImportStatus.MERGED_SUCCESSFULLY;
-import static de.eshg.lib.xlsximport.ImportStatus.MERGE_FAILED;
 
 import de.cronn.commons.lang.StreamUtil;
-import de.eshg.base.GenderDto;
-import de.eshg.base.address.AddressDto;
-import de.eshg.base.address.DomesticAddressDto;
-import de.eshg.base.address.PostboxAddressDto;
 import de.eshg.base.centralfile.api.person.PersonKeyAttributes;
-import de.eshg.lib.procedure.domain.model.ProcedureType;
 import de.eshg.lib.xlsximport.FeedbackColumnAccessor;
+import de.eshg.lib.xlsximport.ImportStatus;
 import de.eshg.lib.xlsximport.Importer;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.lib.xlsximport.XlsxColumn;
-import de.eshg.lib.xlsximport.model.AddressData;
 import de.eshg.schoolentry.SchoolEntryService;
-import de.eshg.schoolentry.business.model.ProcedureWithChildData;
-import de.eshg.schoolentry.config.SchoolEntryProperties;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
-import de.eshg.schoolentry.util.ExceptionUtil;
 import java.time.Year;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Stream;
@@ -44,37 +32,26 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.util.Assert;
 
-public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C extends XlsxColumn>
+public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C extends XlsxColumn, M>
     extends Importer<T, C> {
 
-  protected static final Logger log = LoggerFactory.getLogger(SchoolEntryImporter.class);
-
-  protected final ImportType importType;
+  private static final Logger log = LoggerFactory.getLogger(SchoolEntryImporter.class);
   protected final UUID schoolId;
-  protected final UUID locationId;
   protected final Year schoolYear;
   protected final SchoolEntryService schoolEntryService;
-  protected final SchoolEntryProperties schoolEntryProperties;
 
   protected SchoolEntryImporter(
       XSSFSheet sheet,
       RowReader<T, C> rowReader,
       FeedbackColumnAccessor feedbackColumnAccessor,
-      ImportType importType,
       UUID schoolId,
-      UUID locationId,
       Year schoolYear,
-      SchoolEntryService schoolEntryService,
-      SchoolEntryProperties schoolEntryProperties) {
+      SchoolEntryService schoolEntryService) {
     super(sheet, rowReader, feedbackColumnAccessor);
-    this.importType = importType;
     this.schoolId = schoolId;
-    this.locationId = locationId;
     this.schoolYear = schoolYear;
     this.schoolEntryService = schoolEntryService;
-    this.schoolEntryProperties = schoolEntryProperties;
   }
 
   @Override
@@ -82,8 +59,9 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
     Map<Row, T> rowValues = readRows();
 
     List<UUID> existingProcedureIds = fetchExistingProceduresIfNecessary(rowValues);
-    Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates =
-        fetchMergeCandidatesIfNecessary(rowValues);
+
+    Map<PersonKeyAttributes, List<M>> mergeCandidates =
+        fetchMergeCandidates(getChildKeyAttributesOfValidRows(rowValues));
 
     for (Entry<Row, T> entry : rowValues.entrySet()) {
       evaluateActionForRow(entry.getKey(), entry.getValue(), existingProcedureIds, mergeCandidates);
@@ -99,28 +77,22 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
     return schoolEntryService.collectExistingProcedures(procedureIds);
   }
 
-  private Map<PersonKeyAttributes, List<ProcedureWithChildData>> fetchMergeCandidatesIfNecessary(
-      Map<Row, T> rowValues) {
-    Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates;
-    if (importType.supportsMerge()) {
-      Set<PersonKeyAttributes> rowsToSearchFor =
-          rowValues.values().stream()
-              .filter(row -> row.getProcedureId() == null)
-              .filter(SchoolEntryRowValues::isValid)
-              .map(SchoolEntryRowValues::getChildKeyAttributes)
-              .collect(StreamUtil.toLinkedHashSet());
-      mergeCandidates = schoolEntryService.searchForMergeCandidates(rowsToSearchFor);
-    } else {
-      mergeCandidates = Map.of();
-    }
-    return mergeCandidates;
+  private Set<PersonKeyAttributes> getChildKeyAttributesOfValidRows(Map<Row, T> rowValues) {
+    return rowValues.values().stream()
+        .filter(row -> row.getProcedureId() == null)
+        .filter(SchoolEntryRowValues::isValid)
+        .map(SchoolEntryRowValues::getChildKeyAttributes)
+        .collect(StreamUtil.toLinkedHashSet());
   }
 
+  protected abstract Map<PersonKeyAttributes, List<M>> fetchMergeCandidates(
+      Set<PersonKeyAttributes> childKeyAttributes);
+
   private void evaluateActionForRow(
       Row row,
       T rowValues,
       List<UUID> existingProcedureIds,
-      Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
+      Map<PersonKeyAttributes, List<M>> mergeCandidates) {
 
     if (rowValues.getProcedureId() != null) {
       if (existingProcedureIds.contains(rowValues.getProcedureId())) {
@@ -135,12 +107,9 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
       writeStatus(row, DUPLICATE_WITHIN_LIST);
       stats.countDuplicated();
     } else if (rowValues.isValid()) {
-      if (importType.supportsMerge()) {
-        evaluateActionWhenMergeIsEnabled(row, rowValues, mergeCandidates);
-      } else {
-        validRows.importableRows().add(rowValues);
-        stats.countCreated();
-      }
+
+      evaluateActionForValidRow(row, rowValues, mergeCandidates);
+
     } else {
       writeStatus(row, ERROR_INPUT_DATA);
       stats.countFailed();
@@ -152,98 +121,15 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
         .anyMatch(row -> row.isDuplicateRow(values));
   }
 
-  protected void evaluateActionWhenMergeIsEnabled(
-      Row row, T value, Map<PersonKeyAttributes, List<ProcedureWithChildData>> mergeCandidates) {
-    List<ProcedureWithChildData> procedures =
-        mergeCandidates.getOrDefault(value.getChildKeyAttributes(), List.of());
-    if (procedures.isEmpty()) {
-      validRows.importableRows().add(value);
-      stats.countCreated();
-    } else if (procedures.size() > 1
-        || procedures.getFirst().procedure().getProcedureType() != procedureTypeToMergeWith()) {
-      writeStatusAndReferenceId(
-          row, DUPLICATE_IN_ASSET, procedures.getFirst().procedure().getExternalId());
-      stats.countMergeFailed();
-    } else {
-      Assert.isTrue(
-          !schoolEntryProperties.isDirectProcedureTypeAssignmentOnImport(),
-          "Procedures of a draft type should not exist when direct procedure type assignment is enabled.");
-      ProcedureWithChildData procedure = procedures.getFirst();
-      UUID procedureId = procedure.procedure().getExternalId();
-      if (mergeCandidateMatchesImportValues(procedure, value)) {
-        if (validRows.mergeableRows().stream()
-            .anyMatch(mergeableRow -> mergeableRow.getProcedureId().equals(procedureId))) {
-          log.error("Procedure ID {} already found in a previous mergeable row", procedureId);
-          writeStatusAndReferenceId(row, MERGE_FAILED, procedureId);
-          stats.countMergeFailed();
-        } else {
-          value.setProcedureId(procedureId);
-          validRows.mergeableRows().add(value);
-          writeStatusAndProcedureId(row, MERGED_SUCCESSFULLY, procedureId);
-          stats.countMerged();
-        }
-      } else {
-        writeStatusAndReferenceId(row, DUPLICATE_IN_ASSET, procedureId);
-        stats.countMergeFailed();
-      }
-    }
-  }
-
-  private ProcedureType procedureTypeToMergeWith() {
-    return switch (importType) {
-      case CITIZEN_LIST -> ProcedureType.DRAFT_SCHOOL_IMPORT;
-      case SCHOOL_LIST -> ProcedureType.DRAFT_CITIZEN_OFFICE_IMPORT;
-      case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-    };
-  }
-
-  private boolean mergeCandidateMatchesImportValues(
-      ProcedureWithChildData mergeCandidate, T values) {
-    AddressDto address = mergeCandidate.child().address();
-    if (address instanceof PostboxAddressDto) {
-      return false;
-    }
-    DomesticAddressDto domesticAddressDto = (DomesticAddressDto) address;
-    AddressData importAddress = values.getChild().address();
-    boolean commonFieldsMatch =
-        Objects.equals(domesticAddressDto.street(), importAddress.street())
-            && Objects.equals(domesticAddressDto.city(), importAddress.city())
-            && Objects.equals(domesticAddressDto.houseNumber(), importAddress.houseNumber())
-            && Objects.equals(domesticAddressDto.postalCode(), importAddress.postalCode())
-            && Objects.equals(domesticAddressDto.addressAddition(), importAddress.addressAddition())
-            && Objects.equals(
-                mergeCandidate.child().gender(),
-                Optional.ofNullable(values.getChild().gender()).orElse(GenderDto.NOT_SPECIFIED))
-            && (mergeCandidate.procedure().getSchoolYear() == null
-                || Objects.equals(mergeCandidate.procedure().getSchoolYear(), schoolYear));
-
-    if (!commonFieldsMatch) {
-      return false;
-    }
-
-    return switch (importType) {
-      case SCHOOL_LIST ->
-          (mergeCandidate.procedure().getSchoolId() == null
-                  || Objects.equals(mergeCandidate.procedure().getSchoolId(), schoolId))
-              && (mergeCandidate.procedure().getLocationId() == null
-                  || Objects.equals(mergeCandidate.procedure().getLocationId(), locationId));
-      case CITIZEN_LIST ->
-          (mergeCandidate.child().placeOfBirth() == null
-                  || Objects.equals(
-                      mergeCandidate.child().placeOfBirth(), values.getChild().placeOfBirth()))
-              && (mergeCandidate.child().countryOfBirth() == null
-                  || Objects.equals(
-                      mergeCandidate.child().countryOfBirth(), values.getChild().countryOfBirth()));
-      case PAST_PROCEDURE_LIST -> throw ExceptionUtil.mergeNotSupportedForPastProcedureImport();
-    };
-  }
+  protected abstract void evaluateActionForValidRow(
+      Row row, T value, Map<PersonKeyAttributes, List<M>> mergeCandidates);
 
   @Override
   protected void createProceduresAndWriteResults() {
     List<T> importableRows = validRows.importableRows();
     try {
       List<SchoolEntryProcedure> createdProcedures = createProcedures(importableRows);
-      writeProcedureIdsInSheet(importableRows, createdProcedures);
+      writeProcedureIdsInSheet(importableRows, createdProcedures, IMPORTED_SUCCESSFULLY);
     } catch (Exception e) {
       log.error("Failure during creating new procedures.", e);
       writeFailedStatusInSheet(importableRows);
@@ -253,25 +139,25 @@ public abstract class SchoolEntryImporter<T extends SchoolEntryRowValues, C exte
 
   protected abstract List<SchoolEntryProcedure> createProcedures(List<T> importableRows);
 
-  private void writeProcedureIdsInSheet(
-      List<T> importableRows, List<SchoolEntryProcedure> createdProcedures) {
+  protected void writeProcedureIdsInSheet(
+      List<T> importableRows,
+      List<SchoolEntryProcedure> createdProcedures,
+      ImportStatus importStatus) {
     for (int i = 0; i < importableRows.size(); i++) {
       T rowValues = importableRows.get(i);
       SchoolEntryProcedure createdProcedure = createdProcedures.get(i);
 
       Row row = rowValues.getRow();
-      writeStatusAndProcedureId(row, IMPORTED_SUCCESSFULLY, createdProcedure.getExternalId());
+      writeStatusAndProcedureId(row, importStatus, createdProcedure.getExternalId());
     }
   }
 
   @Override
   protected void mergeProceduresAndWriteResults() {
-    if (importType.supportsMerge()) {
-      List<T> mergeableRows = validRows.mergeableRows();
-      List<UUID> failedProcedureIds = mergeProceduresAndGetFailedProcedureIds(mergeableRows);
-      writeMergedFailedStatusInSheet(mergeableRows, failedProcedureIds);
-      stats.correctMergeToFailed(failedProcedureIds.size());
-    }
+    List<T> mergeableRows = validRows.mergeableRows();
+    List<UUID> failedProcedureIds = mergeProceduresAndGetFailedProcedureIds(mergeableRows);
+    writeMergedFailedStatusInSheet(mergeableRows, failedProcedureIds);
+    stats.correctMergeToFailed(failedProcedureIds.size());
   }
 
   protected abstract List<UUID> mergeProceduresAndGetFailedProcedureIds(List<T> mergeableRows);
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
index 046a13836..d95380eab 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowReader.java
@@ -8,11 +8,10 @@ package de.eshg.schoolentry.importer;
 import static de.eshg.schoolentry.importer.SchoolListColumn.*;
 
 import de.eshg.lib.xlsximport.ColumnAccessor;
+import de.eshg.lib.xlsximport.ErrorHandler;
 import de.eshg.lib.xlsximport.RowReader;
 import de.eshg.schoolentry.business.model.ImportChildData;
 import java.util.List;
-import java.util.function.BiConsumer;
-import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
 
 public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolListColumn> {
@@ -24,7 +23,7 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
   @Override
   protected SchoolListRowValues read(ColumnAccessor<SchoolListColumn> col) {
     SchoolListRowValues result = new SchoolListRowValues();
-    BiConsumer<Cell, String> errorHandler = createErrorHandler(result);
+    ErrorHandler errorHandler = createErrorHandler(result);
 
     result.setChild(readChildData(col, errorHandler));
     result.setStatus(readStatus(col, STATUS, errorHandler));
@@ -36,7 +35,7 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
   }
 
   private ImportChildData readChildData(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return new ImportChildData(
         cellAsString(col, FIRST_NAME, errorHandler),
         cellAsString(col, LAST_NAME, errorHandler),
@@ -50,18 +49,17 @@ public class SchoolListRowReader extends RowReader<SchoolListRowValues, SchoolLi
         readPhoneNumber(col, errorHandler));
   }
 
-  private String readPhoneNumber(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+  private String readPhoneNumber(ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsString(col, PHONE_NUMBER, true, true, errorHandler);
   }
 
   private boolean readEntryLevelFlag(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsFlag(col, ENTRY_LEVEL, errorHandler);
   }
 
   private boolean readEarlyExaminationFlag(
-      ColumnAccessor<SchoolListColumn> col, BiConsumer<Cell, String> errorHandler) {
+      ColumnAccessor<SchoolListColumn> col, ErrorHandler errorHandler) {
     return cellAsFlag(col, EARLY_EXAMINATION, errorHandler);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
index bf36ee839..3e97ab64e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/importer/SchoolListRowValueMapper.java
@@ -11,6 +11,7 @@ import de.eshg.schoolentry.business.model.MergeProcedureData;
 import de.eshg.schoolentry.mapper.PersonMapper;
 import de.eshg.schoolentry.util.ProcedureTypeAssignmentHelper;
 import java.time.Year;
+import java.util.List;
 
 public class SchoolListRowValueMapper implements RowValueMapper<SchoolListRowValues> {
 
@@ -43,7 +44,7 @@ public class SchoolListRowValueMapper implements RowValueMapper<SchoolListRowVal
         values.getProcedureId(),
         null,
         null,
-        null,
+        List.of(),
         values.getChild().phoneNumber(),
         values.isEntryLevel(),
         values.isEarlyExamination());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
index 363402079..f9768503e 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/AnamnesisMapper.java
@@ -57,7 +57,10 @@ public class AnamnesisMapper {
             anamnesis.getNumberOfSiblings(),
             anamnesis.getSiblingsBirthYears()),
         new DaycareAndSchoolInfoDto(
-            anamnesis.getInDaycareSince(), anamnesis.getDaycareName(), anamnesis.getSchoolName()),
+            anamnesis.getWasInDaycare(),
+            anamnesis.getInDaycareSince(),
+            anamnesis.getDaycareName(),
+            anamnesis.getSchoolName()),
         new FamilyHistoryInfoDto(
             anamnesis.getSpectaclesInFamily(), anamnesis.getChronicIllnessOrDisabilityInFamily()),
         new DevelopmentInfoDto(
@@ -121,6 +124,7 @@ public class AnamnesisMapper {
     anamnesis.setGestationalAge(dto.developmentInfo().gestationalAge());
     anamnesis.setDevelopmentConspicuities(dto.developmentInfo().developmentConspicuities());
     anamnesis.setInfancyConspicuities(dto.developmentInfo().infancyConspicuities());
+    anamnesis.setWasInDaycare(dto.daycareAndSchoolInfo().wasInDaycare());
     anamnesis.setInDaycareSince(dto.daycareAndSchoolInfo().inDaycareSince());
     anamnesis.setDaycareName(dto.daycareAndSchoolInfo().daycareName());
     anamnesis.setSchoolName(dto.daycareAndSchoolInfo().schoolName());
@@ -231,6 +235,7 @@ public class AnamnesisMapper {
             ? null
             : citizenAnamnesisDto.additionalChildInfo().siblingsBirthYears());
 
+    anamnesis.setWasInDaycare(citizenAnamnesisDto.daycareAndSchoolInfo().wasInDaycare());
     anamnesis.setInDaycareSince(citizenAnamnesisDto.daycareAndSchoolInfo().inDaycareSince());
     anamnesis.setDaycareName(citizenAnamnesisDto.daycareAndSchoolInfo().daycareName());
     anamnesis.setSchoolName(citizenAnamnesisDto.daycareAndSchoolInfo().schoolName());
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
index 4655d4a69..bc5293ba1 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/mapper/ProcedureMapper.java
@@ -46,7 +46,9 @@ public final class ProcedureMapper {
         procedureDetailsData.createdAt(),
         procedureDetailsData.modifiedAt(),
         WaitingRoomMapper.mapToDto(procedureDetailsData.waitingRoom()),
-        procedureDetailsData.schoolInfoLetterCreatedAt());
+        procedureDetailsData.schoolInfoLetterCreatedAt(),
+        procedureDetailsData.hasInformationBlock(),
+        procedureDetailsData.hasBeenClosed());
   }
 
   public static ProcedureDto mapProcedureToDto(ProcedureData procedureData) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
index a63fe477d..e59906522 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/invitation/InvitationGenerator.java
@@ -16,7 +16,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.business.model.ChildData;
 import de.eshg.schoolentry.pdf.AbstractGenerator;
@@ -189,7 +188,6 @@ public class InvitationGenerator extends AbstractGenerator {
             .formatted(
                 invitationData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
index fd12f8e14..1ec6c79bd 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/medicalreport/MedicalReportGenerator.java
@@ -11,7 +11,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.api.CreateMedicalReportRequest;
 import de.eshg.schoolentry.business.model.ChildDetailsData;
@@ -88,7 +87,6 @@ public class MedicalReportGenerator extends AbstractGenerator {
                 addressee,
                 medicalReportData.child().name().replace(" ", "_"),
                 now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, bytes, pdfMetaData);
   }
 }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
index 76a0a90ed..c7686906f 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterExaminationMapper.java
@@ -53,7 +53,9 @@ public class SchoolInfoLetterExaminationMapper {
             procedureDetails.child().dateOfBirth().format(DATE_FORMATTER)),
         YEAR_FORMATTER.format(procedureDetails.schoolYear()),
         DATE_FORMATTER.format(
-            procedureDetails.appointment().getAppointmentEnd().atZone(clock.getZone())),
+            procedure.getExaminationDate() != null
+                ? procedure.getExaminationDate()
+                : procedureDetails.appointment().getAppointmentEnd().atZone(clock.getZone())),
         new SchoolInfoLetterExaminationType(
             mapType(procedureDetails.type()),
             List.of(BACK_REGULAR, BACK_ENTRY_LEVEL)
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
index 451b8f3bd..1becd091c 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterGenerator.java
@@ -11,7 +11,6 @@ import de.eshg.lib.document.generator.department.DepartmentClient;
 import de.eshg.lib.document.generator.department.DepartmentLogo;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import de.eshg.schoolentry.api.CreateSchoolInfoLetterRequest;
 import de.eshg.schoolentry.business.model.ProcedureDetailsData;
@@ -73,8 +72,7 @@ public class SchoolInfoLetterGenerator extends AbstractGenerator {
     String filename =
         "Schulinfobrief_%s.pdf"
             .formatted(now.format(ReportGeneratorConstants.FILENAME_TIMESTAMP_FORMAT));
-    return FileFactory.createPdfWithMetaData(
-        filename, ProcedureFileType.PDF, baos.toByteArray(), pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(filename, baos.toByteArray(), pdfMetaData);
   }
 
   @VisibleForTesting
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
index 876a39405..0e3349bec 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/pdf/schoolinfoletter/SchoolInfoLetterValidator.java
@@ -29,7 +29,8 @@ public class SchoolInfoLetterValidator {
 
     result.put(
         RequiredProcedureData.DETAILS,
-        procedure.getSchoolId() != null && procedure.getAppointment() != null);
+        procedure.getSchoolId() != null
+            && (procedure.getAppointment() != null || procedure.getExaminationDate() != null));
     result.put(RequiredProcedureData.HEARING_TEST, validate(procedure.getHearingTestResult()));
     result.put(
         RequiredProcedureData.EYE_EXAMINATION, validate(procedure.getEyeExaminationResult()));
@@ -270,6 +271,7 @@ public class SchoolInfoLetterValidator {
           anamnesisProperty(Anamnesis::getUnderMedicalTreatmentFor),
           anamnesisProperty(Anamnesis::getVisionImpairment),
           anamnesisProperty(Anamnesis::getVisionSchoolSince),
+          anamnesisProperty(Anamnesis::getWasInDaycare),
           eyeExaminationProperty(EyeExaminationResult::getAmblyopia),
           eyeExaminationProperty(EyeExaminationResult::getAstigmatism),
           eyeExaminationProperty(EyeExaminationResult::getColorVisionDisorder),
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
index 425005af3..3547ac6c7 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/statistics/SchoolEntryStatisticsService.java
@@ -8,6 +8,10 @@ package de.eshg.schoolentry.statistics;
 import static de.eshg.schoolentry.statistics.DevelopmentScreeningStatistics.*;
 import static java.lang.Math.abs;
 
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment_;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.ProcedureType;
+import de.eshg.lib.procedure.domain.model.Procedure_;
 import de.eshg.lib.statistics.AbstractStatisticsService;
 import de.eshg.lib.statistics.api.SubjectType;
 import de.eshg.lib.statistics.util.AttributeInfo;
@@ -19,11 +23,18 @@ import de.eshg.schoolentry.statistics.options.*;
 import de.eshg.schoolentry.statistics.options.BooleanWithUnknown;
 import de.eshg.schoolentry.statistics.options.DoctorLetterValue;
 import jakarta.annotation.Nullable;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.JoinType;
+import jakarta.persistence.criteria.Path;
+import jakarta.persistence.criteria.Predicate;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.Temporal;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -58,6 +69,76 @@ public class SchoolEntryStatisticsService extends AbstractStatisticsService<Scho
     return SubjectType.PERSON;
   }
 
+  @Override
+  protected Specification<SchoolEntryProcedure> getProcedureSpecification(
+      Instant startTimestamp, Instant endTimestamp) {
+    return (root, query, criteriaBuilder) -> {
+      Path<LocalDate> examinationDatePath = root.get(SchoolEntryProcedure_.examinationDate);
+
+      Predicate examinationDateInTimeRange =
+          isInTimeRangeIfPresent(
+              criteriaBuilder,
+              examinationDatePath,
+              toLocalDate(startTimestamp),
+              toLocalDate(endTimestamp));
+
+      Path<Instant> appointmentStartPath =
+          root.join(SchoolEntryProcedure_.appointment, JoinType.LEFT)
+              .get(Appointment_.appointmentStart);
+
+      Predicate appointmentStartInTimeRange =
+          isInTimeRangeIfPresent(
+              criteriaBuilder, appointmentStartPath, startTimestamp, endTimestamp);
+
+      // Paranoia check - this should be true for all closed procedures
+      Predicate examinationDateOrAppointmentStartNotNull =
+          criteriaBuilder.or(
+              criteriaBuilder.isNotNull(examinationDatePath),
+              criteriaBuilder.isNotNull(appointmentStartPath));
+
+      Predicate isClosed =
+          criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.CLOSED);
+
+      Predicate isCanChild =
+          criteriaBuilder.equal(root.get(Procedure_.procedureType), ProcedureType.CAN_CHILD);
+
+      Path<SchoolFeedback> schoolFeedbackPath =
+          root.join(SchoolEntryProcedure_.developmentScreeningResult)
+              .get(DevelopmentScreening_.schoolFeedback);
+
+      Predicate hasNegativeFeedback =
+          criteriaBuilder.and(
+              criteriaBuilder.isNotNull(schoolFeedbackPath),
+              criteriaBuilder.equal(schoolFeedbackPath, SchoolFeedback.NEGATIVE));
+
+      Predicate isNotCanChildWithNegativeFeedback =
+          criteriaBuilder.not(criteriaBuilder.and(isCanChild, hasNegativeFeedback));
+
+      return criteriaBuilder.and(
+          examinationDateInTimeRange,
+          appointmentStartInTimeRange,
+          examinationDateOrAppointmentStartNotNull,
+          isClosed,
+          isNotCanChildWithNegativeFeedback);
+    };
+  }
+
+  private LocalDate toLocalDate(Instant instant) {
+    return instant.atZone(clock.getZone()).toLocalDate();
+  }
+
+  private <T extends Temporal & Comparable<? super T>> Predicate isInTimeRangeIfPresent(
+      CriteriaBuilder criteriaBuilder,
+      Expression<T> temporalPath,
+      T startInclusive,
+      T endExclusive) {
+    return criteriaBuilder.or(
+        criteriaBuilder.isNull(temporalPath),
+        criteriaBuilder.and(
+            criteriaBuilder.greaterThanOrEqualTo(temporalPath, startInclusive),
+            criteriaBuilder.lessThan(temporalPath, endExclusive)));
+  }
+
   @Override
   protected Object getSpecificValue(
       SchoolEntryProcedure procedure, AttributeInfo attributeInfo, UUID dataSourceId) {
@@ -484,15 +565,21 @@ public class SchoolEntryStatisticsService extends AbstractStatisticsService<Scho
         || procedure.getAnamnesis() == null) {
       return null;
     }
-    LocalDate appointmentDate =
-        procedure.getAppointment().getAppointmentStart().atZone(clock.getZone()).toLocalDate();
-    LocalDate inDaycareSince = procedure.getAnamnesis().getInDaycareSince();
 
-    if (inDaycareSince == null) {
+    Boolean wasInDaycare = procedure.getAnamnesis().getWasInDaycare();
+    if (Boolean.FALSE.equals(wasInDaycare)) {
+      return Daycare.NO.getValue();
+    }
+
+    LocalDate inDaycareSince = procedure.getAnamnesis().getInDaycareSince();
+    if (wasInDaycare == null || inDaycareSince == null) {
       return Daycare.UNKNOWN.getValue();
-    } else {
-      return getDaycareValue(appointmentDate, inDaycareSince);
     }
+
+    LocalDate appointmentDate =
+        procedure.getAppointment().getAppointmentStart().atZone(clock.getZone()).toLocalDate();
+
+    return getDaycareValue(appointmentDate, inDaycareSince);
   }
 
   public static String getDaycareValue(LocalDate appointmentDate, LocalDate inDaycareSince) {
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
index d455c4e64..843b95b8a 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryProceduresPopulator.java
@@ -21,7 +21,6 @@ import de.eshg.schoolentry.LabelController;
 import de.eshg.schoolentry.SchoolEntryController;
 import de.eshg.schoolentry.api.*;
 import de.eshg.schoolentry.api.anamnesis.*;
-import de.eshg.schoolentry.config.SchoolEntryFeatureToggle;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
 import de.eshg.schoolentry.domain.repository.SchoolEntryProcedureRepository;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
@@ -53,7 +52,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
   private final SchoolEntryProcedureRepository schoolEntryProcedureRepository;
   private final LabelController labelController;
   private final BaseTestHelperApi baseTestHelperApi;
-  private final SchoolEntryFeatureToggle featureToggle;
   private final ContactApi contactApi;
   private final AppointmentBlockProperties appointmentBlockProperties;
 
@@ -65,7 +63,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
       SchoolEntryProcedureRepository schoolEntryProcedureRepository,
       LabelController labelController,
       BaseTestHelperApi baseTestHelperApi,
-      SchoolEntryFeatureToggle featureToggle,
       @SuppressWarnings("unused") // Used to define a dependency
           AppointmentBlockGroupsPopulator appointmentBlockGroupsPopulator,
       EnvironmentConfig environmentConfig,
@@ -82,7 +79,6 @@ public class SchoolEntryProceduresPopulator extends BasePopulator<CreateProcedur
     this.schoolEntryProcedureRepository = schoolEntryProcedureRepository;
     this.labelController = labelController;
     this.baseTestHelperApi = baseTestHelperApi;
-    this.featureToggle = featureToggle;
     this.contactApi = contactApi;
     this.appointmentBlockProperties = appointmentBlockProperties;
   }
diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
index 1b061d6c8..f4cac31b7 100644
--- a/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
+++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/util/ProgressEntryUtil.java
@@ -7,49 +7,56 @@ package de.eshg.schoolentry.util;
 
 import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.File;
-import de.eshg.lib.procedure.domain.model.ProgressEntry;
+import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.model.TriggerType;
+import de.eshg.lib.procedure.progressentry.ProgressEntryService;
 import de.eshg.schoolentry.domain.model.SchoolEntryProcedure;
+import org.springframework.stereotype.Component;
 
+@Component
 public class ProgressEntryUtil {
-  private ProgressEntryUtil() {}
+  private final ProgressEntryService<SchoolEntryProcedure> progressEntryService;
 
-  public static void addProgressEntry(
+  public ProgressEntryUtil(ProgressEntryService<SchoolEntryProcedure> progressEntryService) {
+    this.progressEntryService = progressEntryService;
+  }
+
+  public void addProgressEntry(
       SchoolEntryProcedure procedure, SchoolEntrySystemProgressEntryType progressEntryType) {
     addProgressEntry(procedure, progressEntryType, TriggerType.SYSTEM_AUTOMATIC);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       TriggerType triggerType) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), null, triggerType);
 
-    procedure.addProgressEntry(progressEntry);
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       String changeDescription,
       File file) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), changeDescription, TriggerType.SYSTEM_AUTOMATIC);
-    progressEntry.setFile(file);
-    procedure.addProgressEntry(progressEntry);
+
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry, file);
   }
 
-  public static void addProgressEntry(
+  public void addProgressEntry(
       SchoolEntryProcedure procedure,
       SchoolEntrySystemProgressEntryType progressEntryType,
       File file) {
-    ProgressEntry progressEntry =
+    SystemProgressEntry progressEntry =
         SystemProgressEntryFactory.createSystemProgressEntry(
             progressEntryType.name(), TriggerType.SYSTEM_AUTOMATIC);
-    progressEntry.setFile(file);
-    procedure.addProgressEntry(progressEntry);
+
+    progressEntryService.addSystemProgressEntry(procedure, progressEntry, file);
   }
 }
diff --git a/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties b/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
index 126244685..e7c789fc2 100644
--- a/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
+++ b/backend/school-entry/src/main/resources/application-health-department-frankfurt.properties
@@ -2,3 +2,5 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[REGULAR_EXAMINA
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[ENTRY_LEVEL]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[CAN_CHILD]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SPECIAL_NEEDS]=60m
+
+# opening hours for health department frankfurt are configured in application.properties
diff --git a/backend/school-entry/src/main/resources/application-preview-features.properties b/backend/school-entry/src/main/resources/application-preview-features.properties
index af44e656e..ab33c9711 100644
--- a/backend/school-entry/src/main/resources/application-preview-features.properties
+++ b/backend/school-entry/src/main/resources/application-preview-features.properties
@@ -1,3 +1 @@
-de.eshg.schoolentry.feature-toggle.enabled-new-features=\
-  CLOSE_PROCEDURE,\
-  REOPEN_PROCEDURE
+de.eshg.schoolentry.feature-toggle.enabled-new-features=
diff --git a/backend/school-entry/src/main/resources/application.properties b/backend/school-entry/src/main/resources/application.properties
index 19ad8e9e0..0a137479d 100644
--- a/backend/school-entry/src/main/resources/application.properties
+++ b/backend/school-entry/src/main/resources/application.properties
@@ -21,7 +21,7 @@ spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.i
 
 eshg.citizen-portal.reverse-proxy.url=http://localhost:4001
 
-# Für alle Kinder, die bis einschließlich 1. Juli geboren sind und damit bis zum 30. Juni das sechste Lebensjahr vollenden, beginnt am 1. August die Schulpflicht.
+# F�r alle Kinder, die bis einschlie�lich 1. Juli geboren sind und damit bis zum 30. Juni das sechste Lebensjahr vollenden, beginnt am 1. August die Schulpflicht.
 # See https://kultus.hessen.de/schulsystem/schulformen-und-bildungsgaenge/grundschule/grundschule
 de.eshg.schoolentry.max-date-of-birth-for-regular-school-entry=--07-01
 de.eshg.schoolentry.max-date-of-birth-for-regular-school-entry-is-inclusive=true
@@ -42,3 +42,13 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[REGULAR_EXAMINA
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[ENTRY_LEVEL]=45m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[CAN_CHILD]=45m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SPECIAL_NEEDS]=45m
+
+de.eshg.schoolentry.opening-hours.de[0]=Mo - Do 07:30 - 16:00 Uhr
+de.eshg.schoolentry.opening-hours.de[1]=Nur nach Terminabsprache.
+de.eshg.schoolentry.opening-hours.de[2]=Fr 07:30 - 14:00 Uhr
+de.eshg.schoolentry.opening-hours.de[3]=Nur nach Terminabsprache.
+
+de.eshg.schoolentry.opening-hours.en[0]=Mon - Thu 07:30 AM - 4:00 PM
+de.eshg.schoolentry.opening-hours.en[1]=By appointment only.
+de.eshg.schoolentry.opening-hours.en[2]=Fri 07:30 AM - 2:00 PM
+de.eshg.schoolentry.opening-hours.en[3]=By appointment only.
diff --git a/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml b/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml
new file mode 100644
index 000000000..2ef4593b8
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0051_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml b/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml
new file mode 100644
index 000000000..1b1ac8a9f
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0052_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml b/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml
new file mode 100644
index 000000000..afab6bee3
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0053_add_was_in_daycare_flag.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729689608899-1">
+    <addColumn tableName="anamnesis">
+      <column name="was_in_daycare" type="bool"/>
+    </addColumn>
+  </changeSet>
+  <changeSet id="1730359513772-1" author="GA-Lotse">
+    <sql>
+      UPDATE anamnesis
+      SET was_in_daycare = TRUE
+      WHERE in_daycare_since IS NOT NULL
+         OR daycare_name IS NOT NULL
+    </sql>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml b/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 000000000..609846864
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0054_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml b/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml
new file mode 100644
index 000000000..fc254c7a0
--- /dev/null
+++ b/backend/school-entry/src/main/resources/migrations/0055_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/school-entry/src/main/resources/migrations/changelog.xml b/backend/school-entry/src/main/resources/migrations/changelog.xml
index 803cedab6..29bb3f240 100644
--- a/backend/school-entry/src/main/resources/migrations/changelog.xml
+++ b/backend/school-entry/src/main/resources/migrations/changelog.xml
@@ -60,5 +60,10 @@
   <include file="migrations/0048_add_medical_registry_procedure_types.xml"/>
   <include file="migrations/0049_remove_key_document_type_enum.xml"/>
   <include file="migrations/0050_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0051_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0052_cemetery_sequence.xml"/>
+  <include file="migrations/0053_add_was_in_daycare_flag.xml"/>
+  <include file="migrations/0054_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0055_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/settings.gradle b/backend/settings.gradle
index ed271b2c9..dc4d09efb 100644
--- a/backend/settings.gradle
+++ b/backend/settings.gradle
@@ -16,24 +16,22 @@ dependencyResolutionManagement {
 
     versionCatalogs {
         libs {
-            version('keycloak', '25.0.6')
-            library('keycloak-admin-client', 'org.keycloak', 'keycloak-admin-client').versionRef('keycloak')
-            library('keycloak-common', 'org.keycloak', 'keycloak-common').versionRef('keycloak')
-            library('keycloak-core', 'org.keycloak', 'keycloak-core').versionRef('keycloak')
-            library('keycloak-server-spi', 'org.keycloak', 'keycloak-server-spi').versionRef('keycloak')
-            library('keycloak-server-spi-private', 'org.keycloak', 'keycloak-server-spi-private').versionRef('keycloak')
-            library('keycloak-services', 'org.keycloak', 'keycloak-services').versionRef('keycloak')
-            bundle('keycloak-client', [
-                'keycloak-admin-client',
-                'keycloak-core',
-                'keycloak-common'
-            ])
+            version('keycloak-client', '26.0.1')
+            version('keycloak-server', '26.0.2')
+
+            library('keycloak-client-admin-client', 'org.keycloak', 'keycloak-admin-client').versionRef('keycloak-client')
+
+            library('keycloak-server-common', 'org.keycloak', 'keycloak-common').versionRef('keycloak-server')
+            library('keycloak-server-core', 'org.keycloak', 'keycloak-core').versionRef('keycloak-server')
+            library('keycloak-server-server-spi', 'org.keycloak', 'keycloak-server-spi').versionRef('keycloak-server')
+            library('keycloak-server-server-spi-private', 'org.keycloak', 'keycloak-server-spi-private').versionRef('keycloak-server')
+            library('keycloak-server-services', 'org.keycloak', 'keycloak-services').versionRef('keycloak-server')
             bundle('keycloak-server', [
-                'keycloak-core',
-                'keycloak-common',
-                'keycloak-server-spi',
-                'keycloak-server-spi-private',
-                'keycloak-services',
+                'keycloak-server-core',
+                'keycloak-server-common',
+                'keycloak-server-server-spi',
+                'keycloak-server-server-spi-private',
+                'keycloak-server-services',
             ])
         }
     }
diff --git a/backend/spatz/build.gradle b/backend/spatz/build.gradle
index b05b0ae41..431bd29bb 100644
--- a/backend/spatz/build.gradle
+++ b/backend/spatz/build.gradle
@@ -32,7 +32,7 @@ dependencies {
     }
     testImplementation testFixtures(project(':lib-service-directory-admin-api'))
     testImplementation testFixtures(project(':lib-relay'))
-    testImplementation libs.bundles.keycloak.client
+    testImplementation libs.keycloak.client.admin.client
     testImplementation 'org.springframework.boot:spring-boot-starter-web'
     testImplementation 'io.fabric8:kubernetes-client:6.10.0'
 }
@@ -65,4 +65,4 @@ createDockerfile {
 
 dependencyTrack {
     projectId = project.findProperty('dependency-track-project-id-spatz') ?: "unspecified"
-}
\ No newline at end of file
+}
diff --git a/backend/spatz/gradle.lockfile b/backend/spatz/gradle.lockfile
index 307aff99b..b2ca9f5dd 100644
--- a/backend/spatz/gradle.lockfile
+++ b/backend/spatz/gradle.lockfile
@@ -19,10 +19,7 @@ com.github.docker-java:docker-java-transport-zerodep:3.3.6=testCompileClasspath,
 com.github.docker-java:docker-java-transport:3.3.6=testCompileClasspath,testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.9.2=testRuntimeClasspath
 com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.2=testRuntimeClasspath
-com.github.java-json-tools:btf:1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:jackson-coreutils:2.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.github.java-json-tools:json-patch:1.13=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.github.java-json-tools:msg-simple:1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -48,7 +45,6 @@ com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath
 commons-codec:commons-codec:1.16.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 commons-io:commons-io:2.11.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-commons-logging:commons-logging:1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:commons-lang:1.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 de.cronn:postgres-snapshot-util:1.3.3=testRuntimeClasspath
 de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath
@@ -126,9 +122,9 @@ org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClass
 org.apache.commons:commons-lang3:3.14.0=testRuntimeClasspath
 org.apache.httpcomponents:httpclient:4.5.14=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.httpcomponents:httpcore:4.4.16=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-core:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-dom:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.apache.james:apache-mime4j-storage:0.8.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-core:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-dom:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.apache.james:apache-mime4j-storage:0.8.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-api:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.logging.log4j:log4j-to-slf4j:2.23.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.apache.tomcat.embed:tomcat-embed-core:10.1.31=testCompileClasspath,testRuntimeClasspath
@@ -159,15 +155,16 @@ org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
 org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
 org.java-websocket:Java-WebSocket:1.5.7=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.jboss.logging:jboss-logging:3.5.3.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client-api:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-client:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core-spi:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-core:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jackson2-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-jaxb-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss.resteasy:resteasy-multipart-provider:6.2.7.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.jboss:jandex:2.4.4.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client-api:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-client:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core-spi:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-core:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jackson2-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-jaxb-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss.resteasy:resteasy-multipart-provider:6.2.9.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.jboss:jandex:2.4.5.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testRuntimeClasspath
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=testRuntimeClasspath
@@ -181,9 +178,8 @@ org.junit.platform:junit-platform-commons:1.10.5=testCompileClasspath,testRuntim
 org.junit.platform:junit-platform-engine:1.10.5=testRuntimeClasspath
 org.junit.platform:junit-platform-launcher:1.10.5=testRuntimeClasspath
 org.junit:junit-bom:5.10.5=testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-admin-client:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-common:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-org.keycloak:keycloak-core:25.0.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-admin-client:26.0.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.keycloak:keycloak-client-common-synced:26.0.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
 org.mockito:mockito-junit-jupiter:5.11.0=testCompileClasspath,testRuntimeClasspath
diff --git a/backend/statistics/openApi.yaml b/backend/statistics/openApi.yaml
index c0b2bef50..b36ff723d 100644
--- a/backend/statistics/openApi.yaml
+++ b/backend/statistics/openApi.yaml
@@ -399,46 +399,6 @@ paths:
       tags:
       - GeoShape
   /statistic:
-    get:
-      operationId: getStatistics
-      parameters:
-      - in: query
-        name: sortKey
-        required: false
-        schema:
-          $ref: "#/components/schemas/StatisticSortKey"
-      - in: query
-        name: sortDirection
-        required: false
-        schema:
-          $ref: "#/components/schemas/SortDirection"
-      - in: query
-        name: page
-        required: false
-        schema:
-          type: integer
-          format: int32
-          default: 0
-          minimum: 0
-      - in: query
-        name: pageSize
-        required: false
-        schema:
-          type: integer
-          format: int32
-          default: 25
-          maximum: 200
-          minimum: 1
-      responses:
-        "200":
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/GetStatisticsResponse"
-          description: All statistics
-      summary: Get all statistics
-      tags:
-      - Statistic
     post:
       operationId: addStatistic
       requestBody:
@@ -646,6 +606,25 @@ paths:
       summary: Add a diagram to the evaluation
       tags:
       - Evaluation
+  /statistic/overview:
+    post:
+      operationId: getStatistics
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/GetStatisticsRequest"
+        required: true
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetStatisticsResponse"
+          description: All statistics
+      summary: Get all statistics
+      tags:
+      - Statistic
   /statistic/report-series:
     post:
       operationId: addReportSeries
@@ -667,6 +646,22 @@ paths:
       summary: Add a report series
       tags:
       - ReportSeries
+  /statistic/report-series/deactivate/{reportSeriesId}:
+    patch:
+      operationId: deactivateReportSeries
+      parameters:
+      - in: path
+        name: reportSeriesId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: Returned when the report series is deactivated
+      summary: Deactivate a report series
+      tags:
+      - ReportSeries
   /statistic/report-series/overview:
     post:
       operationId: getReportOverview
@@ -715,10 +710,7 @@ paths:
         content:
           application/json:
             schema:
-              oneOf:
-              - $ref: "#/components/schemas/ActivateAutoReportSeriesRequest"
-              - $ref: "#/components/schemas/DeactivateAutoReportSeriesRequest"
-              - $ref: "#/components/schemas/UpdateNameAndDescriptionReportSeriesRequest"
+              $ref: "#/components/schemas/UpdateReportSeriesRequest"
         required: true
       responses:
         "200":
@@ -727,7 +719,7 @@ paths:
               schema:
                 $ref: "#/components/schemas/ReportSeries"
           description: The patched report series
-      summary: Change title and description of a report series or change activation
+      summary: Change title and description of a report series
       tags:
       - ReportSeries
   /statistic/report/{reportId}:
@@ -1094,15 +1086,6 @@ components:
           type: string
       required:
       - '@type'
-    AbstractUpdateReportSeriesRequest:
-      type: object
-      discriminator:
-        propertyName: '@type'
-      properties:
-        '@type':
-          type: string
-      required:
-      - '@type'
     AbstractUpdateStatisticRequest:
       type: object
       discriminator:
@@ -1112,25 +1095,6 @@ components:
           type: string
       required:
       - '@type'
-    ActivateAutoReportSeriesRequest:
-      type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
-      - type: object
-        properties:
-          frequency:
-            $ref: "#/components/schemas/Frequency"
-          reportingPeriod:
-            $ref: "#/components/schemas/ReportingPeriod"
-          startMonth:
-            type: integer
-            format: int32
-            maximum: 12
-            minimum: 1
-      required:
-      - frequency
-      - reportingPeriod
-      - startMonth
     AddAutoReportSeriesRequest:
       type: object
       allOf:
@@ -1802,10 +1766,6 @@ components:
       required:
       - code
       - name
-    DeactivateAutoReportSeriesRequest:
-      type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
     DecimalAttribute:
       type: object
       allOf:
@@ -2010,6 +1970,8 @@ components:
         userId:
           type: string
           format: uuid
+        withoutAnonymizationAllowed:
+          type: boolean
       required:
       - analysisInfos
       - createdAt
@@ -2017,6 +1979,7 @@ components:
       - id
       - name
       - userId
+      - withoutAnonymizationAllowed
     EvaluationTemplateInfo:
       type: object
       properties:
@@ -2519,6 +2482,26 @@ components:
       required:
       - disabledOldFeatures
       - enabledNewFeatures
+    GetStatisticsRequest:
+      type: object
+      properties:
+        anonymizationValue:
+          type: boolean
+        page:
+          type: integer
+          format: int32
+          default: 0
+          minimum: 0
+        pageSize:
+          type: integer
+          format: int32
+          default: 25
+          maximum: 200
+          minimum: 1
+        sortDirection:
+          $ref: "#/components/schemas/SortDirection"
+        sortKey:
+          $ref: "#/components/schemas/StatisticSortKey"
     GetStatisticsResponse:
       type: object
       properties:
@@ -3021,6 +3004,12 @@ components:
         createdAt:
           type: string
           format: date-time
+        dataSourceNames:
+          type: array
+          items:
+            type: string
+          maxItems: 2147483647
+          minItems: 1
         id:
           type: string
           format: uuid
@@ -3040,6 +3029,7 @@ components:
       required:
       - anonymized
       - createdAt
+      - dataSourceNames
       - id
       - name
       - state
@@ -3048,6 +3038,7 @@ components:
       - userId
     StatisticSortKey:
       type: string
+      default: CREATED_AT
       enum:
       - NAME
       - CREATED_AT
@@ -3061,6 +3052,7 @@ components:
       - CREATING
       - UPDATING
       - COPY_ONGOING
+      - DELETING
     StatisticsFeature:
       type: string
       enum:
@@ -3237,16 +3229,13 @@ components:
           type: string
       required:
       - name
-    UpdateNameAndDescriptionReportSeriesRequest:
+    UpdateReportSeriesRequest:
       type: object
-      allOf:
-      - $ref: "#/components/schemas/AbstractUpdateReportSeriesRequest"
-      - type: object
-        properties:
-          description:
-            type: string
-          name:
-            type: string
+      properties:
+        description:
+          type: string
+        name:
+          type: string
       required:
       - name
     UpdateStatisticNameRequest:
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
index 61328be52..317ab1514 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateController.java
@@ -11,7 +11,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.aggregation.DataSourceValidator;
 import de.eshg.statistics.aggregation.StatisticService;
-import de.eshg.statistics.api.AvailableDataSource;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
 import de.eshg.statistics.api.evaluationtemplate.AbstractAddEvaluationTemplateRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
@@ -136,6 +136,7 @@ public class EvaluationTemplateController {
   @Operation(summary = "Get the information for the expected template")
   public ExpectedEvaluationTemplateDto getTemplateInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     EvaluationTemplateData evaluationTemplateData =
         statisticService.getEvaluationTemplateData(statisticId);
     List<AvailableDataSource> relevantAvailableDataSources =
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
index 09b9005c5..a8d41a41e 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/EvaluationTemplateService.java
@@ -8,7 +8,7 @@ package de.eshg.statistics;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.rest.service.error.NotFoundException;
-import de.eshg.statistics.api.AvailableDataSource;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateFromEvaluationRequest;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
@@ -18,9 +18,11 @@ import de.eshg.statistics.api.evaluationtemplate.GetAllEvaluationTemplatesRespon
 import de.eshg.statistics.api.evaluationtemplate.GetEvaluationTemplatesRequest;
 import de.eshg.statistics.api.evaluationtemplate.GetEvaluationTemplatesResponse;
 import de.eshg.statistics.api.evaluationtemplate.UpdateEvaluationTemplateRequest;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.datatransfer.EvaluationTemplateData;
 import de.eshg.statistics.mapper.EvaluationTemplateMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
+import de.eshg.statistics.persistence.entity.evaluationtemplate.DataSource;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate_;
 import de.eshg.statistics.persistence.repository.EvaluationTemplateRepository;
@@ -28,6 +30,7 @@ import java.time.Clock;
 import java.time.Instant;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.springframework.data.domain.Page;
@@ -41,14 +44,17 @@ public class EvaluationTemplateService {
   private final EvaluationTemplateRepository evaluationTemplateRepository;
   private final StatisticUserService userService;
   private final Clock clock;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public EvaluationTemplateService(
       EvaluationTemplateRepository evaluationTemplateRepository,
       StatisticUserService userService,
-      Clock clock) {
+      Clock clock,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.evaluationTemplateRepository = evaluationTemplateRepository;
     this.userService = userService;
     this.clock = clock;
+    this.originalDataAccessConfig = originalDataAccessConfig;
   }
 
   @Transactional
@@ -63,7 +69,18 @@ public class EvaluationTemplateService {
                 addEvaluationTemplateFromEvaluationRequest.description(),
                 evaluationTemplateData,
                 availableDataSources));
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
+  }
+
+  private boolean withoutAnonymizationAllowed(EvaluationTemplate evaluationTemplate) {
+    Set<String> businessModules =
+        evaluationTemplate.getDataSources().stream()
+            .map(DataSource::getBusinessModuleName)
+            .collect(Collectors.toSet());
+    return originalDataAccessConfig
+        .getBusinessModulesOriginalDataAllowedForCurrentUser()
+        .containsAll(businessModules);
   }
 
   @Transactional
@@ -76,7 +93,8 @@ public class EvaluationTemplateService {
                 addEvaluationTemplateWithDataSourcesRequest.name(),
                 addEvaluationTemplateWithDataSourcesRequest.dataSources(),
                 availableDataSources));
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional
@@ -85,7 +103,8 @@ public class EvaluationTemplateService {
     EvaluationTemplate evaluationTemplate = getEvaluationTemplateInternal(templateId);
     evaluationTemplate.setName(updateEvaluationTemplateRequest.name());
     evaluationTemplate.setDescription(updateEvaluationTemplateRequest.description());
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional(readOnly = true)
@@ -94,7 +113,12 @@ public class EvaluationTemplateService {
         evaluationTemplateRepository.findAll(Sort.by(Sort.Direction.DESC, BaseEntity_.ID));
 
     return new GetAllEvaluationTemplatesResponse(
-        evaluationTemplates.stream().map(EvaluationTemplateMapper::mapToApi).toList());
+        evaluationTemplates.stream()
+            .map(
+                evaluationTemplate ->
+                    EvaluationTemplateMapper.mapToApi(
+                        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate)))
+            .toList());
   }
 
   @Transactional(readOnly = true)
@@ -135,7 +159,8 @@ public class EvaluationTemplateService {
   @Transactional(readOnly = true)
   public EvaluationTemplateDto getEvaluationTemplate(UUID templateId) {
     EvaluationTemplate evaluationTemplate = getEvaluationTemplateInternal(templateId);
-    return EvaluationTemplateMapper.mapToApi(evaluationTemplate);
+    return EvaluationTemplateMapper.mapToApi(
+        evaluationTemplate, withoutAnonymizationAllowed(evaluationTemplate));
   }
 
   @Transactional
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java b/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
index f4e7a11bf..ac32aeb8b 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/FilterTemplateController.java
@@ -9,6 +9,7 @@ import static de.eshg.statistics.FilterTemplateController.BASE_URL;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.statistics.aggregation.StatisticService;
 import de.eshg.statistics.api.filtertemplate.AddFilterTemplateRequest;
 import de.eshg.statistics.api.filtertemplate.FilterTemplateDto;
 import de.eshg.statistics.api.filtertemplate.GetFilterTemplatesForStatisticResponse;
@@ -32,9 +33,12 @@ public class FilterTemplateController {
   public static final String BASE_URL = BaseUrls.Statistics.FILTER_TEMPLATE_CONTROLLER;
 
   private final FilterTemplateService filterTemplateService;
+  private final StatisticService statisticService;
 
-  public FilterTemplateController(FilterTemplateService filterTemplateService) {
+  public FilterTemplateController(
+      FilterTemplateService filterTemplateService, StatisticService statisticService) {
     this.filterTemplateService = filterTemplateService;
+    this.statisticService = statisticService;
   }
 
   @PostExchange(accept = APPLICATION_JSON_VALUE)
@@ -58,6 +62,7 @@ public class FilterTemplateController {
   @Operation(summary = "Get filter templates that can be used on the statistic")
   public GetFilterTemplatesForStatisticResponse findFilterTemplatesForStatistic(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return filterTemplateService.findFilterTemplatesForStatistic(statisticId);
   }
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java b/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
index 4213f4591..01f1cd5e6 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/StatisticsApplication.java
@@ -6,6 +6,7 @@
 package de.eshg.statistics;
 
 import de.eshg.rest.service.security.config.StatisticsPublicSecurityConfig;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -14,7 +15,7 @@ import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
 @Import(StatisticsPublicSecurityConfig.class)
-@EnableConfigurationProperties({StatisticsFeatureToggle.class})
+@EnableConfigurationProperties({StatisticsFeatureToggle.class, OriginalDataAccessConfig.class})
 public class StatisticsApplication {
 
   public static final String MODULE_NAME = "Statistikmodul";
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
index ba4a8f851..f379e8274 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataAggregationService.java
@@ -24,8 +24,8 @@ import de.eshg.lib.statistics.api.SubjectType;
 import de.eshg.lib.statistics.api.ValueType;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorResponseWithLocation;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.filter.NumericComparisonDto;
 import de.eshg.statistics.mapper.AttributeSelectionMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
@@ -71,7 +71,8 @@ import org.springframework.stereotype.Service;
 public class DataAggregationService {
   private final BusinessModuleAggregationHelper businessModuleAggregationHelper;
   private final BaseStatisticsApi baseModuleStatisticsApi;
-  private final int pageSizeForBusinessModuleDataRequest;
+  private final int businessModuleDataRequestPageSize;
+  private final int tableRowPageSize;
   private final TableRowRepository tableRowRepository;
   private final CellEntryRepository cellEntryRepository;
   private final AuditLogger auditLogger;
@@ -79,21 +80,27 @@ public class DataAggregationService {
   public DataAggregationService(
       BusinessModuleAggregationHelper businessModuleAggregationHelper,
       BaseStatisticsApi baseModuleStatisticsApi,
+      @Value("${eshg.statistics.tablerows.pagesize:500}") int tableRowPageSize,
       @Value("${eshg.statistics.businessmodule.pagesize:500}")
-          int pageSizeForBusinessModuleDataRequest,
+          int businessModuleDataRequestPageSize,
       TableRowRepository tableRowRepository,
       CellEntryRepository cellEntryRepository,
       AuditLogger auditLogger) {
     this.businessModuleAggregationHelper = businessModuleAggregationHelper;
     this.baseModuleStatisticsApi = baseModuleStatisticsApi;
-    this.pageSizeForBusinessModuleDataRequest = pageSizeForBusinessModuleDataRequest;
+    this.businessModuleDataRequestPageSize = businessModuleDataRequestPageSize;
+    this.tableRowPageSize = tableRowPageSize;
     this.tableRowRepository = tableRowRepository;
     this.cellEntryRepository = cellEntryRepository;
     this.auditLogger = auditLogger;
-    if (this.pageSizeForBusinessModuleDataRequest <= 0) {
+    if (this.businessModuleDataRequestPageSize <= 0) {
       throw new IllegalArgumentException(
           "'eshg.statistics.businessmodule.pagesize' must be greater than 0");
     }
+    if (this.tableRowPageSize <= 0) {
+      throw new IllegalArgumentException(
+          "'eshg.statistics.tablerows.pagesize' must be greater than 0");
+    }
   }
 
   public Statistic createStatistic(
@@ -354,8 +361,8 @@ public class DataAggregationService {
 
   public void collectTableRows(AbstractAggregationResult aggregationResult) {
     Long tableRowsCount = countTableRows(aggregationResult);
-    int page = (int) (tableRowsCount / pageSizeForBusinessModuleDataRequest);
-    int ignoreTableRowsCount = (int) (tableRowsCount % pageSizeForBusinessModuleDataRequest);
+    int page = (int) (tableRowsCount / businessModuleDataRequestPageSize);
+    int ignoreTableRowsCount = (int) (tableRowsCount % businessModuleDataRequestPageSize);
 
     TableColumn firstTableColumn = aggregationResult.getTableColumns().getFirst();
     List<String> attributeCodes =
@@ -370,7 +377,7 @@ public class DataAggregationService {
             firstTableColumn.getDataSourceId(),
             attributeCodes,
             page,
-            pageSizeForBusinessModuleDataRequest);
+            businessModuleDataRequestPageSize);
 
     GetSpecificDataResponse dataFromBusinessModule =
         getDataFromBusinessModule(request, firstTableColumn.getBusinessModuleName());
@@ -891,8 +898,7 @@ public class DataAggregationService {
   public void removeTableRows(AbstractAggregationResult aggregationResult) {
     tableRowRepository.deleteAll(
         tableRowRepository
-            .findAllByAggregationResult(
-                aggregationResult, Pageable.ofSize(pageSizeForBusinessModuleDataRequest))
+            .findAllByAggregationResult(aggregationResult, Pageable.ofSize(tableRowPageSize))
             .getContent());
   }
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
index 76e388633..e590238f0 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceAggregationService.java
@@ -13,15 +13,13 @@ import de.eshg.base.statistics.api.BaseAvailableDataSource;
 import de.eshg.lib.aggregation.BusinessModuleAggregationHelper;
 import de.eshg.lib.aggregation.BusinessModuleClient;
 import de.eshg.lib.aggregation.ClientResponse;
-import de.eshg.lib.common.BusinessModule;
-import de.eshg.lib.keycloak.EmployeePermissionRole;
 import de.eshg.lib.statistics.api.Attribute;
 import de.eshg.lib.statistics.api.GetDataSourcesResponse;
-import de.eshg.rest.service.security.CurrentUserHelper;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
@@ -34,12 +32,15 @@ public class DataSourceAggregationService {
 
   private final BusinessModuleAggregationHelper businessModuleAggregationHelper;
   private final BaseStatisticsApi baseModuleStatisticsApi;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public DataSourceAggregationService(
       BusinessModuleAggregationHelper businessModuleAggregationHelper,
-      BaseStatisticsApi baseModuleStatisticsApi) {
+      BaseStatisticsApi baseModuleStatisticsApi,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.businessModuleAggregationHelper = businessModuleAggregationHelper;
     this.baseModuleStatisticsApi = baseModuleStatisticsApi;
+    this.originalDataAccessConfig = originalDataAccessConfig;
   }
 
   public GetAvailableDataSourcesResponse getAvailableDataSources() {
@@ -73,7 +74,7 @@ public class DataSourceAggregationService {
         availableDataSources, aggregateErrorResponses(extractedResponses));
   }
 
-  private static List<AvailableDataSource> mapToAvailableDataSources(
+  private List<AvailableDataSource> mapToAvailableDataSources(
       GetDataSourcesResponse response,
       String businessModule,
       List<BaseAvailableDataSource> baseAvailableDataSources) {
@@ -82,21 +83,13 @@ public class DataSourceAggregationService {
             dataSource ->
                 new AvailableDataSource(
                     businessModule,
-                    hasPermissionToRetrieveDataWithoutAnonymization(businessModule),
+                    originalDataAccessConfig.originalDataAllowedForCurrentUser(businessModule),
                     dataSource.id(),
                     dataSource.name(),
                     mapAndExtendAttributes(dataSource.attributes(), baseAvailableDataSources)))
         .toList();
   }
 
-  private static boolean hasPermissionToRetrieveDataWithoutAnonymization(String businessModule) {
-    // Todo ISSUE-6151: retrieve the required permission from business module
-    if (BusinessModule.SCHOOL_ENTRY.name().equals(businessModule)) {
-      return CurrentUserHelper.currentUserHasRole(EmployeePermissionRole.SCHOOL_ENTRY_ADMIN);
-    }
-    return false;
-  }
-
   private static List<BusinessDataSourceAttribute> mapAndExtendAttributes(
       List<Attribute> businessAttributes, List<BaseAvailableDataSource> baseAvailableDataSources) {
     return businessAttributes.stream()
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
index c50b66e14..1b9bc8e9f 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceController.java
@@ -8,7 +8,7 @@ package de.eshg.statistics.aggregation;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
index dfb9bd1b8..08a12c467 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/DataSourceValidator.java
@@ -7,12 +7,12 @@ package de.eshg.statistics.aggregation;
 
 import de.eshg.lib.aggregation.BusinessModuleAggregationHelper;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.DataSourceDto;
-import de.eshg.statistics.api.GetAvailableDataSourcesResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
+import de.eshg.statistics.api.datasource.GetAvailableDataSourcesResponse;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
index 274b0e161..917c0f139 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationController.java
@@ -31,11 +31,15 @@ import org.springframework.web.service.annotation.PostExchange;
 @RestController
 @Tag(name = "Evaluation")
 public class EvaluationController {
+  private final StatisticService statisticService;
   private final EvaluationService evaluationService;
   private final DiagramCreationService diagramCreationService;
 
   public EvaluationController(
-      EvaluationService evaluationService, DiagramCreationService diagramCreationService) {
+      StatisticService statisticService,
+      EvaluationService evaluationService,
+      DiagramCreationService diagramCreationService) {
+    this.statisticService = statisticService;
     this.evaluationService = evaluationService;
     this.diagramCreationService = diagramCreationService;
   }
@@ -45,6 +49,7 @@ public class EvaluationController {
   @Operation(summary = "Add an evaluation")
   public EvaluationDto addEvaluation(
       @RequestBody @Valid AddEvaluationRequest addEvaluationRequest) {
+    statisticService.checkPermissionForStatistic(addEvaluationRequest.statisticId());
     return evaluationService.addEvaluation(addEvaluationRequest);
   }
 
@@ -55,6 +60,7 @@ public class EvaluationController {
   @Operation(summary = "Get an evaluation by id")
   public EvaluationWithDiagrams getEvaluation(
       @PathVariable(name = "evaluationId") UUID evaluationId) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     return evaluationService.getEvaluation(evaluationId);
   }
 
@@ -66,6 +72,7 @@ public class EvaluationController {
   public EvaluationDto updateEvaluation(
       @PathVariable(name = "evaluationId") UUID evaluationId,
       @RequestBody @Valid UpdateEvaluationRequest updateEvaluationRequest) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     return evaluationService.updateEvaluation(evaluationId, updateEvaluationRequest);
   }
 
@@ -75,6 +82,7 @@ public class EvaluationController {
   @ApiResponse(responseCode = "200", description = "Returned when the evaluation is deleted")
   @Operation(summary = "Delete an evaluation")
   public void deleteEvaluation(@PathVariable(name = "evaluationId") UUID evaluationId) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     evaluationService.deleteEvaluation(evaluationId);
   }
 
@@ -86,6 +94,7 @@ public class EvaluationController {
   public UUID addDiagram(
       @PathVariable(name = "evaluationId") UUID evaluationId,
       @RequestBody @Valid AddDiagramRequest addDiagramRequest) {
+    evaluationService.checkPermissionForEvaluation(evaluationId);
     EvaluationDto evaluation = evaluationService.getEvaluationDto(evaluationId);
     return diagramCreationService.createDiagram(evaluation, addDiagramRequest);
   }
@@ -98,6 +107,7 @@ public class EvaluationController {
   public DiagramDto updateDiagram(
       @PathVariable(name = "diagramId") UUID diagramId,
       @RequestBody @Valid UpdateDiagramRequest updateDiagramRequest) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     return evaluationService.updateDiagram(diagramId, updateDiagramRequest);
   }
 
@@ -107,6 +117,7 @@ public class EvaluationController {
   @ApiResponse(responseCode = "200", description = "Returned when the diagram is deleted")
   @Operation(summary = "Delete a diagram")
   public void deleteDiagram(@PathVariable(name = "diagramId") UUID diagramId) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     evaluationService.deleteDiagram(diagramId);
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
index 5c19cbed5..6c0525196 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationService.java
@@ -99,6 +99,8 @@ import org.springframework.util.CollectionUtils;
 
 @Service
 public class EvaluationService {
+  private static final String EVALUATION_WITH_ID_NOT_FOUND = "Evaluation with id '%s' not found";
+  private static final String DIAGRAM_WITH_ID_NOT_FOUND = "Diagram with id '%s' not found";
   private static final String PRIMARY_ATTRIBUTE = "primaryAttribute";
   private static final String SECONDARY_ATTRIBUTE = "secondaryAttribute";
 
@@ -129,6 +131,24 @@ public class EvaluationService {
     }
   }
 
+  @Transactional(readOnly = true)
+  public void checkPermissionForEvaluation(UUID evaluationId) {
+    AbstractAggregationResult aggregationResult =
+        getEvaluationInternal(evaluationId).getAggregationResult();
+    if (statisticService.accessNotAllowed(aggregationResult)) {
+      throw new NotFoundException(EVALUATION_WITH_ID_NOT_FOUND.formatted(evaluationId));
+    }
+  }
+
+  @Transactional(readOnly = true)
+  public void checkPermissionForDiagram(UUID diagramId) {
+    AbstractAggregationResult aggregationResult =
+        getDiagramInternal(diagramId).getEvaluation().getAggregationResult();
+    if (statisticService.accessNotAllowed(aggregationResult)) {
+      throw new NotFoundException(DIAGRAM_WITH_ID_NOT_FOUND.formatted(diagramId));
+    }
+  }
+
   public static void addEvaluationAndDiagramsWithoutData(
       Statistic statistic, AnalysisTemplate analysisTemplate) {
     String analysisName = analysisTemplate.getName();
@@ -555,8 +575,7 @@ public class EvaluationService {
     return evaluationRepository
         .findByExternalId(evaluationId)
         .orElseThrow(
-            () ->
-                new NotFoundException("Evaluation with id '%s' not found".formatted(evaluationId)));
+            () -> new NotFoundException(EVALUATION_WITH_ID_NOT_FOUND.formatted(evaluationId)));
   }
 
   @Transactional
@@ -1477,7 +1496,7 @@ public class EvaluationService {
 
   @Transactional
   public DiagramDto updateDiagram(UUID diagramId, UpdateDiagramRequest updateDiagramRequest) {
-    Diagram diagram = getDiagram(diagramId);
+    Diagram diagram = getDiagramInternal(diagramId);
     validateEvaluationNotInReport(diagram.getEvaluation());
     diagram.setTitle(updateDiagramRequest.title());
     diagram.setDescription(updateDiagramRequest.description());
@@ -1485,16 +1504,15 @@ public class EvaluationService {
     return EvaluationMapper.mapToApi(diagram);
   }
 
-  public Diagram getDiagram(UUID diagramId) {
+  public Diagram getDiagramInternal(UUID diagramId) {
     return diagramRepository
         .findByExternalId(diagramId)
-        .orElseThrow(
-            () -> new NotFoundException("Diagram with id '%s' not found".formatted(diagramId)));
+        .orElseThrow(() -> new NotFoundException(DIAGRAM_WITH_ID_NOT_FOUND.formatted(diagramId)));
   }
 
   @Transactional
   public void deleteDiagram(UUID diagramId) {
-    Diagram diagram = getDiagram(diagramId);
+    Diagram diagram = getDiagramInternal(diagramId);
     validateEvaluationNotInReport(diagram.getEvaluation());
     diagramRepository.delete(diagram);
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
index 11499de40..737b885e1 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportController.java
@@ -15,7 +15,6 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.DeleteExchange;
@@ -29,14 +28,17 @@ public class ReportController {
   private final StatisticsFeatureToggle statisticsFeatureToggle;
   private final ReportService reportService;
   private final ReportExecution reportExecution;
+  private final StatisticExecutorService statisticExecutorService;
 
   public ReportController(
       StatisticsFeatureToggle statisticsFeatureToggle,
       ReportService reportService,
-      ReportExecution reportExecution) {
+      ReportExecution reportExecution,
+      StatisticExecutorService statisticExecutorService) {
     this.statisticsFeatureToggle = statisticsFeatureToggle;
     this.reportService = reportService;
     this.reportExecution = reportExecution;
+    this.statisticExecutorService = statisticExecutorService;
   }
 
   @GetExchange(value = "/{reportId}", accept = APPLICATION_JSON_VALUE)
@@ -55,6 +57,6 @@ public class ReportController {
   public void deleteReport(@PathVariable(name = "reportId") UUID reportId) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
     reportService.flagReportForDeletion(reportId);
-    CompletableFuture.runAsync(() -> reportExecution.deleteReport(reportId));
+    statisticExecutorService.submit(() -> reportExecution.deleteReport(reportId));
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
index 225b06045..db69de69a 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportExecution.java
@@ -101,9 +101,9 @@ public class ReportExecution {
         () -> reportService.setStateToFailed(reportId));
   }
 
-  public void deleteReport(UUID reportId) {
+  public boolean deleteReport(UUID reportId) {
+    AtomicBoolean deletionFinished = new AtomicBoolean(false);
     try {
-      AtomicBoolean deletionFinished = new AtomicBoolean(false);
       while (!deletionFinished.get()) {
         moduleClientAuthenticator.doWithModuleClientAuthentication(
             () -> deletionFinished.set(reportService.deleteReport(reportId)));
@@ -112,5 +112,6 @@ public class ReportExecution {
       log.error("Could not delete report {}", reportId, e);
       setToFailed(reportId);
     }
+    return deletionFinished.get();
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
index bb485f562..d576a1559 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesController.java
@@ -9,11 +9,11 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
-import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
+import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
 import de.eshg.statistics.config.StatisticsFeature;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import io.swagger.v3.oas.annotations.Operation;
@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
@@ -34,16 +33,25 @@ import org.springframework.web.service.annotation.PostExchange;
 @HttpExchange(BaseUrls.Statistics.REPORT_SERIES_URL)
 @Tag(name = "ReportSeries")
 public class ReportSeriesController {
+  private final StatisticService statisticService;
   private final ReportSeriesService reportSeriesService;
+  private final StatisticExecutorService statisticExecutorService;
   private final ReportExecution reportExecution;
+  private final ReportSeriesExecution reportSeriesExecution;
   private final StatisticsFeatureToggle statisticsFeatureToggle;
 
   public ReportSeriesController(
+      StatisticService statisticService,
       ReportSeriesService reportSeriesService,
+      StatisticExecutorService statisticExecutorService,
       ReportExecution reportExecution,
+      ReportSeriesExecution reportSeriesExecution,
       StatisticsFeatureToggle statisticsFeatureToggle) {
+    this.statisticService = statisticService;
     this.reportSeriesService = reportSeriesService;
+    this.statisticExecutorService = statisticExecutorService;
     this.reportExecution = reportExecution;
+    this.reportSeriesExecution = reportSeriesExecution;
     this.statisticsFeatureToggle = statisticsFeatureToggle;
   }
 
@@ -53,10 +61,11 @@ public class ReportSeriesController {
   public ReportSeriesDto addReportSeries(
       @RequestBody @Valid AbstractAddReportSeriesRequest addReportSeriesRequest) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    statisticService.checkPermissionForStatistic(addReportSeriesRequest.statisticId());
 
     ReportSeriesDto reportSeriesDto = reportSeriesService.addReportSeries(addReportSeriesRequest);
     if (addReportSeriesRequest instanceof AddManualReportSeriesRequest) {
-      CompletableFuture.runAsync(
+      statisticExecutorService.submit(
           () -> reportExecution.completeReport(reportSeriesDto.reportInfos().getFirst().id()));
     }
     return reportSeriesDto;
@@ -64,10 +73,10 @@ public class ReportSeriesController {
 
   @PatchExchange(value = "/{reportSeriesId}", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "The patched report series")
-  @Operation(summary = "Change title and description of a report series or change activation")
+  @Operation(summary = "Change title and description of a report series")
   public ReportSeriesDto updateReportSeries(
       @PathVariable(name = "reportSeriesId") UUID reportSeriesId,
-      @RequestBody @Valid AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
+      @RequestBody @Valid UpdateReportSeriesRequest updateReportSeriesRequest) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
     return reportSeriesService.updateReportSeries(reportSeriesId, updateReportSeriesRequest);
   }
@@ -77,7 +86,20 @@ public class ReportSeriesController {
   @Operation(summary = "Delete a report series with the reports")
   public void deleteReportSeries(@PathVariable(name = "reportSeriesId") UUID reportSeriesId) {
     statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
-    reportSeriesService.deleteReportSeries(reportSeriesId);
+    boolean isDeleted =
+        reportSeriesService.deactivateAndDeleteOrFlagReportsForDeletion(reportSeriesId);
+    if (!isDeleted) {
+      statisticExecutorService.submit(
+          () -> reportSeriesExecution.deleteReportSeries(reportSeriesId));
+    }
+  }
+
+  @PatchExchange(value = "/deactivate/{reportSeriesId}", accept = APPLICATION_JSON_VALUE)
+  @ApiResponse(responseCode = "200", description = "Returned when the report series is deactivated")
+  @Operation(summary = "Deactivate a report series")
+  public void deactivateReportSeries(@PathVariable(name = "reportSeriesId") UUID reportSeriesId) {
+    statisticsFeatureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    reportSeriesService.deactivateOrDeleteReportSeries(reportSeriesId);
   }
 
   @PostExchange(value = "/overview", accept = APPLICATION_JSON_VALUE)
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java
new file mode 100644
index 000000000..62946ea0a
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesExecution.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.aggregation;
+
+import de.eshg.statistics.exception.IncompleteDeletionException;
+import java.util.Set;
+import java.util.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ReportSeriesExecution {
+  private final ReportExecution reportExecution;
+  private final ReportSeriesService reportSeriesService;
+
+  private static final Logger log = LoggerFactory.getLogger(ReportSeriesExecution.class);
+
+  public ReportSeriesExecution(
+      ReportExecution reportExecution, ReportSeriesService reportSeriesService) {
+    this.reportExecution = reportExecution;
+    this.reportSeriesService = reportSeriesService;
+  }
+
+  public boolean deleteReportSeries(UUID reportSeriesId) {
+    try {
+      deleteReports(reportSeriesId);
+      return true;
+    } catch (Exception e) {
+      log.error("Could not delete report series {}", reportSeriesId, e);
+    }
+
+    return false;
+  }
+
+  private void deleteReports(UUID reportSeriesId) {
+    Set<UUID> reportIds = reportSeriesService.getReportIds(reportSeriesId);
+    reportIds.forEach(
+        reportId -> {
+          if (!reportExecution.deleteReport(reportId)) {
+            throw new IncompleteDeletionException();
+          }
+        });
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
index 6aa682f53..4eec9a9f5 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportSeriesService.java
@@ -13,15 +13,12 @@ import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.statistics.StatisticUserService;
 import de.eshg.statistics.api.report.AbstractAddReportSeriesRequest;
-import de.eshg.statistics.api.report.AbstractUpdateReportSeriesRequest;
-import de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.AddManualReportSeriesRequest;
-import de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest;
 import de.eshg.statistics.api.report.GetReportsRequest;
 import de.eshg.statistics.api.report.GetReportsResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
-import de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest;
+import de.eshg.statistics.api.report.UpdateReportSeriesRequest;
 import de.eshg.statistics.mapper.ReportMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
@@ -34,6 +31,7 @@ import java.time.Clock;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -47,19 +45,16 @@ import org.springframework.transaction.annotation.Transactional;
 public class ReportSeriesService {
   private final ReportSeriesRepository reportSeriesRepository;
   private final StatisticService statisticService;
-  private final ReportService reportService;
   private final StatisticUserService userService;
   private final Clock clock;
 
   public ReportSeriesService(
       ReportSeriesRepository reportSeriesRepository,
       StatisticService statisticService,
-      ReportService reportService,
       StatisticUserService userService,
       Clock clock) {
     this.reportSeriesRepository = reportSeriesRepository;
     this.statisticService = statisticService;
-    this.reportService = reportService;
     this.userService = userService;
     this.clock = clock;
   }
@@ -68,9 +63,11 @@ public class ReportSeriesService {
   public ReportSeriesDto addReportSeries(AbstractAddReportSeriesRequest addReportSeriesRequest) {
     Statistic statistic =
         statisticService.getStatisticInternal(addReportSeriesRequest.statisticId());
-    if (StatisticService.hasNoDiagrams(statistic)) {
-      throw new BadRequestException("Report creation is only possible with existing diagrams");
+    if (!statistic.isAnonymized()) {
+      throw new BadRequestException("Reports are only allowed for anonymized statistics");
     }
+    validateHasDiagrams(statistic);
+    validateIsNotDeleting(statistic);
 
     ReportSeries reportSeries =
         switch (addReportSeriesRequest) {
@@ -86,6 +83,19 @@ public class ReportSeriesService {
     return ReportMapper.mapToApi(reportSeries);
   }
 
+  private void validateHasDiagrams(Statistic statistic) {
+    if (StatisticService.hasNoDiagrams(statistic)) {
+      throw new BadRequestException("Report creation is only possible with existing diagrams");
+    }
+  }
+
+  private void validateIsNotDeleting(Statistic statistic) {
+    if (AggregationResultState.DELETING.equals(statistic.getState())) {
+      throw new BadRequestException(
+          "Statistic %s is in the process of being deleted".formatted(statistic.getExternalId()));
+    }
+  }
+
   private ReportSeries createManualReportSeries(
       Statistic statistic, AddManualReportSeriesRequest addManualReportSeriesRequest) {
     AggregationResultUtil.validateTimeRange(
@@ -122,21 +132,21 @@ public class ReportSeriesService {
     reportSeries.setPeriod(
         ReportMapper.mapToReportingPeriod(addAutoReportSeriesRequest.reportingPeriod()));
 
-    addNewPlannedReportToSeries(
-        reportSeries, "1", addAutoReportSeriesRequest.startMonth(), statistic);
+    addInitialPlannedReportToSeries(
+        reportSeries, addAutoReportSeriesRequest.startMonth(), statistic);
 
     return reportSeries;
   }
 
-  private void addNewPlannedReportToSeries(
-      ReportSeries reportSeries, String name, int startMonth, Statistic statistic) {
+  private void addInitialPlannedReportToSeries(
+      ReportSeries reportSeries, int startMonth, Statistic statistic) {
     LocalDate executionAndEndDate = calculateExecutionDate(startMonth);
     LocalDate dateStart =
         ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
 
     reportSeries.addReport(
         ReportService.createReport(
-            name,
+            "1",
             dateStart.atStartOfDay(clock.getZone()).toInstant(),
             executionAndEndDate.atStartOfDay(clock.getZone()).toInstant(),
             AggregationResultState.PLANNED,
@@ -167,68 +177,68 @@ public class ReportSeriesService {
 
   @Transactional
   public ReportSeriesDto updateReportSeries(
-      UUID reportSeriesId, AbstractUpdateReportSeriesRequest updateReportSeriesRequest) {
+      UUID reportSeriesId, UpdateReportSeriesRequest updateReportSeriesRequest) {
     ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
 
-    switch (updateReportSeriesRequest) {
-      case ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest ->
-          activateReportSeries(activateAutoReportSeriesRequest, reportSeries);
-      case DeactivateAutoReportSeriesRequest ignored -> deactivateReportSeries(reportSeries);
-      case UpdateNameAndDescriptionReportSeriesRequest
-                  updateNameAndDescriptionReportSeriesRequest ->
-          updateNameAndDescription(updateNameAndDescriptionReportSeriesRequest, reportSeries);
+    validateNotPendingManualReport(reportSeries);
+
+    reportSeries.setName(updateReportSeriesRequest.name());
+    reportSeries.setDescription(updateReportSeriesRequest.description());
+    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      reportSeries.getReports().getFirst().setName(updateReportSeriesRequest.name());
     }
 
     return ReportMapper.mapToApi(reportSeries);
   }
 
-  private void activateReportSeries(
-      ActivateAutoReportSeriesRequest activateAutoReportSeriesRequest, ReportSeries reportSeries) {
-    validateIsAutoReportSeries(reportSeries);
-    reportSeries.setActive(true);
-    reportSeries.setStartMonth(activateAutoReportSeriesRequest.startMonth());
-    reportSeries.setFrequency(
-        ReportMapper.mapToFrequency(activateAutoReportSeriesRequest.frequency()));
-    reportSeries.setPeriod(
-        ReportMapper.mapToReportingPeriod(activateAutoReportSeriesRequest.reportingPeriod()));
-
-    Report plannedReport = getPlannedReport(reportSeries);
-    if (plannedReport == null) {
-      int nextNumber = reportService.findNextNumberInReports(reportSeries.getReports());
-      addNewPlannedReportToSeries(
-          reportSeries,
-          String.valueOf(nextNumber),
-          reportSeries.getStartMonth(),
-          reportSeries.getStatistic());
-    } else {
-      LocalDate executionAndEndDate = calculateExecutionDate(reportSeries.getStartMonth());
-      LocalDate dateStart =
-          ReportService.calculateStartDate(reportSeries.getPeriod(), executionAndEndDate);
-
-      plannedReport.setTimeRangeStart(dateStart.atStartOfDay(clock.getZone()).toInstant());
-      plannedReport.setTimeRangeEnd(executionAndEndDate.atStartOfDay(clock.getZone()).toInstant());
-      plannedReport.setExecutionDate(executionAndEndDate);
+  private static void validateNotPendingManualReport(ReportSeries reportSeries) {
+    AggregationResultState reportState = reportSeries.getReports().getFirst().getState();
+    if (isManualReportSeries(reportSeries)
+        && (reportState.equals(AggregationResultState.CREATING)
+            || reportState.equals(AggregationResultState.DELETING))) {
+      throw new BadRequestException(
+          "Report series %s has a pending report".formatted(reportSeries.getExternalId()));
     }
   }
 
-  private void deactivateReportSeries(ReportSeries reportSeries) {
+  @Transactional
+  public void deactivateOrDeleteReportSeries(UUID reportSeriesId) {
+    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
+
+    deactivateOrDeleteReportSeries(reportSeries);
+  }
+
+  private boolean deactivateOrDeleteReportSeries(ReportSeries reportSeries) {
     validateIsAutoReportSeries(reportSeries);
+
     if (reportSeries.isActive()) {
-      reportSeries.setActive(false);
-      Report plannedReport = getPlannedReport(reportSeries);
-      if (plannedReport != null) {
-        reportSeries.removeReport(plannedReport);
+      if (hasOnlyPlannedReport(reportSeries)) {
+        reportSeriesRepository.delete(reportSeries);
+        return true;
+      } else {
+        reportSeries.setActive(false);
+        Report plannedReport = getPlannedReport(reportSeries);
+        if (plannedReport != null) {
+          reportSeries.removeReport(plannedReport);
+        }
       }
     }
+
+    return false;
   }
 
   private static void validateIsAutoReportSeries(ReportSeries reportSeries) {
-    if (!reportSeries.getReportType().equals(ReportType.AUTO)) {
+    if (isManualReportSeries(reportSeries)) {
       throw new BadRequestException(
           "Report series %s is not of type 'AUTO'".formatted(reportSeries.getExternalId()));
     }
   }
 
+  private static boolean hasOnlyPlannedReport(ReportSeries reportSeries) {
+    return reportSeries.getReports().stream()
+        .allMatch(report -> report.getState().equals(AggregationResultState.PLANNED));
+  }
+
   private static Report getPlannedReport(ReportSeries reportSeries) {
     return reportSeries.getReports().stream()
         .filter(report -> report.getState().equals(AggregationResultState.PLANNED))
@@ -236,38 +246,18 @@ public class ReportSeriesService {
         .orElse(null);
   }
 
-  private static void updateNameAndDescription(
-      UpdateNameAndDescriptionReportSeriesRequest updateNameAndDescriptionReportSeriesRequest,
-      ReportSeries reportSeries) {
-    validateNotPendingManualReport(reportSeries);
+  @Transactional
+  public boolean deactivateAndDeleteOrFlagReportsForDeletion(UUID reportSeriesId) {
+    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
+    validateBelongsToCurrentUserOrIsAdmin(reportSeries);
 
-    reportSeries.setName(updateNameAndDescriptionReportSeriesRequest.name());
-    reportSeries.setDescription(updateNameAndDescriptionReportSeriesRequest.description());
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
-      reportSeries
-          .getReports()
-          .getFirst()
-          .setName(updateNameAndDescriptionReportSeriesRequest.name());
+    if (!isManualReportSeries(reportSeries) && deactivateOrDeleteReportSeries(reportSeries)) {
+      return true;
     }
-  }
 
-  private static void validateNotPendingManualReport(ReportSeries reportSeries) {
-    if (reportSeries.getReportType().equals(ReportType.MANUAL)
-        && reportSeries
-            .getReports()
-            .getFirst()
-            .getState()
-            .equals(AggregationResultState.CREATING)) {
-      throw new BadRequestException(
-          "Report series %s has a pending report".formatted(reportSeries.getExternalId()));
-    }
-  }
+    reportSeries.getReports().forEach(report -> report.setState(AggregationResultState.DELETING));
 
-  @Transactional
-  public void deleteReportSeries(UUID reportSeriesId) {
-    ReportSeries reportSeries = getReportSeriesInternal(reportSeriesId);
-    validateBelongsToCurrentUserOrIsAdmin(reportSeries);
-    reportSeriesRepository.delete(reportSeries);
+    return false;
   }
 
   static void validateBelongsToCurrentUserOrIsAdmin(ReportSeries reportSeries) {
@@ -320,4 +310,15 @@ public class ReportSeriesService {
             .filter(report -> report.getState().equals(AggregationResultState.COMPLETED));
     return ReportMapper.mapToApi(reportSeries, reportStream);
   }
+
+  @Transactional(readOnly = true)
+  public Set<UUID> getReportIds(UUID reportSeriesId) {
+    return getReportSeriesInternal(reportSeriesId).getReports().stream()
+        .map(Report::getExternalId)
+        .collect(Collectors.toSet());
+  }
+
+  private static boolean isManualReportSeries(ReportSeries reportSeries) {
+    return reportSeries.getReportType().equals(ReportType.MANUAL);
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
index 2a18e9e41..5216ea2ab 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/ReportService.java
@@ -155,7 +155,6 @@ public class ReportService {
   @Transactional
   public UUID getPlannedReportToExecuteSetToPending() {
     LocalDate now = LocalDate.now(clock);
-
     Optional<Report> reportOptional =
         reportRepository.findByExecutionDateLessThanEqualAndState(
             now, AggregationResultState.PLANNED);
@@ -217,19 +216,6 @@ public class ReportService {
     return getNumberOfReport(report).orElse(report.getReportSeries().getReports().size()) + 1;
   }
 
-  int findNextNumberInReports(List<Report> reports) {
-    int currentHighest = 0;
-    for (Report report : reports) {
-      Optional<Integer> numberOfReport = getNumberOfReport(report);
-      if (numberOfReport.isEmpty()) {
-        return reports.size() + 1;
-      } else if (numberOfReport.get() > currentHighest) {
-        currentHighest = numberOfReport.get();
-      }
-    }
-    return currentHighest + 1;
-  }
-
   private Optional<Integer> getNumberOfReport(Report report) {
     try {
       return Optional.of(Integer.parseInt(report.getName()));
@@ -516,9 +502,7 @@ public class ReportService {
   @Transactional
   public void setStateToFailed(UUID reportId) {
     Report report = getReportInternal(reportId);
-    if (!report.getState().equals(AggregationResultState.FAILED)) {
-      report.setState(AggregationResultState.FAILED);
-    }
+    report.setState(AggregationResultState.FAILED);
   }
 
   @Transactional
@@ -540,18 +524,17 @@ public class ReportService {
   public boolean deleteReport(UUID reportId) {
     Report report = getReportInternal(reportId);
 
-    dataAggregationService.removeTableRows(report);
-
     if (dataAggregationService.countTableRows(report) <= 0) {
       ReportSeries reportSeries = report.getReportSeries();
-      if (reportSeries.getReportType().equals(ReportType.MANUAL)) {
+      if (reportSeries.getReports().size() <= 1) {
         reportSeriesRepository.delete(reportSeries);
       } else {
-        reportRepository.delete(report);
+        reportSeries.removeReport(report);
       }
       return true;
+    } else {
+      dataAggregationService.removeTableRows(report);
+      return false;
     }
-
-    return false;
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
index ddfc8d311..d5cf10938 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticController.java
@@ -9,7 +9,6 @@ import static de.eshg.statistics.aggregation.StatisticController.BASE_URL;
 import static de.eshg.statistics.config.StatisticsFeature.CLONE_STATISTIC;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
-import de.eshg.base.SortDirection;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.statistics.api.AbstractAddStatisticRequest;
@@ -18,8 +17,8 @@ import de.eshg.statistics.api.CloneStatisticRequest;
 import de.eshg.statistics.api.GetDetailPageInformationResponse;
 import de.eshg.statistics.api.GetStatisticRequest;
 import de.eshg.statistics.api.GetStatisticResponse;
+import de.eshg.statistics.api.GetStatisticsRequest;
 import de.eshg.statistics.api.GetStatisticsResponse;
-import de.eshg.statistics.api.StatisticSortKey;
 import de.eshg.statistics.api.UpdateStatisticTimeRangeRequest;
 import de.eshg.statistics.api.completeness.GetCompletenessDataResponse;
 import de.eshg.statistics.api.report.GetReportSeriesEntriesOfStatisticResponse;
@@ -29,13 +28,9 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
 import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.DeleteExchange;
 import org.springframework.web.service.annotation.GetExchange;
@@ -50,16 +45,19 @@ public class StatisticController {
   public static final String BASE_URL = BaseUrls.Statistics.STATISTIC_CONTROLLER;
 
   private final StatisticService statisticService;
+  private final StatisticExecutorService statisticExecutorService;
   private final StatisticExecution statisticExecution;
   private final StatisticCopyService statisticCopyService;
   private final StatisticsFeatureToggle featureToggle;
 
   public StatisticController(
       StatisticService statisticService,
+      StatisticExecutorService statisticExecutorService,
       StatisticExecution statisticExecution,
       StatisticCopyService statisticCopyService,
       StatisticsFeatureToggle featureToggle) {
     this.statisticService = statisticService;
+    this.statisticExecutorService = statisticExecutorService;
     this.statisticExecution = statisticExecution;
     this.statisticCopyService = statisticCopyService;
     this.featureToggle = featureToggle;
@@ -74,7 +72,7 @@ public class StatisticController {
       throw new BadRequestException("Only allowed without anonymization");
     }
     UUID statisticId = statisticService.addStatistic(addStatisticRequest);
-    CompletableFuture.runAsync(() -> statisticExecution.addStatistic(statisticId));
+    statisticExecutorService.submit(() -> statisticExecution.addStatistic(statisticId));
     return statisticId;
   }
 
@@ -83,9 +81,10 @@ public class StatisticController {
   public void updateStatistic(
       @PathVariable(name = "statisticId") UUID statisticId,
       @Valid @RequestBody AbstractUpdateStatisticRequest updateStatisticRequest) {
+    statisticService.checkPermissionForStatistic(statisticId);
     statisticService.updateStatistic(statisticId, updateStatisticRequest);
     if (updateStatisticRequest instanceof UpdateStatisticTimeRangeRequest) {
-      CompletableFuture.runAsync(() -> statisticExecution.updateStatistic(statisticId));
+      statisticExecutorService.submit(() -> statisticExecution.updateStatistic(statisticId));
     }
   }
 
@@ -94,26 +93,21 @@ public class StatisticController {
   @Operation(summary = "Clone a statistic")
   public UUID cloneStatistic(@Valid @RequestBody CloneStatisticRequest cloneStatisticRequest) {
     featureToggle.assertNewFeatureIsEnabled(CLONE_STATISTIC);
+    statisticService.checkPermissionForStatistic(cloneStatisticRequest.originalStatisticId());
     UUID originalId = cloneStatisticRequest.originalStatisticId();
     UUID copyId = statisticCopyService.addCopy(cloneStatisticRequest);
 
-    CompletableFuture.runAsync(() -> statisticExecution.cloneStatistic(originalId, copyId));
+    statisticExecutorService.submit(() -> statisticExecution.cloneStatistic(originalId, copyId));
 
     return copyId;
   }
 
-  @GetExchange(accept = APPLICATION_JSON_VALUE)
+  @PostExchange(value = "/overview", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "All statistics")
   @Operation(summary = "Get all statistics")
   public GetStatisticsResponse getStatistics(
-      @RequestParam(name = "sortKey", required = false, defaultValue = "CREATED_AT")
-          StatisticSortKey sortKey,
-      @RequestParam(name = "sortDirection", required = false, defaultValue = "DESC")
-          SortDirection sortDirection,
-      @Min(0) @RequestParam(name = "page", required = false, defaultValue = "0") Integer page,
-      @Min(1) @Max(200) @RequestParam(name = "pageSize", required = false, defaultValue = "25")
-          Integer pageSize) {
-    return statisticService.getStatistics(sortKey, sortDirection, page, pageSize);
+      @RequestBody @Valid GetStatisticsRequest getStatisticsRequest) {
+    return statisticService.getStatistics(getStatisticsRequest);
   }
 
   @GetExchange(value = "/{statisticId}", accept = APPLICATION_JSON_VALUE)
@@ -121,6 +115,7 @@ public class StatisticController {
   @Operation(summary = "Get the information for the detail page")
   public GetDetailPageInformationResponse getDetailPageInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getDetailPageInformation(statisticId);
   }
 
@@ -128,7 +123,9 @@ public class StatisticController {
   @ApiResponse(responseCode = "200", description = "Returned when the statistic is deleted")
   @Operation(summary = "Delete a statistic")
   public void deleteStatistic(@PathVariable(name = "statisticId") UUID statisticId) {
-    statisticService.deleteStatistic(statisticId);
+    statisticService.checkPermissionForStatistic(statisticId);
+    statisticService.prepareStatisticForDeletion(statisticId);
+    statisticExecutorService.submit(() -> statisticExecution.deleteStatistic(statisticId));
   }
 
   @PostExchange(
@@ -139,6 +136,7 @@ public class StatisticController {
   public GetStatisticResponse getStatistic(
       @PathVariable(name = "statisticId") UUID statisticId,
       @RequestBody @Valid GetStatisticRequest getStatisticRequest) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getStatistic(statisticId, getStatisticRequest);
   }
 
@@ -147,6 +145,7 @@ public class StatisticController {
   @Operation(summary = "Get information about the completeness of the statistic data")
   public GetCompletenessDataResponse getCompletenessInformation(
       @PathVariable(name = "statisticId") UUID statisticId) {
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getCompletenessInformation(statisticId);
   }
 
@@ -156,6 +155,7 @@ public class StatisticController {
   public GetReportSeriesEntriesOfStatisticResponse getReportSeriesEntriesOfStatistic(
       @PathVariable(name = "statisticId") UUID statisticId) {
     featureToggle.assertNewFeatureIsEnabled(StatisticsFeature.REPORTS);
+    statisticService.checkPermissionForStatistic(statisticId);
     return statisticService.getReportSeriesEntriesOfStatistic(statisticId);
   }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
index ef1e090bd..e788d74bb 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecution.java
@@ -8,7 +8,10 @@ package de.eshg.statistics.aggregation;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.TABLE_ROWS_REMOVAL;
 
 import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
+import de.eshg.statistics.exception.IncompleteDeletionException;
+import de.eshg.statistics.persistence.entity.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
+import java.util.Set;
 import java.util.UUID;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -23,18 +26,21 @@ public class StatisticExecution {
   private final StatisticService statisticService;
   private final StatisticCopyService statisticCopyService;
   private final ModuleClientAuthenticator moduleClientAuthenticator;
+  private final ReportSeriesExecution reportSeriesExecution;
 
   public StatisticExecution(
       DiagramCreationService diagramCreationService,
       EvaluationService evaluationService,
       StatisticService statisticService,
       StatisticCopyService statisticCopyService,
-      ModuleClientAuthenticator moduleClientAuthenticator) {
+      ModuleClientAuthenticator moduleClientAuthenticator,
+      ReportSeriesExecution reportSeriesExecution) {
     this.diagramCreationService = diagramCreationService;
     this.evaluationService = evaluationService;
     this.statisticService = statisticService;
     this.statisticCopyService = statisticCopyService;
     this.moduleClientAuthenticator = moduleClientAuthenticator;
+    this.reportSeriesExecution = reportSeriesExecution;
   }
 
   public void addStatistic(UUID statisticId) {
@@ -73,7 +79,7 @@ public class StatisticExecution {
 
   public void updateStatistic(UUID statisticId) {
     try {
-      removeStatisticData(statisticId);
+      removeStatisticData(statisticId, AggregationResultPendingState.DATA_AGGREGATION);
       updateStatisticData(statisticId);
     } catch (Exception e) {
       log.error("Could not update statistic", e);
@@ -81,12 +87,12 @@ public class StatisticExecution {
     }
   }
 
-  private void removeStatisticData(UUID statisticId) {
-    while (statisticService
-        .getAggregationResultPendingState(statisticId)
-        .equals(TABLE_ROWS_REMOVAL)) {
+  private void removeStatisticData(
+      UUID statisticId, AggregationResultPendingState pendingStateAfterRemoval) {
+    while (TABLE_ROWS_REMOVAL.equals(
+        statisticService.getAggregationResultPendingState(statisticId))) {
       moduleClientAuthenticator.doWithModuleClientAuthentication(
-          () -> statisticService.removeTableRows(statisticId));
+          () -> statisticService.removeTableRows(statisticId, pendingStateAfterRemoval));
     }
   }
 
@@ -113,4 +119,31 @@ public class StatisticExecution {
       setState(copyId, AggregationResultState.FAILED);
     }
   }
+
+  public void deleteStatistic(UUID statisticId) {
+    try {
+      deleteAllReportSeries(statisticId);
+      removeStatisticData(statisticId, null);
+      moduleClientAuthenticator.doWithModuleClientAuthentication(
+          () -> statisticService.deleteStatistic(statisticId));
+    } catch (Exception e) {
+      log.error("Could not delete statistic {}", statisticId, e);
+      setFailed(statisticId);
+    }
+  }
+
+  private void deleteAllReportSeries(UUID statisticId) {
+    Set<UUID> ids = statisticService.getReportSeriesIdsOfStatistic(statisticId);
+    ids.forEach(
+        id -> {
+          if (!reportSeriesExecution.deleteReportSeries(id)) {
+            throw new IncompleteDeletionException();
+          }
+        });
+  }
+
+  private void setFailed(UUID statisticId) {
+    moduleClientAuthenticator.doWithModuleClientAuthentication(
+        () -> statisticService.setStateToFailed(statisticId));
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java
new file mode 100644
index 000000000..8e7e6b265
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticExecutorService.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.aggregation;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+@Service
+public class StatisticExecutorService {
+  private static final Logger log = LoggerFactory.getLogger(StatisticExecutorService.class);
+  private static final String EXECUTOR_SERVICE_NAME =
+      StatisticExecutorService.class.getSimpleName();
+  private static final int TIMEOUT_MILLIS = 1000;
+
+  private ExecutorService executorService;
+  private final AtomicBoolean isStarted = new AtomicBoolean(false);
+
+  @PostConstruct
+  public void start() {
+    if (isStarted.compareAndSet(false, true)) {
+      executorService =
+          Executors.newFixedThreadPool(
+              8, new CustomizableThreadFactory(EXECUTOR_SERVICE_NAME + "-"));
+    }
+  }
+
+  @PreDestroy
+  public void stop() throws InterruptedException {
+    if (isStarted.compareAndSet(true, false)) {
+      executorService.shutdown();
+      clearQueue(executorService);
+      boolean success = executorService.awaitTermination(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+      if (success) {
+        log.info("Finished shutdown of '{}'", EXECUTOR_SERVICE_NAME);
+      } else if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
+        log.warn(
+            "Shutdown of '{}' timed out after {} ms. Active tasks: {}",
+            EXECUTOR_SERVICE_NAME,
+            TIMEOUT_MILLIS,
+            threadPoolExecutor.getActiveCount());
+      } else {
+        log.warn("Shutdown of '{}' timed out after {} ms.", EXECUTOR_SERVICE_NAME, TIMEOUT_MILLIS);
+      }
+      executorService = null;
+    }
+  }
+
+  private static void clearQueue(ExecutorService executorService) {
+    if (executorService instanceof ThreadPoolExecutor threadPoolExecutor) {
+      BlockingQueue<Runnable> queue = threadPoolExecutor.getQueue();
+      if (!queue.isEmpty()) {
+        int queueSize = queue.size();
+        log.warn(
+            "Clearing approximately {} elements from queue of '{}'",
+            queueSize,
+            EXECUTOR_SERVICE_NAME);
+        queue.clear();
+      }
+    }
+  }
+
+  public void submit(Runnable runnable) {
+    Assert.isTrue(isStarted.get(), "Executor service must be started");
+    executorService.submit(runnable);
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
index 220672e76..1d4097fa4 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/StatisticService.java
@@ -10,7 +10,6 @@ import static de.eshg.statistics.mapper.StatisticMapper.mapSortKey;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.EVALUATION_CONDUCTION;
 import static de.eshg.statistics.persistence.entity.AggregationResultPendingState.TABLE_ROWS_REMOVAL;
 
-import de.eshg.base.SortDirection;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.domain.model.BaseEntity_;
 import de.eshg.lib.keycloak.EmployeePermissionRole;
@@ -25,25 +24,26 @@ import de.eshg.statistics.api.AbstractUpdateStatisticRequest;
 import de.eshg.statistics.api.AddStatisticWithDataSourcesRequest;
 import de.eshg.statistics.api.AddStatisticWithTemplateRequest;
 import de.eshg.statistics.api.AttributeSelectionDto;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.EvaluationDto;
 import de.eshg.statistics.api.GetDetailPageInformationResponse;
 import de.eshg.statistics.api.GetStatisticRequest;
 import de.eshg.statistics.api.GetStatisticResponse;
+import de.eshg.statistics.api.GetStatisticsRequest;
 import de.eshg.statistics.api.GetStatisticsResponse;
-import de.eshg.statistics.api.StatisticSortKey;
 import de.eshg.statistics.api.UpdateStatisticNameRequest;
 import de.eshg.statistics.api.UpdateStatisticTimeRangeRequest;
 import de.eshg.statistics.api.completeness.CompletenessOfAttribute;
 import de.eshg.statistics.api.completeness.CompletenessOfBaseAttribute;
 import de.eshg.statistics.api.completeness.CompletenessOfBusinessAttribute;
 import de.eshg.statistics.api.completeness.GetCompletenessDataResponse;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest;
 import de.eshg.statistics.api.evaluationtemplate.EvaluationTemplateDto;
 import de.eshg.statistics.api.report.GetReportSeriesEntriesOfStatisticResponse;
 import de.eshg.statistics.api.report.ReportSeriesDto;
+import de.eshg.statistics.config.OriginalDataAccessConfig;
 import de.eshg.statistics.datatransfer.AnalysisTemplateData;
 import de.eshg.statistics.datatransfer.DiagramTemplateData;
 import de.eshg.statistics.datatransfer.EvaluationTemplateData;
@@ -52,6 +52,7 @@ import de.eshg.statistics.mapper.FilterParameterMapper;
 import de.eshg.statistics.mapper.ReportMapper;
 import de.eshg.statistics.mapper.StatisticMapper;
 import de.eshg.statistics.persistence.entity.AbstractAggregationResult;
+import de.eshg.statistics.persistence.entity.AbstractAggregationResult_;
 import de.eshg.statistics.persistence.entity.AggregationResultPendingState;
 import de.eshg.statistics.persistence.entity.AggregationResultState;
 import de.eshg.statistics.persistence.entity.ChartConfiguration;
@@ -59,15 +60,24 @@ import de.eshg.statistics.persistence.entity.Diagram;
 import de.eshg.statistics.persistence.entity.Evaluation;
 import de.eshg.statistics.persistence.entity.MinMaxNullUnknownValues;
 import de.eshg.statistics.persistence.entity.Statistic;
+import de.eshg.statistics.persistence.entity.Statistic_;
 import de.eshg.statistics.persistence.entity.TableColumn;
+import de.eshg.statistics.persistence.entity.TableColumn_;
 import de.eshg.statistics.persistence.entity.TableRow;
 import de.eshg.statistics.persistence.entity.evaluationtemplate.EvaluationTemplate;
+import de.eshg.statistics.persistence.entity.report.ReportSeries;
+import de.eshg.statistics.persistence.entity.report.ReportType;
 import de.eshg.statistics.persistence.repository.StatisticRepository;
 import de.eshg.statistics.persistence.repository.TableRowRepository;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import jakarta.persistence.criteria.Subquery;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -88,6 +98,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 @Service
 public class StatisticService {
+  private static final String STATISTIC_WITH_ID_NOT_FOUND = "Statistic with id '%s' not found";
+
   private static final Logger log = LoggerFactory.getLogger(StatisticService.class);
   private final StatisticRepository statisticRepository;
   private final StatisticUserService userService;
@@ -95,6 +107,7 @@ public class StatisticService {
   private final EvaluationTemplateService evaluationTemplateService;
   private final DataSourceValidator dataSourceValidator;
   private final DataAggregationService dataAggregationService;
+  private final OriginalDataAccessConfig originalDataAccessConfig;
 
   public StatisticService(
       StatisticRepository statisticRepository,
@@ -102,13 +115,40 @@ public class StatisticService {
       TableRowRepository tableRowRepository,
       EvaluationTemplateService evaluationTemplateService,
       DataSourceValidator dataSourceValidator,
-      DataAggregationService dataAggregationService) {
+      DataAggregationService dataAggregationService,
+      OriginalDataAccessConfig originalDataAccessConfig) {
     this.statisticRepository = statisticRepository;
     this.userService = userService;
     this.tableRowRepository = tableRowRepository;
     this.evaluationTemplateService = evaluationTemplateService;
     this.dataSourceValidator = dataSourceValidator;
     this.dataAggregationService = dataAggregationService;
+    this.originalDataAccessConfig = originalDataAccessConfig;
+  }
+
+  @Transactional(readOnly = true)
+  public void checkPermissionForStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+    if (accessNotAllowed(statistic)) {
+      throw new NotFoundException(STATISTIC_WITH_ID_NOT_FOUND.formatted(statisticId));
+    }
+  }
+
+  public boolean accessNotAllowed(AbstractAggregationResult aggregationResultProxy) {
+    AbstractAggregationResult aggregationResult =
+        Hibernate.unproxy(aggregationResultProxy, AbstractAggregationResult.class);
+    if (aggregationResult instanceof Statistic statistic && !statistic.isAnonymized()) {
+      Set<String> businessModules =
+          statistic.getTableColumns().stream()
+              .map(TableColumn::getBusinessModuleName)
+              .collect(Collectors.toSet());
+      return businessModules.stream()
+          .anyMatch(
+              businessModule ->
+                  !originalDataAccessConfig.originalDataAllowedForCurrentUser(businessModule));
+    } else {
+      return false;
+    }
   }
 
   @Transactional
@@ -170,6 +210,12 @@ public class StatisticService {
       Instant timeRangeEnd,
       boolean anonymized,
       UUID templateId) {
+    if (!anonymized
+        && !originalDataAccessConfig.originalDataAllowedForCurrentUser(
+            dataSource.businessModuleName())) {
+      throw new BadRequestException(
+          "Only anonymized statistics allowed for data source '%s'".formatted(dataSource.id()));
+    }
     EvaluationTemplate evaluationTemplate = null;
     if (templateId != null) {
       evaluationTemplate = evaluationTemplateService.getEvaluationTemplateInternal(templateId);
@@ -240,19 +286,71 @@ public class StatisticService {
   }
 
   @Transactional(readOnly = true)
-  public GetStatisticsResponse getStatistics(
-      StatisticSortKey sortKey, SortDirection sortDirection, Integer page, Integer pageSize) {
+  public GetStatisticsResponse getStatistics(GetStatisticsRequest getStatisticsRequest) {
+    Specification<Statistic> specification;
+    if (getStatisticsRequest.anonymizationValue() == null) {
+      specification =
+          Specification.anyOf(
+              anonymizedStatistics(),
+              notAnonymizedStatistics(
+                  originalDataAccessConfig.getBusinessModulesOriginalDataAllowedForCurrentUser()));
+    } else {
+      if (Boolean.TRUE.equals(getStatisticsRequest.anonymizationValue())) {
+        specification = anonymizedStatistics();
+      } else {
+        specification =
+            notAnonymizedStatistics(
+                originalDataAccessConfig.getBusinessModulesOriginalDataAllowedForCurrentUser());
+      }
+    }
+
     Page<Statistic> statisticPage =
         statisticRepository.findAll(
+            specification,
             PageRequest.of(
-                page,
-                pageSize,
-                Sort.by(mapSortDirection(sortDirection), mapSortKey(sortKey), BaseEntity_.ID)));
+                getStatisticsRequest.page(),
+                getStatisticsRequest.pageSize(),
+                Sort.by(
+                    mapSortDirection(getStatisticsRequest.sortDirection()),
+                    mapSortKey(getStatisticsRequest.sortKey()),
+                    BaseEntity_.ID)));
 
     Map<UUID, UserDto> resolvedUsers = getResolvedUsers(statisticPage.get());
     return StatisticMapper.mapStatisticPageToResponse(statisticPage, resolvedUsers);
   }
 
+  private Specification<Statistic> anonymizedStatistics() {
+    return (root, query, criteriaBuilder) ->
+        criteriaBuilder.isTrue(root.get(Statistic_.ANONYMIZED));
+  }
+
+  private Specification<Statistic> notAnonymizedStatistics(Set<String> allowedBusinessModuleNames) {
+    return (root, query, criteriaBuilder) -> {
+      Predicate notAnonymizedPredicate = criteriaBuilder.isFalse(root.get(Statistic_.ANONYMIZED));
+
+      Subquery<TableColumn> subquery = query.subquery(TableColumn.class);
+      Root<TableColumn> tableColumnRoot = subquery.from(TableColumn.class);
+      subquery.select(tableColumnRoot);
+
+      Expression<Collection<TableColumn>> tableColumnsExpression =
+          root.get(AbstractAggregationResult_.TABLE_COLUMNS);
+      Predicate tableColumnMemberPredicate =
+          criteriaBuilder.isMember(tableColumnRoot, tableColumnsExpression);
+
+      Predicate businessModuleNotAllowedPredicate =
+          criteriaBuilder.not(
+              tableColumnRoot
+                  .get(TableColumn_.BUSINESS_MODULE_NAME)
+                  .in(allowedBusinessModuleNames));
+
+      subquery.where(
+          criteriaBuilder.and(tableColumnMemberPredicate, businessModuleNotAllowedPredicate));
+
+      return criteriaBuilder.and(
+          notAnonymizedPredicate, criteriaBuilder.not(criteriaBuilder.exists(subquery)));
+    };
+  }
+
   private Map<UUID, UserDto> getResolvedUsers(
       Stream<? extends AbstractAggregationResult> statisticStream) {
     Set<UUID> userIds =
@@ -302,7 +400,7 @@ public class StatisticService {
     return statisticRepository
         .findByExternalId(statisticId)
         .orElseThrow(
-            () -> new NotFoundException("Statistic with id '%s' not found".formatted(statisticId)));
+            () -> new NotFoundException(STATISTIC_WITH_ID_NOT_FOUND.formatted(statisticId)));
   }
 
   public static void validateStatisticCompleted(Statistic statistic) {
@@ -341,10 +439,49 @@ public class StatisticService {
   }
 
   @Transactional
-  public void deleteStatistic(UUID statisticId) {
+  public void prepareStatisticForDeletion(UUID statisticId) {
     Statistic statistic = getStatisticInternal(statisticId);
     validateBelongsToCurrentUserOrIsAdmin(statistic);
     validateCopyProcessIsNotOngoing(statistic);
+
+    statistic.setState(AggregationResultState.DELETING);
+    statistic.setPendingState(TABLE_ROWS_REMOVAL);
+
+    deactivateAndDeleteEmptyAutoReportSeries(statistic);
+    flagAllReportsForDeletion(statistic);
+  }
+
+  private void deactivateAndDeleteEmptyAutoReportSeries(Statistic statistic) {
+    List<ReportSeries> reportSeriesEntriesToDelete = new ArrayList<>();
+    statistic.getReportSeriesList().stream()
+        .filter(reportSeries -> reportSeries.getReportType().equals(ReportType.AUTO))
+        .forEach(
+            reportSeries -> {
+              reportSeries.setActive(false);
+              reportSeries.getReports().stream()
+                  .filter(report -> report.getState().equals(AggregationResultState.PLANNED))
+                  .findFirst()
+                  .ifPresent(reportSeries::removeReport);
+              if (reportSeries.getReports().isEmpty()) {
+                reportSeriesEntriesToDelete.add(reportSeries);
+              }
+            });
+    statistic.removeReportSeriesEntries(reportSeriesEntriesToDelete);
+  }
+
+  private void flagAllReportsForDeletion(Statistic statistic) {
+    statistic
+        .getReportSeriesList()
+        .forEach(
+            reportSeries ->
+                reportSeries
+                    .getReports()
+                    .forEach(report -> report.setState(AggregationResultState.DELETING)));
+  }
+
+  @Transactional
+  public void deleteStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
     statisticRepository.delete(statistic);
   }
 
@@ -454,6 +591,15 @@ public class StatisticService {
         resolvedUsers);
   }
 
+  @Transactional(readOnly = true)
+  public Set<UUID> getReportSeriesIdsOfStatistic(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+
+    return statistic.getReportSeriesList().stream()
+        .map(ReportSeries::getExternalId)
+        .collect(Collectors.toSet());
+  }
+
   static boolean hasNoDiagrams(Statistic statistic) {
     return statistic.getEvaluations().isEmpty()
         || statistic.getEvaluations().stream()
@@ -491,13 +637,14 @@ public class StatisticService {
   }
 
   @Transactional
-  public void removeTableRows(UUID statisticId) {
+  public void removeTableRows(
+      UUID statisticId, AggregationResultPendingState pendingStateAfterRemoval) {
     Statistic statistic = getStatisticInternal(statisticId);
 
-    dataAggregationService.removeTableRows(statistic);
-
     if (dataAggregationService.countTableRows(statistic) <= 0) {
-      statistic.setPendingState(AggregationResultPendingState.DATA_AGGREGATION);
+      statistic.setPendingState(pendingStateAfterRemoval);
+    } else {
+      dataAggregationService.removeTableRows(statistic);
     }
   }
 
@@ -573,4 +720,10 @@ public class StatisticService {
                     FilterParameterMapper.mapToApi(diagram.getFilters())))
         .toList();
   }
+
+  @Transactional
+  public void setStateToFailed(UUID statisticId) {
+    Statistic statistic = getStatisticInternal(statisticId);
+    statistic.setState(AggregationResultState.FAILED);
+  }
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
index ee185051b..588ea8472 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/AddStatisticWithDataSourcesRequest.java
@@ -7,6 +7,7 @@ package de.eshg.statistics.api;
 
 import static de.eshg.statistics.api.AddStatisticWithDataSourcesRequest.SCHEMA_NAME;
 
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java
new file mode 100644
index 000000000..c8af84e23
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/GetStatisticsRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api;
+
+import de.eshg.base.SortDirection;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import java.util.Optional;
+
+public record GetStatisticsRequest(
+    @Schema(defaultValue = "CREATED_AT") StatisticSortKey sortKey,
+    @Schema(defaultValue = "DESC") SortDirection sortDirection,
+    @Min(0) @Schema(defaultValue = "0") Integer page,
+    @Min(1) @Max(200) @Schema(defaultValue = "25") Integer pageSize,
+    Boolean anonymizationValue) {
+
+  public GetStatisticsRequest(
+      StatisticSortKey sortKey,
+      SortDirection sortDirection,
+      Integer page,
+      Integer pageSize,
+      Boolean anonymizationValue) {
+    this.sortKey = Optional.ofNullable(sortKey).orElse(StatisticSortKey.CREATED_AT);
+    this.sortDirection = Optional.ofNullable(sortDirection).orElse(SortDirection.DESC);
+    this.page = Optional.ofNullable(page).orElse(0);
+    this.pageSize = Optional.ofNullable(pageSize).orElse(25);
+    this.anonymizationValue = anonymizationValue;
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
index d4b9edd64..5b9cea926 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticInfo.java
@@ -7,13 +7,16 @@ package de.eshg.statistics.api;
 
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
 import java.time.Instant;
+import java.util.List;
 import java.util.UUID;
 
 public record StatisticInfo(
     @NotNull UUID id,
     @NotNull UUID userId,
     @NotBlank String name,
+    @NotNull @Size(min = 1) List<String> dataSourceNames,
     @NotNull StatisticStateDto state,
     @NotNull Instant timeRangeStart,
     @NotNull Instant timeRangeEnd,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
index 7844a5364..f21009c20 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/StatisticStateDto.java
@@ -13,5 +13,6 @@ public enum StatisticStateDto {
   FAILED,
   CREATING,
   UPDATING,
-  COPY_ONGOING
+  COPY_ONGOING,
+  DELETING
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
similarity index 91%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
index ca73dbc64..9b2601a22 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/AvailableDataSource.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/AvailableDataSource.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
similarity index 83%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
index ba6daee6b..f87713a9e 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BaseDataSourceAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.constraints.NotBlank;
 
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
similarity index 67%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
index 6d8c84fe2..4b3cfd7c0 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataAttribute.java
@@ -3,14 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
-import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotBlank;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
-public record BusinessDataAttribute(@NotNull String code, List<String> baseAttributeCodes) {
+public record BusinessDataAttribute(@NotBlank String code, List<String> baseAttributeCodes) {
   public BusinessDataAttribute(String code, List<String> baseAttributeCodes) {
     this.code = code;
     this.baseAttributeCodes =
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
similarity index 89%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
index e696b69a9..59b5d4045 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/BusinessDataSourceAttribute.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
similarity index 92%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
index 758669dac..b6949fbd3 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/DataSourceDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/DataSourceDto.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
similarity index 90%
rename from backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java
rename to backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
index c5f84fb2e..87c621abf 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/GetAvailableDataSourcesResponse.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/GetAvailableDataSourcesResponse.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.statistics.api;
+package de.eshg.statistics.api.datasource;
 
 import de.eshg.rest.service.error.ErrorResponseWithLocation;
 import jakarta.validation.Valid;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
index 71fa11c97..44170a495 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/AddEvaluationTemplateWithDataSourcesRequest.java
@@ -7,7 +7,7 @@ package de.eshg.statistics.api.evaluationtemplate;
 
 import static de.eshg.statistics.api.evaluationtemplate.AddEvaluationTemplateWithDataSourcesRequest.SCHEMA_NAME;
 
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
index ac098b525..df48e6eba 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BaseDataAttributeWithName.java
@@ -5,6 +5,6 @@
 
 package de.eshg.statistics.api.evaluationtemplate;
 
-import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotBlank;
 
-public record BaseDataAttributeWithName(@NotNull String code, @NotNull String name) {}
+public record BaseDataAttributeWithName(@NotBlank String code, @NotBlank String name) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
index 2b4d2f623..5a6c71e5a 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/BusinessDataAttributeWithName.java
@@ -6,10 +6,11 @@
 package de.eshg.statistics.api.evaluationtemplate;
 
 import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import java.util.List;
 
 public record BusinessDataAttributeWithName(
-    @NotNull String code,
-    @NotNull String name,
+    @NotBlank String code,
+    @NotBlank String name,
     @NotNull @Valid List<BaseDataAttributeWithName> baseDataAttributes) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
index 98386f4f1..a8a0dbadb 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluationtemplate/EvaluationTemplateDto.java
@@ -18,6 +18,7 @@ public record EvaluationTemplateDto(
     @NotNull UUID id,
     @NotBlank String name,
     String description,
+    @NotNull boolean withoutAnonymizationAllowed,
     @NotNull @Valid List<DataSourceWithAttributeNames> dataSources,
     @NotNull @Valid List<AnalysisInfo> analysisInfos,
     @NotNull UUID userId,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
deleted file mode 100644
index e912018ff..000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/AbstractUpdateReportSeriesRequest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import io.swagger.v3.oas.annotations.Hidden;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = "AbstractUpdateReportSeriesRequest")
-@JsonTypeInfo(
-    use = JsonTypeInfo.Id.NAME,
-    property = "@type",
-    include = JsonTypeInfo.As.EXISTING_PROPERTY)
-@JsonSubTypes({
-  @JsonSubTypes.Type(
-      value = ActivateAutoReportSeriesRequest.class,
-      name = ActivateAutoReportSeriesRequest.SCHEMA_NAME),
-  @JsonSubTypes.Type(
-      value = DeactivateAutoReportSeriesRequest.class,
-      name = DeactivateAutoReportSeriesRequest.SCHEMA_NAME),
-  @JsonSubTypes.Type(
-      value = UpdateNameAndDescriptionReportSeriesRequest.class,
-      name = UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME),
-})
-public sealed interface AbstractUpdateReportSeriesRequest
-    permits ActivateAutoReportSeriesRequest,
-        DeactivateAutoReportSeriesRequest,
-        UpdateNameAndDescriptionReportSeriesRequest {
-  @Hidden
-  @NotNull
-  @JsonProperty("@type")
-  String type();
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
deleted file mode 100644
index 8c48b8197..000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/ActivateAutoReportSeriesRequest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.ActivateAutoReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.NotNull;
-
-@Schema(name = SCHEMA_NAME)
-public record ActivateAutoReportSeriesRequest(
-    @Min(1) @Max(12) @NotNull Integer startMonth,
-    @NotNull FrequencyDto frequency,
-    @NotNull ReportingPeriodDto reportingPeriod)
-    implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "ActivateAutoReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
deleted file mode 100644
index 57e0e7ec9..000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/DeactivateAutoReportSeriesRequest.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.DeactivateAutoReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-
-@Schema(name = SCHEMA_NAME)
-public record DeactivateAutoReportSeriesRequest() implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "DeactivateAutoReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
deleted file mode 100644
index 673396837..000000000
--- a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateNameAndDescriptionReportSeriesRequest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.statistics.api.report;
-
-import static de.eshg.statistics.api.report.UpdateNameAndDescriptionReportSeriesRequest.SCHEMA_NAME;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-
-@Schema(name = SCHEMA_NAME)
-public record UpdateNameAndDescriptionReportSeriesRequest(@NotBlank String name, String description)
-    implements AbstractUpdateReportSeriesRequest {
-  public static final String SCHEMA_NAME = "UpdateNameAndDescriptionReportSeriesRequest";
-
-  @Override
-  public String type() {
-    return SCHEMA_NAME;
-  }
-}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
new file mode 100644
index 000000000..f40b7ee98
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/api/report/UpdateReportSeriesRequest.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.api.report;
+
+import jakarta.validation.constraints.NotBlank;
+
+public record UpdateReportSeriesRequest(@NotBlank String name, String description) {}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java b/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java
new file mode 100644
index 000000000..98d0686b3
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/config/OriginalDataAccessConfig.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.config;
+
+import de.eshg.lib.keycloak.EmployeePermissionRole;
+import de.eshg.rest.service.security.CurrentUserHelper;
+import de.eshg.statistics.aggregation.ReportExecution;
+import jakarta.validation.constraints.NotBlank;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties(prefix = "eshg.statistics.businessmodule")
+public class OriginalDataAccessConfig {
+  private static final Logger log = LoggerFactory.getLogger(ReportExecution.class);
+
+  private List<OriginalDataPermission> originalDataPermissions = Collections.emptyList();
+
+  public Set<String> getBusinessModulesOriginalDataAllowedForCurrentUser() {
+    Set<String> allBusinessModules =
+        originalDataPermissions.stream()
+            .map(OriginalDataPermission::getBusinessModule)
+            .collect(Collectors.toSet());
+
+    return allBusinessModules.stream()
+        .filter(this::originalDataAllowedForCurrentUser)
+        .collect(Collectors.toSet());
+  }
+
+  public boolean originalDataAllowedForCurrentUser(String businessModuleName) {
+    List<EmployeePermissionRole> permissionRoles =
+        getPermissionRolesForOriginalData(businessModuleName);
+    return !permissionRoles.isEmpty()
+        && permissionRoles.stream().allMatch(CurrentUserHelper::currentUserHasRole);
+  }
+
+  private List<EmployeePermissionRole> getPermissionRolesForOriginalData(
+      String businessModuleName) {
+    return originalDataPermissions.stream()
+        .filter(
+            originalDataEntry -> businessModuleName.equals(originalDataEntry.getBusinessModule()))
+        .map(OriginalDataAccessConfig::mapPermissionRole)
+        .toList();
+  }
+
+  private static EmployeePermissionRole mapPermissionRole(
+      OriginalDataPermission originalDataPermission) {
+    try {
+      return EmployeePermissionRole.valueOf(originalDataPermission.getPermission());
+    } catch (IllegalArgumentException ignored) {
+      log.error("Invalid permission role configured: {}", originalDataPermission.getPermission());
+      return null;
+    }
+  }
+
+  public void setOriginalDataPermissions(List<OriginalDataPermission> originalDataPermissions) {
+    this.originalDataPermissions = originalDataPermissions;
+  }
+
+  public static class OriginalDataPermission {
+    @NotBlank private String businessModule;
+    @NotBlank private String permission;
+
+    public String getBusinessModule() {
+      return businessModule;
+    }
+
+    public void setBusinessModule(String businessModule) {
+      this.businessModule = businessModule;
+    }
+
+    public String getPermission() {
+      return permission;
+    }
+
+    public void setPermission(String permission) {
+      this.permission = permission;
+    }
+  }
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
index ee2a37253..823213da1 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/datatransfer/EvaluationTemplateData.java
@@ -5,7 +5,7 @@
 
 package de.eshg.statistics.datatransfer;
 
-import de.eshg.statistics.api.DataSourceDto;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import java.util.List;
 
 public record EvaluationTemplateData(
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java b/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java
new file mode 100644
index 000000000..e29231ad2
--- /dev/null
+++ b/backend/statistics/src/main/java/de/eshg/statistics/exception/IncompleteDeletionException.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.statistics.exception;
+
+import java.io.Serial;
+
+public class IncompleteDeletionException extends RuntimeException {
+  @Serial private static final long serialVersionUID = 1L;
+}
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
index e62b7f449..237a858b1 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportController.java
@@ -9,6 +9,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 import de.eshg.file.common.CustomMediaTypes;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.statistics.aggregation.EvaluationService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -28,15 +29,19 @@ import org.springframework.web.service.annotation.HttpExchange;
 public class DataExportController {
   static final String DIAGRAM_EXPORT_FILENAME = "diagramm-daten-export.xlsx";
   private final DataExportService dataExportService;
+  private final EvaluationService evaluationService;
 
-  public DataExportController(DataExportService dataExportService) {
+  public DataExportController(
+      DataExportService dataExportService, EvaluationService evaluationService) {
     this.dataExportService = dataExportService;
+    this.evaluationService = evaluationService;
   }
 
   @GetExchange(value = "/diagram/{diagramId}", accept = APPLICATION_JSON_VALUE)
   @ApiResponse(responseCode = "200", description = "Exported diagram data")
   @Operation(summary = "Export diagram data")
   public ResponseEntity<Resource> exportData(@PathVariable(name = "diagramId") UUID diagramId) {
+    evaluationService.checkPermissionForDiagram(diagramId);
     return ResponseEntity.ok()
         .header(
             HttpHeaders.CONTENT_DISPOSITION,
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
index 2d651c4b1..3f952824e 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/export/DataExportService.java
@@ -8,6 +8,7 @@ package de.eshg.statistics.export;
 import static de.eshg.statistics.StatisticsApplication.MODULE_NAME;
 
 import de.eshg.lib.auditlog.AuditLogger;
+import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.statistics.aggregation.AggregationResultUtil;
 import de.eshg.statistics.aggregation.EvaluationService;
@@ -16,6 +17,7 @@ import de.eshg.statistics.persistence.entity.AbstractAggregationResult;
 import de.eshg.statistics.persistence.entity.AttributeSelection;
 import de.eshg.statistics.persistence.entity.ChartConfiguration;
 import de.eshg.statistics.persistence.entity.Diagram;
+import de.eshg.statistics.persistence.entity.Statistic;
 import de.eshg.statistics.persistence.entity.TableColumn;
 import de.eshg.statistics.persistence.entity.chart.BarChartConfiguration;
 import de.eshg.statistics.persistence.entity.chart.ChoroplethMapConfiguration;
@@ -71,7 +73,13 @@ public class DataExportService {
 
   @Transactional(readOnly = true)
   public Resource exportData(UUID diagramId) {
-    Diagram diagram = evaluationService.getDiagram(diagramId);
+    Diagram diagram = evaluationService.getDiagramInternal(diagramId);
+    AbstractAggregationResult aggregationResult =
+        Hibernate.unproxy(
+            diagram.getEvaluation().getAggregationResult(), AbstractAggregationResult.class);
+    if (aggregationResult instanceof Statistic statistic && !statistic.isAnonymized()) {
+      throw new BadRequestException("Data exports are only allowed for anonymized statistics");
+    }
 
     try (XSSFWorkbook workbook = new XSSFWorkbook();
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
index b6dda39d7..28303d4f8 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationTemplateMapper.java
@@ -6,17 +6,17 @@
 package de.eshg.statistics.mapper;
 
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.statistics.api.AvailableDataSource;
-import de.eshg.statistics.api.BaseDataSourceAttribute;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.BusinessDataSourceAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.chart.BarChartConfigurationDto;
 import de.eshg.statistics.api.chart.ChoroplethMapConfigurationDto;
 import de.eshg.statistics.api.chart.HistogramChartConfigurationDto;
 import de.eshg.statistics.api.chart.LineChartConfigurationDto;
 import de.eshg.statistics.api.chart.PieChartConfigurationDto;
 import de.eshg.statistics.api.chart.ScatterChartConfigurationDto;
+import de.eshg.statistics.api.datasource.AvailableDataSource;
+import de.eshg.statistics.api.datasource.BaseDataSourceAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataSourceAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.AnalysisInfo;
 import de.eshg.statistics.api.evaluationtemplate.BaseDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.BusinessDataAttributeWithName;
@@ -253,7 +253,8 @@ public class EvaluationTemplateMapper {
         evaluationTemplate.getLastUsageAt());
   }
 
-  public static EvaluationTemplateDto mapToApi(EvaluationTemplate evaluationTemplate) {
+  public static EvaluationTemplateDto mapToApi(
+      EvaluationTemplate evaluationTemplate, boolean withoutAnonymizationAllowed) {
     List<DataSourceWithAttributeNames> dataSources =
         mapToDataSourceDtos(evaluationTemplate.getDataSources());
 
@@ -261,6 +262,7 @@ public class EvaluationTemplateMapper {
         evaluationTemplate.getExternalId(),
         evaluationTemplate.getName(),
         evaluationTemplate.getDescription(),
+        withoutAnonymizationAllowed,
         dataSources,
         mapToAnalysisInfos(evaluationTemplate.getAnalysisTemplates()),
         evaluationTemplate.getCreatedByUserId(),
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
index 112346da9..61e983c96 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/StatisticMapper.java
@@ -10,8 +10,6 @@ import de.eshg.base.user.api.UserDto;
 import de.eshg.lib.statistics.api.DataRow;
 import de.eshg.lib.statistics.api.ValueOptionInternal;
 import de.eshg.lib.statistics.api.ValueType;
-import de.eshg.statistics.api.BusinessDataAttribute;
-import de.eshg.statistics.api.DataSourceDto;
 import de.eshg.statistics.api.GetStatisticResponse;
 import de.eshg.statistics.api.GetStatisticsResponse;
 import de.eshg.statistics.api.StatisticInfo;
@@ -28,6 +26,8 @@ import de.eshg.statistics.api.attributes.ProcedureIdAttribute;
 import de.eshg.statistics.api.attributes.TextAttribute;
 import de.eshg.statistics.api.attributes.ValueOption;
 import de.eshg.statistics.api.attributes.ValueWithOptionsAttribute;
+import de.eshg.statistics.api.datasource.BusinessDataAttribute;
+import de.eshg.statistics.api.datasource.DataSourceDto;
 import de.eshg.statistics.api.evaluationtemplate.BaseDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.BusinessDataAttributeWithName;
 import de.eshg.statistics.api.evaluationtemplate.DataSourceWithAttributeNames;
@@ -230,6 +230,11 @@ public class StatisticMapper {
         statistic.getExternalId(),
         statistic.getCreatedByUserId(),
         statistic.getName(),
+        statistic.getTableColumns().stream()
+            .map(TableColumn::getDataSourceName)
+            .distinct()
+            .sorted()
+            .toList(),
         mapStatisticState(statistic.getState()),
         statistic.getTimeRangeStart(),
         statistic.getTimeRangeEnd(),
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
index f93ad83c0..28cbceee8 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/Statistic.java
@@ -49,6 +49,11 @@ public class Statistic extends AbstractAggregationResult {
     reportSeriesList.add(reportSeries);
   }
 
+  public void removeReportSeriesEntries(List<ReportSeries> reportSeriesList) {
+    reportSeriesList.forEach(reportSeries -> reportSeries.setStatistic(null));
+    this.reportSeriesList.removeAll(reportSeriesList);
+  }
+
   public List<ReportSeries> getReportSeriesList() {
     return reportSeriesList;
   }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
index ef8ed9ff0..e87d7bee5 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/repository/StatisticRepository.java
@@ -9,8 +9,10 @@ import de.eshg.statistics.persistence.entity.Statistic;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
-public interface StatisticRepository extends JpaRepository<Statistic, Long> {
+public interface StatisticRepository
+    extends JpaRepository<Statistic, Long>, JpaSpecificationExecutor<Statistic> {
 
   Optional<Statistic> findByExternalId(UUID externalId);
 }
diff --git a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
index 2705ea6b3..a9047e14f 100644
--- a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
+++ b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsTestHelperController.java
@@ -8,6 +8,7 @@ package de.eshg.statistics.testhelper;
 import de.eshg.auditlog.SharedAuditLogTestHelperApi;
 import de.eshg.lib.auditlog.AuditLogTestHelperService;
 import de.eshg.statistics.aggregation.ReportExecution;
+import de.eshg.statistics.aggregation.StatisticExecutorService;
 import de.eshg.statistics.config.StatisticsFeature;
 import de.eshg.statistics.config.StatisticsFeatureToggle;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
@@ -15,7 +16,6 @@ import de.eshg.testhelper.DefaultTestHelperService;
 import de.eshg.testhelper.TestHelperController;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import java.io.IOException;
-import java.util.concurrent.CompletableFuture;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.PostExchange;
@@ -27,6 +27,7 @@ public class StatisticsTestHelperController extends TestHelperController
 
   private final StatisticsFeatureToggle statisticsFeatureToggle;
   private final AuditLogTestHelperService auditLogTestHelperService;
+  private final StatisticExecutorService statisticExecutorService;
   private final ReportExecution reportExecution;
 
   public StatisticsTestHelperController(
@@ -34,11 +35,13 @@ public class StatisticsTestHelperController extends TestHelperController
       DefaultTestHelperService testHelperService,
       AuditLogTestHelperService auditLogTestHelperService,
       ReportExecution reportExecution,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      StatisticExecutorService statisticExecutorService) {
     super(testHelperService, environmentConfig);
     this.statisticsFeatureToggle = statisticsFeatureToggle;
     this.auditLogTestHelperService = auditLogTestHelperService;
     this.reportExecution = reportExecution;
+    this.statisticExecutorService = statisticExecutorService;
   }
 
   @PostExchange("/enabled-new-features/{featureToEnable}")
@@ -48,7 +51,7 @@ public class StatisticsTestHelperController extends TestHelperController
 
   @PostExchange("/finish-auto-reports")
   public void finishAutoReports() {
-    CompletableFuture.runAsync(reportExecution::handlePlannedReports);
+    statisticExecutorService.submit(reportExecution::handlePlannedReports);
   }
 
   @Override
diff --git a/backend/statistics/src/main/resources/application.properties b/backend/statistics/src/main/resources/application.properties
index c0e60cd36..c2ef1378b 100644
--- a/backend/statistics/src/main/resources/application.properties
+++ b/backend/statistics/src/main/resources/application.properties
@@ -17,3 +17,6 @@ logging.level.org.zalando.logbook=TRACE
 spring.security.oauth2.client.registration.module-client.client-id=system-statistics
 spring.security.oauth2.client.registration.module-client.client-secret=password
 spring.security.oauth2.client.provider.eshg-keycloak.token-uri=${eshg.keycloak.internal.url}/realms/eshg/protocol/openid-connect/token
+
+eshg.statistics.businessmodule.original-data-permissions[0].business-module=SCHOOL_ENTRY
+eshg.statistics.businessmodule.original-data-permissions[0].permission=SCHOOL_ENTRY_ADMIN
diff --git a/backend/sti-protection/build.gradle b/backend/sti-protection/build.gradle
index b58e0d6c3..66a82fa7e 100644
--- a/backend/sti-protection/build.gradle
+++ b/backend/sti-protection/build.gradle
@@ -9,6 +9,8 @@ dependencies {
     implementation project(':lib-calendar')
     implementation project(':business-module-persistence-commons')
     implementation project(":lib-document-generator")
+    implementation project(':lib-scheduling')
+    implementation project(':rest-oauth-client-commons')
     implementation 'org.springdoc:springdoc-openapi-starter-common:latest.release'
 
     annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen'
diff --git a/backend/sti-protection/gradle.lockfile b/backend/sti-protection/gradle.lockfile
index 0fc7e303b..5bf0eab47 100644
--- a/backend/sti-protection/gradle.lockfile
+++ b/backend/sti-protection/gradle.lockfile
@@ -29,7 +29,7 @@ com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,te
 com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.googlecode.java-diff-utils:diffutils:1.3.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-com.googlecode.libphonenumber:libphonenumber:8.13.46=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+com.googlecode.libphonenumber:libphonenumber:8.13.48=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 com.nimbusds:lang-tag:1.7=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -88,9 +88,12 @@ javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCo
 junit:junit:4.13.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy-agent:1.14.19=testCompileClasspath,testRuntimeClasspath
 net.bytebuddy:byte-buddy:1.14.19=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-net.datafaker:datafaker:2.4.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.datafaker:datafaker:2.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.jna:jna:5.15.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.java.dev.stax-utils:stax-utils:20070216=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-core:5.16.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.16.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+net.javacrumbs.shedlock:shedlock-spring:5.16.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.logstash.logback:logstash-logback-encoder:8.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
 net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
 net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
diff --git a/backend/sti-protection/openApi.yaml b/backend/sti-protection/openApi.yaml
index 84b267831..30e15a1d0 100644
--- a/backend/sti-protection/openApi.yaml
+++ b/backend/sti-protection/openApi.yaml
@@ -546,6 +546,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -1351,6 +1366,44 @@ paths:
           description: OK
       tags:
       - StiProtectionProcedure
+  /sti-procedures/waiting-room-procedures:
+    get:
+      operationId: getWaitingRoomProcedures
+      parameters:
+      - in: query
+        name: sortKey
+        required: false
+        schema:
+          $ref: "#/components/schemas/WaitingRoomSortKey"
+      - in: query
+        name: sortDirection
+        required: false
+        schema:
+          $ref: "#/components/schemas/SortDirection"
+      - in: query
+        name: pageNumber
+        required: false
+        schema:
+          type: integer
+          format: int32
+          minimum: 0
+      - in: query
+        name: pageSize
+        required: false
+        schema:
+          type: integer
+          format: int32
+          minimum: 1
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/GetWaitingRoomProceduresResponse"
+          description: OK
+      summary: Get waiting rooms.
+      tags:
+      - WaitingRoom
   /sti-procedures/{id}:
     get:
       operationId: getStiProcedure
@@ -1372,8 +1425,8 @@ paths:
       tags:
       - StiProtectionProcedure
   /sti-procedures/{id}/anon-ident-document:
-    post:
-      operationId: createAnonymousIdentificationDocument
+    get:
+      operationId: getAnonymousIdentificationDocument
       parameters:
       - in: path
         name: id
@@ -1389,7 +1442,7 @@ paths:
                 type: string
                 format: byte
           description: OK
-      summary: Create an anonymous identification document
+      summary: Get an anonymous identification document
       tags:
       - StiProtectionProcedure
   /sti-procedures/{id}/close:
@@ -1481,6 +1534,32 @@ paths:
       summary: Add medical history item to STI protection procedure.
       tags:
       - MedicalHistory
+  /sti-procedures/{procedureId}/waiting-room:
+    put:
+      operationId: updateWaitingRoomDetails
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/WaitingRoom"
+        required: true
+      responses:
+        "200":
+          content:
+            '*/*':
+              schema:
+                $ref: "#/components/schemas/WaitingRoom"
+          description: OK
+      summary: Update waiting room details for a procedure.
+      tags:
+      - WaitingRoom
   /task-metrics:
     get:
       operationId: getTaskMetrics
@@ -1697,6 +1776,14 @@ paths:
           description: OK
       tags:
       - TestHelper
+  /test-helper/notify/overdue-procedures:
+    post:
+      operationId: notifyOfOverdueProcedures
+      responses:
+        "200":
+          description: OK
+      tags:
+      - TestHelper
   /test-helper/population:
     post:
       operationId: populateDefaults
@@ -1912,6 +1999,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -2592,12 +2690,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreateMedicalHistoryRequest:
@@ -2927,6 +3021,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -3298,7 +3398,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -3424,6 +3526,19 @@ components:
       enum:
       - ASC
       - DESC
+    GetWaitingRoomProceduresResponse:
+      type: object
+      properties:
+        elements:
+          type: array
+          items:
+            $ref: "#/components/schemas/WaitingRoomProcedure"
+        totalNumberOfElements:
+          type: integer
+          format: int64
+      required:
+      - elements
+      - totalNumberOfElements
     HttpMethod:
       type: string
       enum:
@@ -3576,6 +3691,20 @@ components:
       - FORBIDDEN
       - NOT_FOUND
       - INTERNAL_SERVER_ERROR
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Mail:
       type: object
       allOf:
@@ -3621,13 +3750,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -3661,13 +3796,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -3753,15 +3885,9 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     Pdf:
       type: object
       allOf:
@@ -4295,6 +4421,12 @@ components:
         properties:
           additionalComments:
             type: string
+          amountAbortions:
+            type: integer
+            format: int32
+          amountPregnancies:
+            type: integer
+            format: int32
           contactToClarifyDuration:
             type: string
             format: date
@@ -4304,6 +4436,14 @@ components:
             type: string
           examinations:
             $ref: "#/components/schemas/Examination"
+          knownOperations:
+            type: string
+          lastCancerScreeningDuration:
+            type: string
+            format: date
+          lastMenstruationDuration:
+            type: string
+            format: date
           medications:
             type: string
           previousIllnesses:
@@ -4374,6 +4514,8 @@ components:
           $ref: "#/components/schemas/Person"
         status:
           $ref: "#/components/schemas/ProcedureStatus"
+        waitingRoom:
+          $ref: "#/components/schemas/WaitingRoom"
       required:
       - appointment
       - concern
@@ -4381,6 +4523,7 @@ components:
       - id
       - person
       - status
+      - waitingRoom
     StiProtectionProcedureOverview:
       type: object
       properties:
@@ -4439,6 +4582,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -4450,6 +4598,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -4715,3 +4864,61 @@ components:
       required:
       - userIdsWithEventConflicts
       - userIdsWithoutEventConflicts
+    WaitingRoom:
+      type: object
+      properties:
+        info:
+          type: string
+          maxLength: 60
+          minLength: 0
+        status:
+          $ref: "#/components/schemas/WaitingStatus"
+    WaitingRoomProcedure:
+      type: object
+      properties:
+        accessCode:
+          type: string
+        gender:
+          $ref: "#/components/schemas/Gender"
+        modifiedAt:
+          type: string
+          format: date-time
+        procedureId:
+          type: string
+          format: uuid
+        waitingRoom:
+          $ref: "#/components/schemas/WaitingRoom"
+        yearOfBirth:
+          type: object
+          properties:
+            leap:
+              type: boolean
+            value:
+              type: integer
+              format: int32
+      required:
+      - accessCode
+      - gender
+      - modifiedAt
+      - procedureId
+      - waitingRoom
+      - yearOfBirth
+    WaitingRoomSortKey:
+      type: string
+      enum:
+      - ID
+      - YEAR_OF_BIRTH
+      - GENDER
+      - STATUS
+      - INFO
+      - MODIFIED_AT
+    WaitingStatus:
+      type: string
+      enum:
+      - WAITING_FOR_CONSULTATION
+      - WAITING_FOR_RESULTS_REVIEW
+      - WAITING_FOR_TESTS
+      - IN_CONSULTATION
+      - IN_TESTING
+      - CANCELLED
+      - DONE
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
index 4fbed13ab..2da51daf4 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/AppointmentService.java
@@ -5,20 +5,45 @@
 
 package de.eshg.stiprotection;
 
+import de.eshg.base.calendar.CalendarApi;
+import de.eshg.base.calendar.CalendarEventApi;
+import de.eshg.base.calendar.api.BusinessCaseEventRequest;
+import de.eshg.base.calendar.api.DetailedEvent;
+import de.eshg.base.calendar.api.EventTimeData;
+import de.eshg.base.calendar.api.GetUserCalendarsRequest;
+import de.eshg.base.calendar.api.GetUserCalendarsResponse;
+import de.eshg.base.calendar.api.UserCalendar;
 import de.eshg.lib.appointmentblock.AppointmentBlockSlotUtil;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlock;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup;
 import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 import de.eshg.stiprotection.persistence.db.UserDefinedAppointment;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Stream;
 import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
 
 @Service
 public class AppointmentService {
+  private final CalendarEventApi calendarEventApi;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final CalendarApi calendarApi;
 
-  public AppointmentService(AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+  public AppointmentService(
+      CalendarApi calendarApi,
+      CalendarEventApi calendarEventApi,
+      AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+    this.calendarApi = calendarApi;
+    this.calendarEventApi = calendarEventApi;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
   }
 
@@ -27,7 +52,52 @@ public class AppointmentService {
     checkExistingAppointment(procedure);
     AppointmentType appointmentType = AppointmentType.valueOf(procedure.getConcern().name());
     Instant end = start.plus(Duration.ofMinutes(durationInMinutes));
+
     appointmentBlockSlotUtil.updateAppointment(appointmentType, null, procedure, start, end);
+    createAppointmentCalendarEvent(procedure, start, end);
+  }
+
+  private void createAppointmentCalendarEvent(
+      StiProtectionProcedure procedure, Instant start, Instant end) {
+    List<UUID> userIds = getUserIdsFromAppointment(procedure.getAppointment());
+
+    GetUserCalendarsResponse userCalendarsResponse =
+        calendarApi.getUserCalendars(new GetUserCalendarsRequest(userIds));
+    List<UUID> calendarIds =
+        userCalendarsResponse.userCalendars().stream().map(UserCalendar::calendarId).toList();
+
+    DetailedEvent appointmentEventData =
+        calendarEventApi.addBusinessCaseEvent(
+            new BusinessCaseEventRequest(calendarIds, new EventTimeData(start, end, false)));
+    procedure.setCalendarEventId(appointmentEventData.id());
+  }
+
+  private List<UUID> getUserIdsFromAppointment(Appointment appointment) {
+    validateAppointmentBlockGroup(appointment);
+    AppointmentBlockGroup appointmentBlockGroup =
+        appointment.getAppointmentBlock().getAppointmentBlockGroup();
+
+    List<UUID> userIds =
+        Stream.concat(
+                appointmentBlockGroup.getPhysicians().stream(),
+                appointmentBlockGroup.getConsultants().stream())
+            .toList();
+
+    if (CollectionUtils.isEmpty(userIds)) {
+      throw new BadRequestException(
+          ErrorCode.DATA_INTEGRITY_VIOLATION,
+          "The AppointmentBlockGroup of the selected appointment has no physician or consultant assigned.");
+    }
+    return userIds;
+  }
+
+  private static void validateAppointmentBlockGroup(Appointment appointment) {
+    Assert.notNull(appointment, "Appointment should not be null.");
+    AppointmentBlock appointmentBlock =
+        Objects.requireNonNull(
+            appointment.getAppointmentBlock(), "AppointmentBlock should not be null.");
+    Objects.requireNonNull(
+        appointmentBlock.getAppointmentBlockGroup(), "AppointmentBlockGroup should not be null.");
   }
 
   public void bookUserDefinedAppointment(
@@ -40,12 +110,12 @@ public class AppointmentService {
     if (procedure.getUserDefinedAppointment() != null) {
       throw new BadRequestException(
           String.format(
-              "Procedure step %s already has an user defined appointment.", procedure.getId()));
+              "Procedure %s already has an user defined appointment.", procedure.getId()));
     }
     if (procedure.getAppointment() != null) {
       throw new BadRequestException(
           String.format(
-              "Procedure step %s already has an appointment from appointment block.",
+              "Procedure %s already has an appointment from appointment block.",
               procedure.getId()));
     }
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java
new file mode 100644
index 000000000..d25c48df3
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/OverdueProceduresNotifier.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import de.eshg.lib.notification.domain.model.SimpleNotification;
+import de.eshg.lib.notification.domain.repository.SimpleNotificationRepository;
+import de.eshg.lib.procedure.domain.model.Task;
+import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.Period;
+import java.util.List;
+import java.util.UUID;
+import net.javacrumbs.shedlock.core.LockAssert;
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OverdueProceduresNotifier {
+
+  private final StiProtectionProcedureRepository procedures;
+  private final SimpleNotificationRepository notificationRepository;
+  private final ModuleClientAuthenticator moduleClientAuthenticator;
+  private final Clock clock;
+
+  @Value("${eshg.sti-protection.overdue-procedures.overdue-days:180}")
+  private int overdueDays;
+
+  public OverdueProceduresNotifier(
+      StiProtectionProcedureRepository procedures,
+      SimpleNotificationRepository notificationRepository,
+      ModuleClientAuthenticator moduleClientAuthenticator,
+      Clock clock) {
+    this.procedures = procedures;
+    this.notificationRepository = notificationRepository;
+    this.moduleClientAuthenticator = moduleClientAuthenticator;
+    this.clock = clock;
+  }
+
+  @Scheduled(cron = "${eshg.sti-protection.overdue-procedures.cron}")
+  @SchedulerLock(name = "OverdueProceduresNotifier")
+  @Transactional
+  public void run() {
+    LockAssert.assertLocked();
+    runNow();
+  }
+
+  @Transactional
+  public void runNow() {
+    Instant overdueLimit = clock.instant().minus(Period.ofDays(overdueDays));
+    List<SimpleNotification> notifications = generateNotifications(overdueLimit);
+    SecurityContextHolder.clearContext();
+    moduleClientAuthenticator.doWithModuleClientAuthentication(
+        () -> notificationRepository.saveAll(notifications));
+  }
+
+  private List<SimpleNotification> generateNotifications(Instant overdueLimit) {
+    return procedures.findByCreatedAtBefore(overdueLimit).stream()
+        .map(procedure -> procedure.getTasks().getFirst())
+        .map(Task::getAssigneeId)
+        .distinct()
+        .map(OverdueProceduresNotifier::toNotification)
+        .toList();
+  }
+
+  private static SimpleNotification toNotification(UUID assigneeId) {
+    return new SimpleNotification(
+        assigneeId,
+        "Vorgang überprüfen",
+        "Es existieren veraltete, noch nicht geschlossene Vorgänge. Bitte überprüfen Sie diese.");
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
index 04c081866..70c3714be 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionApplication.java
@@ -9,11 +9,13 @@ import de.eshg.lib.common.BusinessModule;
 import de.eshg.rest.service.security.config.StiProtectionPublicSecurityConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
 @Import(StiProtectionPublicSecurityConfig.class)
+@EntityScan("de.eshg")
 public class StiProtectionApplication {
 
   @Bean
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
index ea7752115..1800b05cb 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureController.java
@@ -104,12 +104,12 @@ public class StiProtectionProcedureController {
         stiProtectionService.getProcedure(procedureId));
   }
 
-  @PostMapping(path = "/{id}/anon-ident-document")
-  @Operation(summary = "Create an anonymous identification document")
+  @GetMapping(path = "/{id}/anon-ident-document")
+  @Operation(summary = "Get an anonymous identification document")
   @Transactional
-  public ResponseEntity<byte[]> createAnonymousIdentificationDocument(
+  public ResponseEntity<byte[]> getAnonymousIdentificationDocument(
       @PathVariable("id") UUID procedureId) {
-    Pdf pdf = stiProtectionService.createAnonymousIdentificationDocument(procedureId);
+    Pdf pdf = stiProtectionService.getAnonymousIdentificationDocument(procedureId);
     byte[] content = pdf.getFileContent().getContent();
     return ResponseEntity.ok()
         .contentType(MediaType.APPLICATION_PDF)
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
index c39e277b5..ecc4886e9 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureService.java
@@ -47,6 +47,7 @@ import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure_;
 import de.eshg.stiprotection.persistence.db.StiProtectionTask;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
 import jakarta.persistence.criteria.Join;
 import jakarta.persistence.criteria.JoinType;
 import jakarta.persistence.criteria.Path;
@@ -93,6 +94,7 @@ public class StiProtectionProcedureService {
     procedure.addRelatedPerson(createPerson(request));
     procedure.addTask(createTask());
     bookAppointment(procedure, request);
+    procedure.setWaitingRoom(new WaitingRoom());
     return repository.save(procedure);
   }
 
@@ -195,7 +197,8 @@ public class StiProtectionProcedureService {
         procedure.getConcern(),
         procedure.getPerson(),
         procedure.getAppointment(),
-        procedure.getUserDefinedAppointment());
+        procedure.getUserDefinedAppointment(),
+        procedure.getWaitingRoom());
   }
 
   public StiProtectionProcedureData getProcedure(UUID procedureId) {
@@ -249,7 +252,7 @@ public class StiProtectionProcedureService {
         "%s: unexpected procedure status: %s".formatted(procedureId, procedureStatus));
   }
 
-  public Pdf createAnonymousIdentificationDocument(UUID procedureId) {
+  public Pdf getAnonymousIdentificationDocument(UUID procedureId) {
     StiProtectionProcedureData procedure = getProcedure(procedureId);
     TimeRange timeRange = toAppointmentTimeRange(procedure);
     Department department = mapToDepartment(departmentClient.getDepartmentInfo());
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java
new file mode 100644
index 000000000..16b25a743
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import de.eshg.api.commons.InlineParameterObject;
+import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.stiprotection.api.waitingroom.GetWaitingRoomProceduresResponse;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedurePaginationAndSortParameters;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.transaction.Transactional;
+import jakarta.validation.Valid;
+import java.util.UUID;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = WaitingRoomController.BASE_URL)
+@Tag(name = "WaitingRoom")
+public class WaitingRoomController {
+  public static final String BASE_URL = BaseUrls.StiProtection.PROCEDURE_CONTROLLER;
+
+  private final WaitingRoomService waitingRoomService;
+
+  public WaitingRoomController(WaitingRoomService waitingRoomService) {
+    this.waitingRoomService = waitingRoomService;
+  }
+
+  @PutMapping("/{procedureId}/waiting-room")
+  @Operation(summary = "Update waiting room details for a procedure.")
+  @Transactional
+  public WaitingRoomDto updateWaitingRoomDetails(
+      @PathVariable("procedureId") UUID procedureId, @Valid @RequestBody WaitingRoomDto request) {
+    return waitingRoomService.updateWaitingRoomDetails(procedureId, request);
+  }
+
+  @GetMapping("/waiting-room-procedures")
+  @Operation(summary = "Get waiting rooms.")
+  @Transactional
+  public GetWaitingRoomProceduresResponse getWaitingRoomProcedures(
+      @InlineParameterObject @ParameterObject @Valid
+          WaitingRoomProcedurePaginationAndSortParameters paginationAndSortParameters) {
+    return waitingRoomService.getWaitingRoomProcedures(paginationAndSortParameters);
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java
new file mode 100644
index 000000000..910cb995e
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/WaitingRoomService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection;
+
+import static de.eshg.stiprotection.StiProtectionProcedureService.unexpectedProcedureStatus;
+
+import de.eshg.base.SortDirection;
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.stiprotection.api.waitingroom.GetWaitingRoomProceduresResponse;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedurePaginationAndSortParameters;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomSortKey;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomMapper;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomProcedureMapper;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingStatusMapper;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoomSpecification;
+import jakarta.validation.Valid;
+import java.util.UUID;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WaitingRoomService {
+
+  private final StiProtectionProcedureService stiProtectionProcedureService;
+  private final StiProtectionProcedureRepository stiProtectionProcedureRepository;
+
+  public WaitingRoomService(
+      StiProtectionProcedureService stiProtectionProcedureService,
+      StiProtectionProcedureRepository stiProtectionProcedureRepository) {
+    this.stiProtectionProcedureService = stiProtectionProcedureService;
+    this.stiProtectionProcedureRepository = stiProtectionProcedureRepository;
+  }
+
+  public WaitingRoomDto updateWaitingRoomDetails(UUID procedureId, @Valid WaitingRoomDto request) {
+    StiProtectionProcedure procedure =
+        stiProtectionProcedureService.findProcedureByExternalId(procedureId);
+
+    ProcedureStatus procedureStatus = procedure.getProcedureStatus();
+    if (!procedureStatus.isOpen()) {
+      throw unexpectedProcedureStatus(procedureId, procedureStatus);
+    }
+
+    WaitingRoom waitingRoom = procedure.getWaitingRoom();
+    waitingRoom.setInfo(request.info());
+    waitingRoom.setStatus(WaitingStatusMapper.toDatabaseType(request.status()));
+
+    StiProtectionProcedure persistedProcedure = stiProtectionProcedureRepository.save(procedure);
+    return WaitingRoomMapper.toInterfaceType(persistedProcedure.getWaitingRoom());
+  }
+
+  public GetWaitingRoomProceduresResponse getWaitingRoomProcedures(
+      @Valid WaitingRoomProcedurePaginationAndSortParameters parameters) {
+
+    WaitingRoomSpecification specification =
+        new WaitingRoomSpecification(
+            parameters.sortKeyOrFallback(WaitingRoomSortKey.ID),
+            WaitingRoomMapper.toDatabaseType(
+                parameters.sortDirectionOrFallback(SortDirection.DESC)));
+
+    PageRequest pageable =
+        PageRequest.of(parameters.pageNumberOrFallback(0), parameters.pageSizeOrFallback(25));
+
+    Page<StiProtectionProcedure> results =
+        stiProtectionProcedureRepository.findAll(specification, pageable);
+
+    return new GetWaitingRoomProceduresResponse(
+        results.stream().map(WaitingRoomProcedureMapper::toInterface).toList(),
+        results.getNumberOfElements());
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
index addee12e0..4110ab675 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureDto.java
@@ -7,6 +7,7 @@ package de.eshg.stiprotection.api;
 
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.procedure.model.ProcedureStatusDto;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -20,4 +21,5 @@ public record StiProtectionProcedureDto(
     @NotNull ProcedureStatusDto status,
     @NotNull ConcernDto concern,
     @NotNull @Valid PersonDto person,
-    @NotNull @Valid AppointmentDto appointment) {}
+    @NotNull @Valid AppointmentDto appointment,
+    @NotNull @Valid WaitingRoomDto waitingRoom) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
index 3af9a5b90..d6c0072b3 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/medicalhistory/SexWorkMedicalHistoryDto.java
@@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.PastOrPresent;
+import jakarta.validation.constraints.PositiveOrZero;
 import java.time.LocalDate;
 
 @Schema(name = SexWorkMedicalHistoryDto.SCHEMA_NAME)
@@ -17,6 +18,11 @@ public record SexWorkMedicalHistoryDto(
     String currentSymptoms,
     @PastOrPresent LocalDate contactToClarifyDuration,
     RelationshipModelDto relationshipModel,
+    @PastOrPresent LocalDate lastMenstruationDuration,
+    @PastOrPresent LocalDate lastCancerScreeningDuration,
+    @PositiveOrZero Integer amountPregnancies,
+    @PositiveOrZero Integer amountAbortions,
+    String knownOperations,
     String medications,
     @Valid ExaminationDto examinations,
     @NotNull @Valid PreviousIllnessDto previousIllnesses,
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java
new file mode 100644
index 000000000..b5afd273e
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/GetWaitingRoomProceduresResponse.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.base.PagedResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+@Schema(name = "GetWaitingRoomProceduresResponse")
+public record GetWaitingRoomProceduresResponse(
+    @Valid @NotNull List<WaitingRoomProcedureDto> elements, @NotNull long totalNumberOfElements)
+    implements PagedResponse<WaitingRoomProcedureDto> {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java
new file mode 100644
index 000000000..7bb3e10a0
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomDto.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "WaitingRoom")
+public record WaitingRoomDto(@Size(max = 60) String info, WaitingStatusDto status) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java
new file mode 100644
index 000000000..34896e28f
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedureDto.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.stiprotection.persistence.db.Gender;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import java.time.Year;
+import java.util.UUID;
+
+@Schema(name = "WaitingRoomProcedure")
+public record WaitingRoomProcedureDto(
+    @NotNull UUID procedureId,
+    @NotNull String accessCode,
+    @NotNull Year yearOfBirth,
+    @NotNull Gender gender,
+    @NotNull @Valid WaitingRoomDto waitingRoom,
+    @NotNull Instant modifiedAt) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java
new file mode 100644
index 000000000..d24dc1f42
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomProcedurePaginationAndSortParameters.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import de.eshg.base.PaginationParameters;
+import de.eshg.base.SortDirection;
+import de.eshg.base.SortParameters;
+import jakarta.validation.constraints.Min;
+
+public record WaitingRoomProcedurePaginationAndSortParameters(
+    WaitingRoomSortKey sortKey,
+    SortDirection sortDirection,
+    @Min(0) Integer pageNumber,
+    @Min(1) Integer pageSize)
+    implements PaginationParameters, SortParameters<WaitingRoomSortKey> {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java
new file mode 100644
index 000000000..9121601db
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingRoomSortKey.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+public enum WaitingRoomSortKey {
+  ID,
+  YEAR_OF_BIRTH,
+  GENDER,
+  STATUS,
+  INFO,
+  MODIFIED_AT
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java
new file mode 100644
index 000000000..16ef24cc5
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/waitingroom/WaitingStatusDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.api.waitingroom;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "WaitingStatus")
+public enum WaitingStatusDto {
+  WAITING_FOR_CONSULTATION,
+  WAITING_FOR_RESULTS_REVIEW,
+  WAITING_FOR_TESTS,
+  IN_CONSULTATION,
+  IN_TESTING,
+  CANCELLED,
+  DONE
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
index ce6167f9e..6085c876b 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/calendar/StiProtectionEventMetadataService.java
@@ -11,8 +11,13 @@ import de.eshg.lib.appointmentblock.AppointmentBlockSlotUtil;
 import de.eshg.lib.appointmentblock.model.AppointmentBlockData;
 import de.eshg.lib.appointmentblock.persistence.AppointmentBlockRepository;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
+import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlock;
+import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Stream;
 import org.springframework.stereotype.Service;
@@ -22,30 +27,56 @@ public class StiProtectionEventMetadataService implements EventMetadataService {
 
   private final AppointmentBlockRepository appointmentBlockRepository;
   private final AppointmentBlockSlotUtil appointmentBlockSlotUtil;
+  private final StiProtectionProcedureRepository procedureRepository;
 
   public StiProtectionEventMetadataService(
       AppointmentBlockRepository appointmentBlockRepository,
-      AppointmentBlockSlotUtil appointmentBlockSlotUtil) {
+      AppointmentBlockSlotUtil appointmentBlockSlotUtil,
+      StiProtectionProcedureRepository procedureRepository) {
     this.appointmentBlockRepository = appointmentBlockRepository;
     this.appointmentBlockSlotUtil = appointmentBlockSlotUtil;
+    this.procedureRepository = procedureRepository;
   }
 
   @Override
   public Stream<EventWithMetaData> findByCalendarEventIds(List<UUID> eventIds) {
     List<AppointmentBlock> appointmentBlocks =
         appointmentBlockRepository.findAllByCalendarEventIdInOrderById(eventIds);
+    Stream<EventWithMetaData> appointmentBlockMetaData =
+        appointmentBlockSlotUtil
+            .augmentAppointmentBlocksWithEventDetails(appointmentBlocks)
+            .values()
+            .stream()
+            .map(StiProtectionEventMetadataService::mapAppointmentBlockToEventWithMetaData);
+
+    Stream<EventWithMetaData> stiProcedures =
+        procedureRepository.findAllByCalendarEventIdOrderById(eventIds).stream()
+            .map(this::mapStiProcedureAppointmentToEventMetaData);
+
+    return Stream.concat(appointmentBlockMetaData, stiProcedures);
+  }
+
+  private EventWithMetaData mapStiProcedureAppointmentToEventMetaData(
+      StiProtectionProcedure procedure) {
+    Appointment appointment =
+        Objects.requireNonNull(procedure.getAppointment(), "Appointment should not be null.");
+    AppointmentType type =
+        Objects.requireNonNull(
+            getAppointmentType(appointment.getAppointmentBlock()),
+            "AppointmentBlock should not be null.");
 
-    return appointmentBlockSlotUtil
-        .augmentAppointmentBlocksWithEventDetails(appointmentBlocks)
-        .values()
-        .stream()
-        .map(StiProtectionEventMetadataService::mapAppointmentBlockToEventWithMetaData);
+    return new EventWithMetaData(
+        procedure.getCalendarEventId(),
+        mapAppointmentTypeToSubjectString(type),
+        null,
+        null,
+        procedure.getExternalId());
   }
 
   private static EventWithMetaData mapAppointmentBlockToEventWithMetaData(
       AppointmentBlockData appointmentBlockData) {
-    AppointmentType type =
-        appointmentBlockData.appointmentBlock().getAppointmentBlockGroup().getType();
+
+    AppointmentType type = getAppointmentType(appointmentBlockData);
     validateAppointmentType(type);
 
     String subject = mapAppointmentTypeToSubjectString(type);
@@ -64,10 +95,25 @@ public class StiProtectionEventMetadataService implements EventMetadataService {
         null);
   }
 
+  private static AppointmentType getAppointmentType(AppointmentBlockData appointmentBlockData) {
+    return getAppointmentType(
+        Objects.requireNonNull(
+            appointmentBlockData.appointmentBlock(), "AppointmentBlock should not be null."));
+  }
+
+  private static AppointmentType getAppointmentType(AppointmentBlock appointmentBlock) {
+    AppointmentBlockGroup appointmentBlockGroup =
+        Objects.requireNonNull(
+            appointmentBlock.getAppointmentBlockGroup(),
+            "AppointmentBlockGroup should not be null.");
+    return Objects.requireNonNull(
+        appointmentBlockGroup.getType(), "AppointmentType should not be null.");
+  }
+
   private static void validateAppointmentType(AppointmentType type) {
-    if (type == null) {
-      throw new NullPointerException("The appointment block type should not be null");
-    } else if (type != AppointmentType.HIV_STI_CONSULTATION
+    Objects.requireNonNull(type, "The AppointmentType should not be null.");
+
+    if (type != AppointmentType.HIV_STI_CONSULTATION
         && type != AppointmentType.SEX_WORK
         && type != AppointmentType.RESULTS_REVIEW) {
       throw new IllegalArgumentException(createIllegalAppointmentTypeMessage(type));
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java
new file mode 100644
index 000000000..a2e19c91d
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/config/StiProtectionNotificationConfiguration.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.config;
+
+import static de.eshg.lib.notification.spring.config.NotificationLibraryInternalSecurityConfig.NOTIFICATION_ACCESS_ROLE;
+
+import de.eshg.lib.keycloak.EmployeePermissionRole;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class StiProtectionNotificationConfiguration {
+
+  @Bean(name = NOTIFICATION_ACCESS_ROLE)
+  EmployeePermissionRole notificationAccessRole() {
+    return EmployeePermissionRole.STI_PROTECTION_ADMIN;
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
index 6fe53fdf9..76811c02e 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/StiProtectionProcedureMapper.java
@@ -9,6 +9,7 @@ import de.eshg.lib.procedure.mapping.ProcedureMapper;
 import de.eshg.stiprotection.api.CreateProcedureResponse;
 import de.eshg.stiprotection.api.StiProtectionProcedureDto;
 import de.eshg.stiprotection.api.StiProtectionProcedureOverviewDto;
+import de.eshg.stiprotection.mapper.waitingroom.WaitingRoomMapper;
 import de.eshg.stiprotection.persistence.data.StiProtectionProcedureData;
 import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
 
@@ -29,7 +30,8 @@ public class StiProtectionProcedureMapper {
         ConcernMapper.toInterfaceType(procedureData.concern()),
         PersonMapper.toInterfaceType(procedureData.person()),
         AppointmentMapper.toInterfaceType(
-            procedureData.appointment(), procedureData.userDefinedAppointment()));
+            procedureData.appointment(), procedureData.userDefinedAppointment()),
+        WaitingRoomMapper.toInterfaceType(procedureData.waitingRoom()));
   }
 
   public static StiProtectionProcedureOverviewDto toOverviewType(
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
index 78a7c4227..915a785d8 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/medicalhistory/MedicalHistoryMapper.java
@@ -33,6 +33,11 @@ public final class MedicalHistoryMapper {
         entity.getCurrentSymptoms(),
         entity.getContactToClarifyDuration(),
         RelationshipModelMapper.toInterfaceType(entity.getRelationshipModel()),
+        entity.getLastMenstruationDuration(),
+        entity.getLastCancerScreeningDuration(),
+        entity.getAmountPregnancies(),
+        entity.getAmountAbortions(),
+        entity.getKnownOperations(),
         entity.getMedications(),
         ExaminationMapper.toInterfaceType(entity.getExaminations()),
         PreviousIllnessMapper.toInterfaceType(entity.getPreviousIllnesses()),
@@ -63,6 +68,11 @@ public final class MedicalHistoryMapper {
 
   private static MedicalHistory toDatabaseType(SexWorkMedicalHistoryDto dto) {
     SexWorkMedicalHistory sexWorkMedicalHistory = new SexWorkMedicalHistory();
+    sexWorkMedicalHistory.setLastMenstruationDuration(dto.lastMenstruationDuration());
+    sexWorkMedicalHistory.setLastCancerScreeningDuration(dto.lastCancerScreeningDuration());
+    sexWorkMedicalHistory.setAmountPregnancies(dto.amountPregnancies());
+    sexWorkMedicalHistory.setAmountAbortions(dto.amountAbortions());
+    sexWorkMedicalHistory.setKnownOperations(dto.knownOperations());
     sexWorkMedicalHistory.setMedications(dto.medications());
     return updateMedicalHistory(dto, sexWorkMedicalHistory);
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java
new file mode 100644
index 000000000..130a41120
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.base.SortDirection;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomDto;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import org.springframework.data.domain.Sort;
+
+public class WaitingRoomMapper {
+  private WaitingRoomMapper() {}
+
+  public static WaitingRoomDto toInterfaceType(WaitingRoom waitingRoom) {
+    if (waitingRoom == null) {
+      return null;
+    }
+
+    return new WaitingRoomDto(
+        waitingRoom.getInfo(), WaitingStatusMapper.toInterfaceType(waitingRoom.getStatus()));
+  }
+
+  public static Sort.Direction toDatabaseType(SortDirection sortDirection) {
+    return switch (sortDirection) {
+      case ASC -> Sort.Direction.ASC;
+      case DESC -> Sort.Direction.DESC;
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java
new file mode 100644
index 000000000..a4727b353
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingRoomProcedureMapper.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomProcedureDto;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+
+public class WaitingRoomProcedureMapper {
+  private WaitingRoomProcedureMapper() {}
+
+  public static WaitingRoomProcedureDto toInterface(StiProtectionProcedure procedure) {
+    return new WaitingRoomProcedureDto(
+        procedure.getExternalId(),
+        "accessCode", // TODO: Map actual accessCode when implemented
+        procedure.getPerson().getYearOfBirth(),
+        procedure.getPerson().getGender(),
+        WaitingRoomMapper.toInterfaceType(procedure.getWaitingRoom()),
+        procedure.getWaitingRoom().getModifiedAt());
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java
new file mode 100644
index 000000000..f154be0cc
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/waitingroom/WaitingStatusMapper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.mapper.waitingroom;
+
+import de.eshg.stiprotection.api.waitingroom.WaitingStatusDto;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingStatus;
+
+public class WaitingStatusMapper {
+
+  private WaitingStatusMapper() {}
+
+  public static WaitingStatusDto toInterfaceType(WaitingStatus entity) {
+    if (entity == null) {
+      return null;
+    }
+
+    return switch (entity) {
+      case WAITING_FOR_CONSULTATION -> WaitingStatusDto.WAITING_FOR_CONSULTATION;
+      case WAITING_FOR_RESULTS_REVIEW -> WaitingStatusDto.WAITING_FOR_RESULTS_REVIEW;
+      case WAITING_FOR_TESTS -> WaitingStatusDto.WAITING_FOR_TESTS;
+      case IN_CONSULTATION -> WaitingStatusDto.IN_CONSULTATION;
+      case IN_TESTING -> WaitingStatusDto.IN_TESTING;
+      case CANCELLED -> WaitingStatusDto.CANCELLED;
+      case DONE -> WaitingStatusDto.DONE;
+    };
+  }
+
+  public static WaitingStatus toDatabaseType(WaitingStatusDto dto) {
+    if (dto == null) {
+      return null;
+    }
+
+    return switch (dto) {
+      case WAITING_FOR_CONSULTATION -> WaitingStatus.WAITING_FOR_CONSULTATION;
+      case WAITING_FOR_RESULTS_REVIEW -> WaitingStatus.WAITING_FOR_RESULTS_REVIEW;
+      case WAITING_FOR_TESTS -> WaitingStatus.WAITING_FOR_TESTS;
+      case IN_CONSULTATION -> WaitingStatus.IN_CONSULTATION;
+      case IN_TESTING -> WaitingStatus.IN_TESTING;
+      case CANCELLED -> WaitingStatus.CANCELLED;
+      case DONE -> WaitingStatus.DONE;
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
index 9992a83f4..f6c898b1b 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/pdf/identification/AnonymousIdentificationDocumentService.java
@@ -8,7 +8,6 @@ package de.eshg.stiprotection.pdf.identification;
 import de.eshg.lib.document.generator.DocumentGenerator;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.file.FileFactory;
 import java.io.ByteArrayOutputStream;
 import java.time.Clock;
@@ -37,8 +36,7 @@ public class AnonymousIdentificationDocumentService {
     byte[] bytes = createPdfFromTemplate(data);
     String fileName = fileName();
     PdfMetaData pdfMetaData = pdfMetaData();
-    return FileFactory.createPdfWithMetaData(
-        fileName, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(fileName, bytes, pdfMetaData);
   }
 
   private String fileName() {
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
index 849876253..e8179ce6e 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/data/StiProtectionProcedureData.java
@@ -10,6 +10,7 @@ import de.eshg.lib.procedure.domain.model.ProcedureStatus;
 import de.eshg.stiprotection.persistence.db.Concern;
 import de.eshg.stiprotection.persistence.db.Person;
 import de.eshg.stiprotection.persistence.db.UserDefinedAppointment;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
 import java.time.Instant;
 import java.util.UUID;
 
@@ -20,4 +21,5 @@ public record StiProtectionProcedureData(
     Concern concern,
     Person person,
     Appointment appointment,
-    UserDefinedAppointment userDefinedAppointment) {}
+    UserDefinedAppointment userDefinedAppointment,
+    WaitingRoom waitingRoom) {}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
index 1e2c261aa..8b23fe396 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedure.java
@@ -12,12 +12,15 @@ import de.eshg.lib.common.SensitivityLevel;
 import de.eshg.lib.procedure.domain.model.Procedure;
 import de.eshg.stiprotection.persistence.db.medicalhistory.MedicalHistory;
 import de.eshg.stiprotection.persistence.db.medicalhistory.MedicalHistory_;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom;
+import de.eshg.stiprotection.persistence.db.waitingroom.WaitingRoom_;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.FetchType;
 import jakarta.persistence.OneToOne;
 import jakarta.persistence.Transient;
+import java.util.UUID;
 import org.hibernate.annotations.JdbcType;
 import org.hibernate.dialect.PostgreSQLEnumJdbcType;
 import org.springframework.util.Assert;
@@ -52,6 +55,18 @@ public class StiProtectionProcedure
       mappedBy = UserDefinedAppointment_.PROCEDURE)
   private UserDefinedAppointment userDefinedAppointment;
 
+  @DataSensitivity(SensitivityLevel.SENSITIVE)
+  @Column(unique = true)
+  private UUID calendarEventId;
+
+  @DataSensitivity(SensitivityLevel.PSEUDONYMIZED)
+  @OneToOne(
+      optional = false,
+      fetch = FetchType.LAZY,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
+      mappedBy = WaitingRoom_.PROCEDURE)
+  private WaitingRoom waitingRoom;
+
   @Transient
   public Person getPerson() {
     Assert.isTrue(getRelatedPersons().size() == 1, "There should be exactly one related person");
@@ -103,4 +118,21 @@ public class StiProtectionProcedure
     }
     this.userDefinedAppointment = userDefinedAppointment;
   }
+
+  public UUID getCalendarEventId() {
+    return calendarEventId;
+  }
+
+  public void setCalendarEventId(UUID calendarEventId) {
+    this.calendarEventId = calendarEventId;
+  }
+
+  public WaitingRoom getWaitingRoom() {
+    return waitingRoom;
+  }
+
+  public void setWaitingRoom(WaitingRoom waitingRoom) {
+    this.waitingRoom = waitingRoom;
+    waitingRoom.setProcedure(this);
+  }
 }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
index 4101fa19d..7e16fac40 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/StiProtectionProcedureRepository.java
@@ -6,8 +6,21 @@
 package de.eshg.stiprotection.persistence.db;
 
 import de.eshg.lib.procedure.domain.repository.ProcedureRepository;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface StiProtectionProcedureRepository
     extends ProcedureRepository<StiProtectionProcedure>,
-        JpaSpecificationExecutor<StiProtectionProcedure> {}
+        JpaSpecificationExecutor<StiProtectionProcedure> {
+  @Query(
+      """
+  select sti from StiProtectionProcedure sti where sti.calendarEventId in :calendarEventIds order by sti.calendarEventId
+  """)
+  List<StiProtectionProcedure> findAllByCalendarEventIdOrderById(Collection<UUID> calendarEventIds);
+
+  List<StiProtectionProcedure> findByCreatedAtBefore(Instant overdueDate);
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
index e25b2f980..e25e14d4c 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/medicalhistory/SexWorkMedicalHistory.java
@@ -9,14 +9,65 @@ import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import jakarta.persistence.DiscriminatorValue;
 import jakarta.persistence.Entity;
+import java.time.LocalDate;
 
 @Entity
 @DataSensitivity(SensitivityLevel.UNDEFINED)
 @DiscriminatorValue(value = "SEX_WORK")
 public class SexWorkMedicalHistory extends MedicalHistory {
 
+  private LocalDate lastMenstruationDuration;
+
+  private LocalDate lastCancerScreeningDuration;
+
+  private Integer amountPregnancies;
+
+  private Integer amountAbortions;
+
+  private String knownOperations;
+
   private String medications;
 
+  public LocalDate getLastMenstruationDuration() {
+    return lastMenstruationDuration;
+  }
+
+  public void setLastMenstruationDuration(LocalDate lastMenstruationDuration) {
+    this.lastMenstruationDuration = lastMenstruationDuration;
+  }
+
+  public LocalDate getLastCancerScreeningDuration() {
+    return lastCancerScreeningDuration;
+  }
+
+  public void setLastCancerScreeningDuration(LocalDate lastCancerScreeningDuration) {
+    this.lastCancerScreeningDuration = lastCancerScreeningDuration;
+  }
+
+  public Integer getAmountPregnancies() {
+    return amountPregnancies;
+  }
+
+  public void setAmountPregnancies(Integer amountPregnancies) {
+    this.amountPregnancies = amountPregnancies;
+  }
+
+  public Integer getAmountAbortions() {
+    return amountAbortions;
+  }
+
+  public void setAmountAbortions(Integer amountAbortions) {
+    this.amountAbortions = amountAbortions;
+  }
+
+  public String getKnownOperations() {
+    return knownOperations;
+  }
+
+  public void setKnownOperations(String knownOperations) {
+    this.knownOperations = knownOperations;
+  }
+
   public String getMedications() {
     return medications;
   }
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java
new file mode 100644
index 000000000..4277ee1c0
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoom.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+import de.eshg.domain.model.GenericEntity;
+import de.eshg.lib.common.DataSensitivity;
+import de.eshg.lib.common.SensitivityLevel;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.Id;
+import jakarta.persistence.MapsId;
+import jakarta.persistence.OneToOne;
+import jakarta.validation.constraints.NotNull;
+import java.time.Instant;
+import org.hibernate.annotations.JdbcType;
+import org.hibernate.dialect.PostgreSQLEnumJdbcType;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+@Entity
+@EntityListeners(AuditingEntityListener.class)
+@DataSensitivity(SensitivityLevel.SENSITIVE)
+public class WaitingRoom extends GenericEntity<Long> {
+  @Id private Long id;
+
+  @MapsId
+  @OneToOne(optional = false)
+  private StiProtectionProcedure procedure;
+
+  private String info;
+
+  @JdbcType(PostgreSQLEnumJdbcType.class)
+  private WaitingStatus status;
+
+  @NotNull @LastModifiedDate private Instant modifiedAt;
+
+  @Override
+  public Long getId() {
+    return id;
+  }
+
+  public StiProtectionProcedure getProcedure() {
+    return procedure;
+  }
+
+  public void setProcedure(StiProtectionProcedure procedure) {
+    this.procedure = procedure;
+  }
+
+  public String getInfo() {
+    return info;
+  }
+
+  public void setInfo(String info) {
+    this.info = info;
+  }
+
+  public WaitingStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(WaitingStatus status) {
+    this.status = status;
+  }
+
+  public Instant getModifiedAt() {
+    return modifiedAt;
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java
new file mode 100644
index 000000000..df25fa2f0
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingRoomSpecification.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+import de.eshg.lib.procedure.domain.model.ProcedureStatus;
+import de.eshg.lib.procedure.domain.model.Procedure_;
+import de.eshg.stiprotection.api.waitingroom.WaitingRoomSortKey;
+import de.eshg.stiprotection.persistence.db.Person_;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure;
+import de.eshg.stiprotection.persistence.db.StiProtectionProcedure_;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Order;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.persistence.criteria.Root;
+import java.io.Serial;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+
+public class WaitingRoomSpecification implements Specification<StiProtectionProcedure> {
+
+  @Serial private static final long serialVersionUID = 1L;
+
+  private final WaitingRoomSortKey sortKey;
+  private final Sort.Direction sortDirection;
+
+  public WaitingRoomSpecification(WaitingRoomSortKey sortKey, Sort.Direction sortDirection) {
+    this.sortKey = sortKey;
+    this.sortDirection = sortDirection;
+  }
+
+  @Override
+  public Predicate toPredicate(
+      Root<StiProtectionProcedure> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
+    List<Predicate> conjunctions = defaultProcedureFilters(root, criteriaBuilder);
+
+    query.orderBy(getSortOrder(root, criteriaBuilder));
+    return criteriaBuilder.and(conjunctions.toArray(Predicate[]::new));
+  }
+
+  private List<Predicate> defaultProcedureFilters(
+      Root<StiProtectionProcedure> root, CriteriaBuilder criteriaBuilder) {
+    List<Predicate> defaultFilter = new ArrayList<>();
+
+    defaultFilter.add(
+        criteriaBuilder.equal(root.get(Procedure_.procedureStatus), ProcedureStatus.OPEN));
+    defaultFilter.add(
+        criteriaBuilder.isNotNull(
+            root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.status)));
+    defaultFilter.add(
+        criteriaBuilder.not(
+            root.get(StiProtectionProcedure_.waitingRoom)
+                .get(WaitingRoom_.status)
+                .in(WaitingStatus.DONE, WaitingStatus.CANCELLED)));
+
+    return defaultFilter;
+  }
+
+  private Order getSortOrder(Root<StiProtectionProcedure> root, CriteriaBuilder criteriaBuilder) {
+    Expression<?> sortOrder =
+        switch (sortKey) {
+          case ID -> root.get(StiProtectionProcedure_.id);
+          case YEAR_OF_BIRTH -> root.join(Procedure_.relatedPersons).get(Person_.YEAR_OF_BIRTH);
+          case GENDER -> root.join(Procedure_.relatedPersons).get(Person_.GENDER);
+          case STATUS -> root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.status);
+          case INFO -> root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.info);
+          case MODIFIED_AT ->
+              root.get(StiProtectionProcedure_.waitingRoom).get(WaitingRoom_.modifiedAt);
+        };
+    return switch (sortDirection) {
+      case ASC -> criteriaBuilder.asc(sortOrder);
+      case DESC -> criteriaBuilder.desc(sortOrder);
+    };
+  }
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java
new file mode 100644
index 000000000..ff1dda47d
--- /dev/null
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/db/waitingroom/WaitingStatus.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.stiprotection.persistence.db.waitingroom;
+
+public enum WaitingStatus {
+  WAITING_FOR_CONSULTATION,
+  WAITING_FOR_RESULTS_REVIEW,
+  WAITING_FOR_TESTS,
+  IN_CONSULTATION,
+  IN_TESTING,
+  CANCELLED,
+  DONE
+}
diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
index 32d03a1ed..6c30049c0 100644
--- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
+++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/StiProtectionTestHelperController.java
@@ -7,6 +7,7 @@ package de.eshg.stiprotection.testhelper;
 
 import de.eshg.auditlog.SharedAuditLogTestHelperApi;
 import de.eshg.lib.auditlog.AuditLogTestHelperService;
+import de.eshg.stiprotection.OverdueProceduresNotifier;
 import de.eshg.stiprotection.api.CreateProcedureResponse;
 import de.eshg.stiprotection.api.StiProtectionProcedurePopulationRequest;
 import de.eshg.stiprotection.api.StiProtectionProcedurePopulationResponse;
@@ -16,6 +17,7 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.population.ListWithTotalNumber;
 import jakarta.validation.Valid;
 import java.io.IOException;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.service.annotation.PostExchange;
@@ -27,15 +29,18 @@ public class StiProtectionTestHelperController extends TestHelperController
 
   private final AuditLogTestHelperService auditLogTestHelperService;
   private final StiProtectionPopulator populator;
+  private final OverdueProceduresNotifier overdueProceduresNotifier;
 
   public StiProtectionTestHelperController(
       StiProtectionTestHelperService testHelperService,
       AuditLogTestHelperService auditLogTestHelperService,
       StiProtectionPopulator populator,
-      EnvironmentConfig environmentConfig) {
+      EnvironmentConfig environmentConfig,
+      OverdueProceduresNotifier overdueProceduresNotifier) {
     super(testHelperService, environmentConfig);
     this.auditLogTestHelperService = auditLogTestHelperService;
     this.populator = populator;
+    this.overdueProceduresNotifier = overdueProceduresNotifier;
   }
 
   @PostExchange("/population/procedures")
@@ -47,6 +52,12 @@ public class StiProtectionTestHelperController extends TestHelperController
         result.entities(), result.totalNumberOfElements());
   }
 
+  @PostExchange("/notify/overdue-procedures")
+  public ResponseEntity<Void> notifyOfOverdueProcedures() {
+    overdueProceduresNotifier.runNow();
+    return ResponseEntity.ok().build();
+  }
+
   @Override
   public void clearAuditLogStorageDirectory() throws IOException {
     auditLogTestHelperService.clearAuditLogStorageDirectory();
diff --git a/backend/sti-protection/src/main/resources/application.properties b/backend/sti-protection/src/main/resources/application.properties
index e78d5f8a9..f49ffc799 100644
--- a/backend/sti-protection/src/main/resources/application.properties
+++ b/backend/sti-protection/src/main/resources/application.properties
@@ -19,3 +19,5 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SEX_WORK]=30m
 de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[RESULTS_REVIEW]=30m
 
 eshg.population.sti-protection-procedure=30
+eshg.sti-protection.overdue-procedures.cron=0 0 1 * * *
+eshg.sti-protection.overdue-procedures.overdue-days=180
diff --git a/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml b/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml
new file mode 100644
index 000000000..2ef4593b8
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0008_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml b/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml
new file mode 100644
index 000000000..c7bf5b8db
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0009_add_calendar_event_id.xml
@@ -0,0 +1,16 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729612558805-1">
+    <addColumn tableName="sti_protection_procedure">
+      <column name="calendar_event_id" type="uuid"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729612558805-2">
+    <addUniqueConstraint columnNames="calendar_event_id" constraintName="sti_protection_procedure_calendar_event_id_key" tableName="sti_protection_procedure"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml b/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml
new file mode 100644
index 000000000..1b1ac8a9f
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0010_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml b/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml
new file mode 100644
index 000000000..92cc4ff34
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0011_shedlock.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog
+  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+                      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
+  <changeSet author="GA-Lotse" id="1729865197316-1">
+    <createTable tableName="shedlock">
+      <column name="name" type="VARCHAR(64)">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_shedlock"/>
+      </column>
+      <column name="lock_until" type="TIMESTAMP WITHOUT TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="locked_at" type="TIMESTAMP WITHOUT TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="locked_by" type="VARCHAR(255)">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml b/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml
new file mode 100644
index 000000000..ecda42383
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0012_add_waiting_room.xml
@@ -0,0 +1,29 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730109739993-1">
+    <ext:createPostgresEnumType name="waitingstatus" values="CANCELLED, DONE, IN_CONSULTATION, IN_TESTING, WAITING_FOR_CONSULTATION, WAITING_FOR_RESULTS_REVIEW, WAITING_FOR_TESTS"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730109739993-2">
+    <createTable tableName="waiting_room">
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="info" type="TEXT"/>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="WAITINGSTATUS"/>
+      <column name="procedure_id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_waiting_room"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730109739993-3">
+    <addForeignKeyConstraint baseColumnNames="procedure_id" baseTableName="waiting_room" constraintName="fk_waiting_room_sti_protection_procedure" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="sti_protection_procedure" validate="true"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml b/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml
new file mode 100644
index 000000000..f915d3cc0
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0013_extend_medical_histories.xml
@@ -0,0 +1,33 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729606119790-1">
+    <addColumn tableName="medical_history">
+      <column name="amount_abortions" type="int4"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-2">
+    <addColumn tableName="medical_history">
+      <column name="amount_pregnancies" type="int4"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-3">
+    <addColumn tableName="medical_history">
+      <column name="known_operations" type="text"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-4">
+    <addColumn tableName="medical_history">
+      <column name="last_cancer_screening_duration" type="date"/>
+    </addColumn>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1729606119790-5">
+    <addColumn tableName="medical_history">
+      <column name="last_menstruation_duration" type="date"/>
+    </addColumn>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml b/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 000000000..609846864
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0014_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml b/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml
new file mode 100644
index 000000000..fc254c7a0
--- /dev/null
+++ b/backend/sti-protection/src/main/resources/migrations/0015_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/sti-protection/src/main/resources/migrations/changelog.xml b/backend/sti-protection/src/main/resources/migrations/changelog.xml
index f8416307a..926857eec 100644
--- a/backend/sti-protection/src/main/resources/migrations/changelog.xml
+++ b/backend/sti-protection/src/main/resources/migrations/changelog.xml
@@ -15,5 +15,13 @@
   <include file="migrations/0005_medical_histories.xml"/>
   <include file="migrations/0006_remove_key_document_type_enum.xml"/>
   <include file="migrations/0007_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0008_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0009_add_calendar_event_id.xml"/>
+  <include file="migrations/0010_cemetery_sequence.xml"/>
+  <include file="migrations/0011_shedlock.xml"/>
+  <include file="migrations/0012_add_waiting_room.xml"/>
+  <include file="migrations/0013_extend_medical_histories.xml"/>
+  <include file="migrations/0014_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0015_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
index 286627aa2..3f6270449 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DatabaseResetHelper.java
@@ -7,20 +7,25 @@ package de.eshg.testhelper;
 
 import com.zaxxer.hikari.HikariDataSource;
 import com.zaxxer.hikari.HikariPoolMXBean;
+import de.cronn.commons.lang.StreamUtil;
 import de.cronn.reflection.util.PropertyUtils;
 import de.eshg.testhelper.environment.EnvironmentConfig;
 import jakarta.persistence.EntityManagerFactory;
 import java.sql.Connection;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import javax.sql.DataSource;
 import liquibase.Scope;
 import liquibase.integration.spring.SpringLiquibase;
+import org.apache.commons.lang3.StringUtils;
 import org.hibernate.SessionFactory;
 import org.hibernate.engine.spi.SessionFactoryImplementor;
 import org.hibernate.generator.Generator;
@@ -32,15 +37,21 @@ import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
 import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.Assert;
+import org.springframework.web.util.UriComponentsBuilder;
 
 @Component
 @ConditionalOnBean(DataSource.class)
 @ConditionalOnTestHelperEnabled
 public class DatabaseResetHelper {
 
+  private static final String JDBC_URL_PREFIX = "jdbc:";
+  private static final Pattern CREATE_DATABASE_STATEMENT_PATTERN =
+      Pattern.compile("CREATE DATABASE ([a-z0-9_-]+) WITH .+");
+
   private final JdbcConnectionDetails jdbcConnectionDetails;
   private final DataSource dataSource;
   private final SpringLiquibase liquibase;
@@ -79,16 +90,18 @@ public class DatabaseResetHelper {
         isLiquibaseAvailableAndEnabled(),
         "Liquibase must be available and enabled in order to restore a database snapshot.");
 
-    dropAndRecreateThePublicSchema();
-
-    String filteredSql =
+    List<String> databaseSnapshotLines =
         Arrays.stream(databaseSnapshotSql.split("\\r?\\n"))
             .filter(line -> !line.isBlank())
             .filter(line -> !line.trim().startsWith("--"))
+            .toList();
+
+    dropAndRecreateTheDatabase(databaseSnapshotLines);
+
+    String filteredSql =
+        databaseSnapshotLines.stream()
             .filter(line -> !line.trim().startsWith("\\connect"))
-            .filter(line -> !line.trim().toLowerCase(Locale.ROOT).startsWith("create database"))
-            .filter(
-                line -> !line.trim().toLowerCase(Locale.ROOT).startsWith("create schema public"))
+            .filter(line -> !isCreateDatabaseStatement(line))
             .collect(Collectors.joining("\n"));
     jdbcTemplate.execute(filteredSql);
 
@@ -106,6 +119,10 @@ public class DatabaseResetHelper {
     runLiquibaseMigrations();
   }
 
+  private static boolean isCreateDatabaseStatement(String line) {
+    return CREATE_DATABASE_STATEMENT_PATTERN.matcher(line).matches();
+  }
+
   private void runLiquibaseMigrations() throws Exception {
     // This is a workaround to force Liquibase to clear all internal thread-local caches.
     // If we do not clear them, we observe that Liquibase sometimes incorrectly assumes that no
@@ -119,25 +136,47 @@ public class DatabaseResetHelper {
     return liquibaseProperties != null && liquibaseProperties.isEnabled() && liquibase != null;
   }
 
-  private void dropAndRecreateThePublicSchema() {
-    List<String> extensions =
-        jdbcTemplate.query(
-            """
-            SELECT extname FROM pg_extension
-            WHERE extnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
-            ORDER BY extname""",
-            (rs, rowNum) -> rs.getString(1));
-
-    jdbcTemplate.execute("drop schema public cascade");
-    jdbcTemplate.execute("create schema public");
-
-    if (!extensions.isEmpty()) {
-      String createExtensions =
-          extensions.stream()
-              .map("create extension %s schema public"::formatted)
-              .collect(Collectors.joining(";\n"));
-      jdbcTemplate.execute(createExtensions);
+  private void dropAndRecreateTheDatabase(List<String> databaseSnapshotLines) throws Exception {
+    String createDatabaseStatement =
+        databaseSnapshotLines.stream()
+            .filter(DatabaseResetHelper::isCreateDatabaseStatement)
+            .collect(StreamUtil.toSingleElement());
+
+    Matcher databaseNameMatcher =
+        CREATE_DATABASE_STATEMENT_PATTERN.matcher(createDatabaseStatement);
+    Assert.isTrue(
+        databaseNameMatcher.matches(),
+        () -> "Failed to parse database name from '%s'".formatted(createDatabaseStatement));
+
+    String database = databaseNameMatcher.group(1);
+
+    DataSource driverManagerDataSource =
+        new DriverManagerDataSource(
+            getJdbcUrlForPostgresDatabase(jdbcConnectionDetails.getJdbcUrl()),
+            jdbcConnectionDetails.getUsername(),
+            jdbcConnectionDetails.getPassword());
+    try (Connection connection = driverManagerDataSource.getConnection()) {
+      try (PreparedStatement preparedStatement =
+          connection.prepareStatement("drop database " + database + " with (force)")) {
+        preparedStatement.execute();
+      }
+      try (PreparedStatement preparedStatement =
+          connection.prepareStatement(createDatabaseStatement)) {
+        preparedStatement.execute();
+      }
     }
+
+    evictAllConnections();
+  }
+
+  private static String getJdbcUrlForPostgresDatabase(String fullJdbcUrl) {
+    String jdbcUriPart = StringUtils.substringAfter(fullJdbcUrl, JDBC_URL_PREFIX);
+    String replacedJdbcUriPart =
+        UriComponentsBuilder.fromUriString(jdbcUriPart)
+            .replacePath("/postgres")
+            .build()
+            .toUriString();
+    return JDBC_URL_PREFIX + replacedJdbcUriPart;
   }
 
   private void evictAllConnections() throws Exception {
@@ -208,8 +247,7 @@ public class DatabaseResetHelper {
   @Transactional
   public void resetAllSequences() {
     environmentConfig.assertIsNotProduction();
-    List<String> sequenceNamesToReset;
-    sequenceNamesToReset = getSequenceNamesThatNeedToBeReset();
+    List<String> sequenceNamesToReset = getSequenceNamesThatNeedToBeReset();
     for (String sequenceName : sequenceNamesToReset) {
       resetSequence(sequenceName);
     }
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
index 564efccbf..09a4246c3 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/BasePopulator.java
@@ -9,7 +9,6 @@ import de.eshg.testhelper.environment.EnvironmentConfig;
 import de.eshg.testhelper.security.AuthenticationFaker;
 import jakarta.annotation.PostConstruct;
 import java.time.Clock;
-import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -165,12 +164,6 @@ public abstract class BasePopulator<R> {
     return getClass().getSimpleName();
   }
 
-  protected LocalDate randomDate(Faker faker, int minYears, int maxYears) {
-    return LocalDate.now(clock)
-        .minusYears(faker.number().numberBetween(minYears, maxYears))
-        .minusDays(faker.number().numberBetween(1, 364));
-  }
-
   protected class UniqueValueProvider {
 
     public UniqueValueProvider() {}
diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
index d9c182548..158875b76 100644
--- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
+++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/population/PopulateWithAccessTokenHelper.java
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Component;
 @ConditionalOnBean(BaseTestHelperApi.class)
 public class PopulateWithAccessTokenHelper {
 
-  private final Logger log = LoggerFactory.getLogger(getClass());
+  private static final Logger log = LoggerFactory.getLogger(PopulateWithAccessTokenHelper.class);
 
   @Value("${eshg.keycloak.test-users-secret-override:}")
   private String testUserPassword;
diff --git a/backend/travel-medicine/openApi.yaml b/backend/travel-medicine/openApi.yaml
index 396674e57..c3f00095f 100644
--- a/backend/travel-medicine/openApi.yaml
+++ b/backend/travel-medicine/openApi.yaml
@@ -411,6 +411,47 @@ paths:
           description: OK
       tags:
       - Archiving
+  /citizen/auth/information-statements/{informationStatementId}:
+    get:
+      operationId: getCitizenInformationStatement
+      parameters:
+      - in: path
+        name: informationStatementId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/DocumentContent"
+          description: OK
+      summary: Gets information statement by id
+      tags:
+      - CitizenAuth
+    patch:
+      operationId: patchCitizenInformationStatement
+      parameters:
+      - in: path
+        name: informationStatementId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DocumentContent"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Updates information statement content
+      tags:
+      - CitizenAuth
   /citizen/auth/procedure-appointments:
     get:
       operationId: getProcedureAppointments
@@ -521,7 +562,7 @@ paths:
           content:
             application/json:
               schema:
-                $ref: "#/components/schemas/MedicalHistoryContent"
+                $ref: "#/components/schemas/DocumentContent"
           description: OK
       summary: Gets medical history for a procedure step appointment
       tags:
@@ -545,7 +586,7 @@ paths:
         content:
           application/json:
             schema:
-              $ref: "#/components/schemas/MedicalHistoryContent"
+              $ref: "#/components/schemas/DocumentContent"
         required: true
       responses:
         "200":
@@ -553,6 +594,19 @@ paths:
       summary: Updates medical history content
       tags:
       - CitizenAuth
+  /citizen/public/appointment-types:
+    get:
+      operationId: getAppointmentTypesForCitizen
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetAppointmentTypesResponse"
+          description: OK
+      summary: Gets all Appointment Types
+      tags:
+      - CitizenPublic
   /citizen/public/department-info:
     get:
       operationId: getDepartmentInfo
@@ -632,6 +686,19 @@ paths:
       summary: Get free appointments for an appointment type.
       tags:
       - CitizenPublic
+  /citizen/public/opening-hours:
+    get:
+      operationId: getOpeningHours
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetOpeningHoursResponse"
+          description: OK
+      summary: Get opening hours.
+      tags:
+      - CitizenPublic
   /citizen/public/vaccination-consultations:
     post:
       operationId: postVaccinationConsultationForCitizen
@@ -890,6 +957,21 @@ paths:
           description: OK
       tags:
       - File
+  /gdpr-validation-tasks:
+    post:
+      operationId: addGdprValidationTask
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/AddGdprValidationTaskRequest"
+        required: true
+      responses:
+        "200":
+          description: Add a GDPR validation task
+      summary: Add a GDPR validation task
+      tags:
+      - GdprValidationTask
   /inbox-procedures:
     get:
       description: |
@@ -2497,13 +2579,7 @@ paths:
       operationId: getAllProcedureAppointmentSummaries
       parameters:
       - in: query
-        name: dateRangeStart
-        required: true
-        schema:
-          type: string
-          format: date
-      - in: query
-        name: dateRangeEnd
+        name: date
         required: true
         schema:
           type: string
@@ -2546,6 +2622,44 @@ paths:
       summary: Add procedure step to vaccination consultation with given id
       tags:
       - VaccinationConsultation
+  /vaccination-consultations/{procedureId}:
+    delete:
+      operationId: abortDraftVaccinationConsultation
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          description: OK
+      summary: Aboard a draft vaccination consultation
+      tags:
+      - VaccinationConsultation
+  /vaccination-consultations/{procedureId}/accept-draft:
+    patch:
+      operationId: acceptDraftVaccinationConsultation
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/PatchAcceptDraftRequest"
+        required: true
+      responses:
+        "200":
+          description: OK
+      summary: Accept a draft vaccination consultation
+      tags:
+      - VaccinationConsultation
   /vaccination-consultations/{procedureId}/assignable-services:
     get:
       operationId: getAllAssignableServices
@@ -2649,6 +2763,25 @@ paths:
       tags:
       - VaccinationConsultation
   /vaccination-consultations/{procedureId}/information-statements:
+    get:
+      operationId: getInformationStatements
+      parameters:
+      - in: path
+        name: procedureId
+        required: true
+        schema:
+          type: string
+          format: uuid
+      responses:
+        "200":
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/GetInformationStatementsResponse"
+          description: OK
+      summary: Get information statements for this VaccinationConsultation.
+      tags:
+      - VaccinationConsultation
     post:
       operationId: postInformationStatements
       parameters:
@@ -3156,6 +3289,17 @@ components:
           minimum: 1
       required:
       - barrierId
+    AddGdprValidationTaskRequest:
+      type: object
+      properties:
+        procedureId:
+          type: string
+          format: uuid
+        type:
+          $ref: "#/components/schemas/GdprProcedureType"
+      required:
+      - procedureId
+      - type
     Address:
       type: object
       discriminator:
@@ -3961,12 +4105,8 @@ components:
           type: string
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
         note:
           type: string
-        subject:
-          type: string
       required:
       - manualProgressEntryType
     CreatedByUserType:
@@ -4064,6 +4204,99 @@ components:
       - id
       - name
       - visibleToCitizenPortal
+    DocumentAnamnesisQuestion:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+        subElementMultiSelect:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSubElementMultiSelect"
+        subElementText:
+          $ref: "#/components/schemas/DocumentSubElementText"
+      required:
+      - questionText
+      - subElementMultiSelect
+    DocumentConfirmation:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        confirmationTextField:
+          type: string
+      required:
+      - confirmationTextField
+    DocumentContent:
+      type: object
+      properties:
+        sections:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSection"
+          maxItems: 2147483647
+          minItems: 1
+      required:
+      - sections
+    DocumentSection:
+      type: object
+      properties:
+        sectionElements:
+          type: array
+          items:
+            $ref: "#/components/schemas/DocumentSectionElement"
+          maxItems: 2147483647
+          minItems: 1
+        sectionTitle:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - sectionElements
+    DocumentSectionElement:
+      type: object
+      properties:
+        anamnesisQuestion:
+          $ref: "#/components/schemas/DocumentAnamnesisQuestion"
+        confirmation:
+          $ref: "#/components/schemas/DocumentConfirmation"
+        textBlock:
+          $ref: "#/components/schemas/DocumentTextBlock"
+    DocumentSubElementMultiSelect:
+      type: object
+      properties:
+        answer:
+          type: boolean
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - questionText
+    DocumentSubElementText:
+      type: object
+      properties:
+        answer:
+          type: string
+          maxLength: 4000
+          minLength: 0
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - questionText
+    DocumentTextBlock:
+      type: object
+      properties:
+        textField:
+          type: string
+      required:
+      - textField
     DomesticAddress:
       type: object
       allOf:
@@ -4253,6 +4486,12 @@ components:
       - PNG
       - PDF
       - EML
+    GdprProcedureType:
+      type: string
+      description: A list of types of GDPR procedures.
+      enum:
+      - RIGHT_OF_ACCESS
+      - RIGHT_TO_ERASURE
     Gender:
       type: string
       description: The list of genders as specified in the German Personenstandsgesetz.
@@ -4379,8 +4618,6 @@ components:
         bookingsRemaining:
           type: integer
           format: int32
-        citizenHasAnswered:
-          type: boolean
         dateOfBirth:
           type: string
           format: date
@@ -4388,20 +4625,27 @@ components:
           type: string
         hasAccomplishedService:
           type: boolean
+        informationStatementSummaries:
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatementSummary"
         isMedicalHistoryCompletelyAnswered:
           type: boolean
         lastName:
           type: string
+        medicalHistoryCitizenHasAnswered:
+          type: boolean
         summaryDto:
           $ref: "#/components/schemas/AppointmentSummary"
       required:
       - bookingsRemaining
-      - citizenHasAnswered
       - dateOfBirth
       - firstName
       - hasAccomplishedService
+      - informationStatementSummaries
       - isMedicalHistoryCompletelyAnswered
       - lastName
+      - medicalHistoryCitizenHasAnswered
       - summaryDto
     GetAppointmentOverviewResponse:
       type: object
@@ -4662,6 +4906,19 @@ components:
             $ref: "#/components/schemas/InformationStatementTemplate"
       required:
       - informationStatementTemplates
+    GetInformationStatementsResponse:
+      type: object
+      properties:
+        informationStatements:
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatement"
+        procedureId:
+          type: string
+          format: uuid
+      required:
+      - informationStatements
+      - procedureId
     GetInventoryVaccinesWithoutRmbiVaccineResponse:
       type: object
       properties:
@@ -4710,6 +4967,20 @@ components:
             - $ref: "#/components/schemas/ImageMetaDataHistory"
             - $ref: "#/components/schemas/MailMetaDataHistory"
             - $ref: "#/components/schemas/PdfMetaDataHistory"
+    GetOpeningHoursResponse:
+      type: object
+      properties:
+        de:
+          type: array
+          items:
+            type: string
+        en:
+          type: array
+          items:
+            type: string
+      required:
+      - de
+      - en
     GetOtherServiceTemplatesResponse:
       type: object
       properties:
@@ -4832,7 +5103,9 @@ components:
         relatedKeyDocumentProgressEntries:
           type: array
           items:
-            $ref: "#/components/schemas/ManualProgressEntry"
+            oneOf:
+            - $ref: "#/components/schemas/ManualProgressEntry"
+            - $ref: "#/components/schemas/SystemProgressEntry"
       required:
       - progressEntry
       - relatedKeyDocumentProgressEntries
@@ -4973,10 +5246,6 @@ components:
       properties:
         createdByUserType:
           $ref: "#/components/schemas/CreatedByUserType"
-        informationStatements:
-          type: array
-          items:
-            $ref: "#/components/schemas/InformationStatement"
         initialAppointment:
           $ref: "#/components/schemas/AppointmentSummary"
         patient:
@@ -4996,7 +5265,6 @@ components:
           $ref: "#/components/schemas/TravelInformation"
       required:
       - createdByUserType
-      - informationStatements
       - initialAppointment
       - patient
       - personSync
@@ -5154,9 +5422,7 @@ components:
         citizenHasAnswered:
           type: boolean
         content:
-          type: string
-          maxLength: 4000
-          minLength: 0
+          $ref: "#/components/schemas/DocumentContent"
         createdAt:
           type: string
           format: date-time
@@ -5177,6 +5443,35 @@ components:
       - id
       - modifiedAt
       - title
+    InformationStatementPopulation:
+      type: object
+      properties:
+        answered:
+          type: boolean
+        key:
+          type: string
+        templateId:
+          type: string
+          format: uuid
+      required:
+      - key
+      - templateId
+    InformationStatementSummary:
+      type: object
+      properties:
+        citizenHasAnswered:
+          type: boolean
+        id:
+          type: string
+          format: uuid
+        title:
+          type: string
+          maxLength: 200
+          minLength: 0
+      required:
+      - citizenHasAnswered
+      - id
+      - title
     InformationStatementTemplate:
       type: object
       properties:
@@ -5284,6 +5579,20 @@ components:
       required:
       - id
       - name
+    KeyDocumentAwareProgressEntry:
+      type: object
+      discriminator:
+        propertyName: '@type'
+      properties:
+        '@type':
+          type: string
+        keyDocumentType:
+          type: string
+        keyDocumentVersion:
+          type: integer
+          format: int32
+      required:
+      - '@type'
     Location:
       type: object
       description: Location defined by latitude and longitude.
@@ -5348,13 +5657,19 @@ components:
             type: string
           mailTo:
             type: string
+          messageText:
+            type: string
           sentDate:
             type: string
             format: date-time
+          subject:
+            type: string
       required:
       - mailFrom
       - mailTo
+      - messageText
       - sentDate
+      - subject
     MailMetaDataHistory:
       type: object
       allOf:
@@ -5388,13 +5703,10 @@ components:
             type: boolean
           manualProgressEntryType:
             $ref: "#/components/schemas/ManualProgressEntryType"
-          messageText:
-            type: string
           note:
             type: string
-          subject:
-            type: string
       - $ref: "#/components/schemas/ApprovalRequestEntity"
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - createdBy
@@ -5444,7 +5756,7 @@ components:
         isCompletelyAnswered:
           type: boolean
         medicalHistoryContent:
-          $ref: "#/components/schemas/MedicalHistoryContent"
+          $ref: "#/components/schemas/DocumentContent"
         modifiedAt:
           type: string
           format: date-time
@@ -5463,86 +5775,6 @@ components:
       - medicalHistoryContent
       - modifiedAt
       - procedureStepId
-    MedicalHistoryContent:
-      type: object
-      properties:
-        sections:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySection"
-          maxItems: 2147483647
-          minItems: 1
-      required:
-      - sections
-    MedicalHistorySection:
-      type: object
-      properties:
-        sectionElements:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySectionElement"
-          maxItems: 2147483647
-          minItems: 1
-        sectionTitle:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - sectionElements
-    MedicalHistorySectionElement:
-      type: object
-      properties:
-        elementData:
-          $ref: "#/components/schemas/MedicalHistorySectionElementData"
-        elementType:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - elementData
-      - elementType
-    MedicalHistorySectionElementData:
-      type: object
-      properties:
-        answer:
-          type: boolean
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-        subElementMultiSelect:
-          type: array
-          items:
-            $ref: "#/components/schemas/MedicalHistorySubElementMultiSelect"
-        subElementText:
-          $ref: "#/components/schemas/MedicalHistorySubElementText"
-      required:
-      - questionText
-      - subElementMultiSelect
-    MedicalHistorySubElementMultiSelect:
-      type: object
-      properties:
-        answer:
-          type: boolean
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - questionText
-    MedicalHistorySubElementText:
-      type: object
-      properties:
-        answer:
-          type: string
-          maxLength: 4000
-          minLength: 0
-        questionText:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - questionText
     MedicalHistoryTemplate:
       type: object
       properties:
@@ -5646,6 +5878,12 @@ components:
       - fee
       - id
       - modifiedAt
+    PatchAcceptDraftRequest:
+      type: object
+      properties:
+        referencePersonId:
+          type: string
+          format: uuid
     PatchAppointmentRequest:
       type: object
       properties:
@@ -5677,20 +5915,14 @@ components:
       properties:
         manualProgressEntryType:
           $ref: "#/components/schemas/ManualProgressEntryType"
-        messageText:
-          type: string
-          nullable: true
         note:
           type: string
           nullable: true
-        subject:
-          type: string
-          nullable: true
     PatchMedicalHistoryRequest:
       type: object
       properties:
         medicalHistoryContent:
-          $ref: "#/components/schemas/MedicalHistoryContent"
+          $ref: "#/components/schemas/DocumentContent"
         note:
           type: string
           maxLength: 4000
@@ -6121,7 +6353,9 @@ components:
           items:
             type: string
         informationStatements:
-          $ref: "#/components/schemas/PostInformationStatementsRequest"
+          type: array
+          items:
+            $ref: "#/components/schemas/InformationStatementPopulation"
         initialStep:
           $ref: "#/components/schemas/InitialStepPopulation"
         otherServices:
@@ -6134,7 +6368,7 @@ components:
           type: array
           items:
             $ref: "#/components/schemas/ProcedureStepPopulation"
-        statusChange:
+        targetState:
           $ref: "#/components/schemas/ProcedureStatus"
         vaccinations:
           type: array
@@ -6145,6 +6379,11 @@ components:
       properties:
         credentials:
           $ref: "#/components/schemas/CitizenPortalCredentials"
+        informationStatementsCreated:
+          type: object
+          additionalProperties:
+            type: string
+            format: uuid
         procedureId:
           type: string
           format: uuid
@@ -6159,6 +6398,7 @@ components:
             type: string
             format: uuid
       required:
+      - informationStatementsCreated
       - procedureId
       - procedureStepsCreated
       - servicesCreated
@@ -6754,6 +6994,11 @@ components:
         properties:
           changeDescription:
             type: string
+          keyDocumentType:
+            type: string
+          keyDocumentVersion:
+            type: integer
+            format: int32
           systemProgressEntryType:
             type: string
           triggerType:
@@ -6765,6 +7010,7 @@ components:
             type: string
           triggeredByUserLastName:
             type: string
+      - $ref: "#/components/schemas/KeyDocumentAwareProgressEntry"
       required:
       - createdAt
       - modifiedAt
@@ -6909,6 +7155,29 @@ components:
       - TRAVEL_MEDICINE
       - MEASLES_PROTECTION
       - STI_PROTECTION
+    TemplateAnamnesisQuestion:
+      type: object
+      properties:
+        questionText:
+          type: string
+          maxLength: 200
+          minLength: 0
+        subElementMultiSelect:
+          type: array
+          items:
+            $ref: "#/components/schemas/TemplateSubElementMultiSelect"
+        subElementText:
+          $ref: "#/components/schemas/TemplateSubElementText"
+      required:
+      - questionText
+      - subElementMultiSelect
+    TemplateConfirmation:
+      type: object
+      properties:
+        confirmationTextField:
+          type: string
+      required:
+      - confirmationTextField
     TemplateContent:
       type: object
       properties:
@@ -6938,57 +7207,37 @@ components:
     TemplateSectionElement:
       type: object
       properties:
-        elementData:
-          $ref: "#/components/schemas/TemplateSectionElementData"
-        elementType:
-          type: string
-          maxLength: 200
-          minLength: 0
-      required:
-      - elementData
-      - elementType
-    TemplateSectionElementData:
+        anamnesisQuestion:
+          $ref: "#/components/schemas/TemplateAnamnesisQuestion"
+        confirmation:
+          $ref: "#/components/schemas/TemplateConfirmation"
+        textBlock:
+          $ref: "#/components/schemas/TemplateTextBlock"
+    TemplateSubElementMultiSelect:
       type: object
       properties:
-        answer:
-          type: boolean
         questionText:
           type: string
           maxLength: 200
           minLength: 0
-        subElementMultiSelect:
-          type: array
-          items:
-            $ref: "#/components/schemas/TemplateSubElementMultiSelect"
-        subElementText:
-          $ref: "#/components/schemas/TemplateSubElementText"
       required:
       - questionText
-      - subElementMultiSelect
-    TemplateSubElementMultiSelect:
+    TemplateSubElementText:
       type: object
       properties:
-        answer:
-          type: boolean
         questionText:
           type: string
           maxLength: 200
           minLength: 0
       required:
       - questionText
-    TemplateSubElementText:
+    TemplateTextBlock:
       type: object
       properties:
-        answer:
-          type: string
-          maxLength: 4000
-          minLength: 0
-        questionText:
+        textField:
           type: string
-          maxLength: 200
-          minLength: 0
       required:
-      - questionText
+      - textField
     TestHelperClockSetRequest:
       type: object
       properties:
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
index 7139420a7..71c205d53 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/TravelMedicineApplication.java
@@ -8,6 +8,7 @@ package de.eshg.travelmedicine;
 import de.eshg.lib.common.BusinessModule;
 import de.eshg.rest.service.security.config.TravelMedicinePublicSecurityConfig;
 import de.eshg.travelmedicine.citizenpublic.DepartmentInfoProperties;
+import de.eshg.travelmedicine.citizenpublic.OpeningHoursProperties;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.notification.NotificationProperties;
 import org.springframework.boot.SpringApplication;
@@ -21,6 +22,7 @@ import org.springframework.context.annotation.Import;
 @EnableConfigurationProperties({
   TravelMedicineFeatureToggle.class,
   DepartmentInfoProperties.class,
+  OpeningHoursProperties.class,
   NotificationProperties.class
 })
 public class TravelMedicineApplication {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
index 0b086ede3..cb7fb0345 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/certificate/CertificateService.java
@@ -12,7 +12,6 @@ import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory;
 import de.eshg.lib.procedure.domain.model.File;
 import de.eshg.lib.procedure.domain.model.Pdf;
 import de.eshg.lib.procedure.domain.model.PdfMetaData;
-import de.eshg.lib.procedure.domain.model.ProcedureFileType;
 import de.eshg.lib.procedure.domain.model.ProgressEntry;
 import de.eshg.lib.procedure.domain.model.SystemProgressEntry;
 import de.eshg.lib.procedure.domain.model.TriggerType;
@@ -211,14 +210,13 @@ public class CertificateService {
       ProcedureStep procedureStep, List<UUID> serviceIds) {
     List<VcService> services = serviceRepository.findAllByIdOrderById(serviceIds);
 
-    UUID patientId =
-        procedureStep.getVaccinationConsultation().getPatientIdsFromCentralFile().getFirst();
-    PatientDto patient = personClient.getPersonFromCentralFile(patientId).patient();
-    VaccinationConsultation consultation = procedureStep.getVaccinationConsultation();
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    PatientDto patient = personClient.getPatientFromCentralFile(patientId);
 
     HealthInsuranceCertificatePdfParameters pdfParameters =
         collectHealthInsuranceCertificatePdfData(
-            departmentInfoService, consultation, patient, services);
+            departmentInfoService, vaccinationConsultation, patient, services);
 
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     documentGenerator.createPdfFromTemplate(certificateTemplateResource, pdfParameters, baos);
@@ -228,8 +226,7 @@ public class CertificateService {
     pdfMetaData.setCreatedDate(Instant.now(clock));
     pdfMetaData.setDescription(pdfParameters.getTitle());
 
-    return FileFactory.createPdfWithMetaData(
-        PDF_FILENAME, ProcedureFileType.PDF, bytes, pdfMetaData, false);
+    return FileFactory.createPdfWithMetaData(PDF_FILENAME, bytes, pdfMetaData);
   }
 
   private UUID generateProgressEntry(ProcedureStep procedureStep, List<UUID> serviceIds) {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
index 09090c747..5edd2cce3 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenauth/CitizenAuthController.java
@@ -7,9 +7,11 @@ package de.eshg.travelmedicine.citizenauth;
 
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
 import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentDetailsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetCitizenAppointmentOverviewResponse;
@@ -42,15 +44,23 @@ public class CitizenAuthController {
   public static final String VACCINATION_CONSULTATION_URL = "/vaccination-consultations";
   public static final String PROCEDURE_STEP_URL = "/procedure-steps";
   public static final String MEDICAL_HISTORY_URL = "/medical-history";
+  public static final String INFORMATION_STATEMENT_URL = "/information-statements";
 
   private final TravelMedicineFeatureToggle featureToggle;
   private final VaccinationConsultationService vaccinationConsultationService;
+  private final InformationStatementService informationStatementService;
+
+  private final MedicalHistoryService medicalHistoryService;
 
   public CitizenAuthController(
       TravelMedicineFeatureToggle featureToggle,
-      VaccinationConsultationService vaccinationConsultationService) {
+      VaccinationConsultationService vaccinationConsultationService,
+      InformationStatementService informationStatementService,
+      MedicalHistoryService medicalHistoryService) {
     this.featureToggle = featureToggle;
     this.vaccinationConsultationService = vaccinationConsultationService;
+    this.informationStatementService = informationStatementService;
+    this.medicalHistoryService = medicalHistoryService;
   }
 
   // Test
@@ -88,7 +98,7 @@ public class CitizenAuthController {
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.cancelAppointment(
+    vaccinationConsultationService.cancelAppointmentByCitizen(
         getCitizenUserId(principal), procedureId, procedureStepId);
   }
 
@@ -100,12 +110,12 @@ public class CitizenAuthController {
           + MEDICAL_HISTORY_URL)
   @Operation(summary = "Gets medical history for a procedure step appointment")
   @Transactional(readOnly = true)
-  public MedicalHistoryContentDto getMedicalHistory(
+  public DocumentContentDto getMedicalHistory(
       @AuthenticationPrincipal Jwt principal,
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    return vaccinationConsultationService.getMedicalHistory(
+    return medicalHistoryService.getMedicalHistoryForCitizenPortal(
         getCitizenUserId(principal), procedureId, procedureStepId);
   }
 
@@ -121,9 +131,9 @@ public class CitizenAuthController {
       @AuthenticationPrincipal Jwt principal,
       @PathVariable("procedureId") UUID procedureId,
       @PathVariable("procedureStepId") UUID procedureStepId,
-      @RequestBody @Valid MedicalHistoryContentDto patchMedicalHistoryContent) {
+      @RequestBody @Valid DocumentContentDto patchMedicalHistoryContent) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.patchMedicalHistory(
+    medicalHistoryService.patchMedicalHistoryForCitizenPortal(
         getCitizenUserId(principal), procedureId, procedureStepId, patchMedicalHistoryContent);
   }
 
@@ -141,10 +151,35 @@ public class CitizenAuthController {
       @PathVariable("procedureStepId") UUID procedureStepId,
       @RequestBody @Valid AppointmentDto appointmentDto) {
     featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
-    vaccinationConsultationService.bookCitizenAppointment(
+    vaccinationConsultationService.bookCitizenAppointmentByCitizen(
         getCitizenUserId(principal), procedureId, procedureStepId, appointmentDto);
   }
 
+  @GetMapping(INFORMATION_STATEMENT_URL + "/{informationStatementId}")
+  @Operation(summary = "Gets information statement by id")
+  @Transactional(readOnly = true)
+  public DocumentContentDto getCitizenInformationStatement(
+      @AuthenticationPrincipal Jwt principal,
+      @PathVariable("informationStatementId") UUID informationStatementId) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    return informationStatementService.getInformationStatementForCitizenPortal(
+        getCitizenUserId(principal), informationStatementId);
+  }
+
+  @PatchMapping(INFORMATION_STATEMENT_URL + "/{informationStatementId}")
+  @Operation(summary = "Updates information statement content")
+  @Transactional()
+  public void patchCitizenInformationStatement(
+      @AuthenticationPrincipal Jwt principal,
+      @PathVariable("informationStatementId") UUID informationStatementId,
+      @RequestBody @Valid DocumentContentDto patchInformationStatementContent) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    informationStatementService.patchInformationStatementForCitizenPortal(
+        getCitizenUserId(principal), informationStatementId, patchInformationStatementContent);
+  }
+
   private UUID getCitizenUserId(Jwt principal) {
     return UUID.fromString(principal.getSubject());
   }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
index 57cb43e76..8b8c7979e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/CitizenPublicController.java
@@ -7,12 +7,15 @@ package de.eshg.travelmedicine.citizenpublic;
 
 import de.eshg.base.department.GetDepartmentInfoResponse;
 import de.eshg.lib.appointmentblock.AppointmentBlockService;
+import de.eshg.lib.appointmentblock.AppointmentTypeService;
 import de.eshg.lib.appointmentblock.MappingUtil;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
 import de.eshg.lib.appointmentblock.api.AppointmentTypeDto;
+import de.eshg.lib.appointmentblock.api.GetAppointmentTypesResponse;
 import de.eshg.lib.appointmentblock.api.GetFreeAppointmentsResponse;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.rest.service.security.config.BaseUrls;
+import de.eshg.travelmedicine.citizenpublic.api.GetOpeningHoursResponse;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
 import de.eshg.travelmedicine.disease.DiseaseService;
 import de.eshg.travelmedicine.disease.api.GetDiseasesResponse;
@@ -23,6 +26,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 import org.springframework.beans.factory.annotation.Value;
@@ -50,27 +54,33 @@ public class CitizenPublicController {
 
   private final DiseaseService diseaseService;
   private final AppointmentBlockService appointmentBlockService;
+  private final AppointmentTypeService appointmentTypeService;
   private final VaccinationConsultationService vaccinationConsultationService;
   private final TravelMedicineFeatureToggle featureToggle;
   private final DepartmentInfoService departmentInfoService;
   private final Resource privacyNotice;
   private final Resource privacyPolicy;
+  private final OpeningHoursProperties openingHoursProperties;
 
   public CitizenPublicController(
       DiseaseService diseaseService,
       AppointmentBlockService appointmentBlockService,
+      AppointmentTypeService appointmentTypeService,
       VaccinationConsultationService vaccinationConsultationService,
       TravelMedicineFeatureToggle featureToggle,
       DepartmentInfoService departmentInfoService,
       @Value("${de.eshg.travel-medicine.privacy-notice-location}") Resource privacyNotice,
-      @Value("${de.eshg.travel-medicine.privacy-policy-location}") Resource privacyPolicy) {
+      @Value("${de.eshg.travel-medicine.privacy-policy-location}") Resource privacyPolicy,
+      OpeningHoursProperties openingHoursProperties) {
     this.diseaseService = diseaseService;
     this.appointmentBlockService = appointmentBlockService;
+    this.appointmentTypeService = appointmentTypeService;
     this.vaccinationConsultationService = vaccinationConsultationService;
     this.featureToggle = featureToggle;
     this.departmentInfoService = departmentInfoService;
     this.privacyNotice = privacyNotice;
     this.privacyPolicy = privacyPolicy;
+    this.openingHoursProperties = openingHoursProperties;
   }
 
   @GetMapping("/diseases")
@@ -94,6 +104,14 @@ public class CitizenPublicController {
     return new GetFreeAppointmentsResponse(appointments);
   }
 
+  @Operation(summary = "Gets all Appointment Types")
+  @GetMapping("/appointment-types")
+  @Transactional(readOnly = true)
+  public GetAppointmentTypesResponse getAppointmentTypesForCitizen() {
+    featureToggle.assertNewFeatureIsEnabled(TravelMedicineFeature.CITIZEN_PORTAL_PROCEDURE);
+    return appointmentTypeService.getAppointmentTypes();
+  }
+
   @PostMapping("/vaccination-consultations")
   @Operation(summary = "Save a new vaccination consultation")
   @Transactional
@@ -112,6 +130,18 @@ public class CitizenPublicController {
     return departmentInfoService.getDepartmentInfo();
   }
 
+  @Operation(summary = "Get opening hours.")
+  @GetMapping("/opening-hours")
+  @Transactional(readOnly = true)
+  public GetOpeningHoursResponse getOpeningHours() {
+
+    return new GetOpeningHoursResponse(
+        openingHoursProperties.de() == null ? Collections.emptyList() : openingHoursProperties.de(),
+        openingHoursProperties.en() == null
+            ? Collections.emptyList()
+            : openingHoursProperties.en());
+  }
+
   @GetMapping(path = "/documents/privacy-notice")
   @Operation(summary = "Get the privacy-notice document.")
   @Transactional(readOnly = true)
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java
new file mode 100644
index 000000000..b18c8685d
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/OpeningHoursProperties.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.citizenpublic;
+
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "de.eshg.travel-medicine.opening-hours")
+public record OpeningHoursProperties(List<String> de, List<String> en) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java
new file mode 100644
index 000000000..9a768d595
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/citizenpublic/api/GetOpeningHoursResponse.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.citizenpublic.api;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+
+public record GetOpeningHoursResponse(@NotNull List<String> de, @NotNull List<String> en) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java
new file mode 100644
index 000000000..8bc260447
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/DocumentDtoHelper.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document;
+
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import java.util.List;
+
+public class DocumentDtoHelper {
+
+  private DocumentDtoHelper() {
+    throw new IllegalStateException("Utility class");
+  }
+
+  public static boolean isDocumentContentCompletelyAnswered(
+      DocumentContentDto medicalHistoryContentDto) {
+    for (DocumentSectionDto section : medicalHistoryContentDto.sections()) {
+      if (!isSectionCompletelyAnswered(section)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private static boolean isSectionCompletelyAnswered(DocumentSectionDto section) {
+    for (DocumentSectionElementDto sectionElement : section.sectionElements()) {
+      DocumentAnamnesisQuestionDto anamnesisQuestionDto = sectionElement.anamnesisQuestion();
+      if (sectionElement.isAnamnesisQuestionSolely()
+          && !isAnamnesisQuestionCompletelyAnswered(anamnesisQuestionDto)) {
+        return false;
+      }
+
+      // textBlocks can never be not completely answered
+
+      DocumentConfirmationDto confirmationDto = sectionElement.confirmation();
+      if (sectionElement.isConfirmationSolely()
+          && !isConfirmationCompletelyAnswered(confirmationDto)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  private static boolean isAnamnesisQuestionCompletelyAnswered(DocumentAnamnesisQuestionDto data) {
+    // simple question
+    if (data.subElementMultiSelect().isEmpty()
+        && data.subElementText() == null
+        && (!isCompletelyAnsweredSimpleQuestion(data))) {
+      return false;
+    }
+
+    // free text question
+    if (data.subElementMultiSelect().isEmpty()
+        && data.subElementText() != null
+        && (!isCompletelyAnsweredFreeTextQuestion(data))) {
+      return false;
+    }
+
+    // multiple choice question without free text question
+    if (!data.subElementMultiSelect().isEmpty()
+        && data.subElementText() == null
+        && (!isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(data))) {
+      return false;
+    }
+
+    // multiple choice question with free text question
+    if (!data.subElementMultiSelect().isEmpty()
+        && data.subElementText() != null
+        && (!isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(data))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  private static boolean isCompletelyAnsweredSimpleQuestion(DocumentAnamnesisQuestionDto data) {
+    // simple question should be answered with yes or no
+    return data.answer() != null;
+  }
+
+  private static boolean isCompletelyAnsweredFreeTextQuestion(DocumentAnamnesisQuestionDto data) {
+    // free text / open question should be answered with no or have (when answering the question
+    // with yes) a non-blank free text field
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && isSubelementTextCompletelyAnswered(data.subElementText()));
+  }
+
+  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(
+      DocumentAnamnesisQuestionDto data) {
+    // multiple choice question should be answered with no or have (when answering the question with
+    // yes) a non-blank free text field and/or at least one marked multiple choice answer
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && isMultiSelectCompletelyAnswered(data.subElementMultiSelect()));
+  }
+
+  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(
+      DocumentAnamnesisQuestionDto data) {
+    // multiple choice question should be answered with no or have (when answering the question with
+    // yes) a non-blank free text field and/or at least one marked multiple choice answer
+    return Boolean.FALSE.equals(data.answer())
+        || (Boolean.TRUE.equals(data.answer())
+            && (isSubelementTextCompletelyAnswered(data.subElementText())
+                || isMultiSelectCompletelyAnswered(data.subElementMultiSelect())));
+  }
+
+  private static boolean isMultiSelectCompletelyAnswered(
+      List<DocumentSubElementMultiSelectDto> subElementMultiSelects) {
+    return subElementMultiSelects.stream()
+        .anyMatch(multiSelect -> Boolean.TRUE.equals(multiSelect.answer()));
+  }
+
+  private static boolean isSubelementTextCompletelyAnswered(DocumentSubElementTextDto text) {
+    if (text == null) {
+      return false;
+    }
+    return text.answer() != null && !text.answer().isBlank();
+  }
+
+  private static boolean isConfirmationCompletelyAnswered(DocumentConfirmationDto confirmationDto) {
+    if (confirmationDto == null) {
+      return false;
+    }
+    return confirmationDto.answer() != null && Boolean.TRUE.equals(confirmationDto.answer());
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java
new file mode 100644
index 000000000..0156630ca
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/TemplateToDocumentMapper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import de.eshg.travelmedicine.document.api.DocumentTextBlockDto;
+import de.eshg.travelmedicine.template.api.TemplateAnamnesisQuestionDto;
+import de.eshg.travelmedicine.template.api.TemplateConfirmationDto;
+import de.eshg.travelmedicine.template.api.TemplateContentDto;
+import de.eshg.travelmedicine.template.api.TemplateSectionDto;
+import de.eshg.travelmedicine.template.api.TemplateSectionElementDto;
+import de.eshg.travelmedicine.template.api.TemplateSubElementMultiSelectDto;
+import de.eshg.travelmedicine.template.api.TemplateSubElementTextDto;
+import de.eshg.travelmedicine.template.api.TemplateTextBlockDto;
+import java.util.List;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TemplateToDocumentMapper {
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public DocumentContentDto transferContent(TemplateContentDto templateContent) {
+    if (templateContent == null) return null;
+    List<DocumentSectionDto> sections =
+        templateContent.templateSections().stream().map(this::mapSection).toList();
+
+    return new DocumentContentDto(sections);
+  }
+
+  public String transferContent(String templateContent) {
+    TemplateContentDto templateContentDto = deserializeTemplateContent(templateContent);
+    DocumentContentDto documentContentDto = transferContent(templateContentDto);
+    return serializeDocumentContent(documentContentDto);
+  }
+
+  private DocumentSectionDto mapSection(TemplateSectionDto templateSection) {
+    if (templateSection == null) return null;
+    List<DocumentSectionElementDto> sectionElements =
+        templateSection.templateSectionElements().stream().map(this::mapSectionElement).toList();
+    return new DocumentSectionDto(templateSection.sectionTitle(), sectionElements);
+  }
+
+  private DocumentSectionElementDto mapSectionElement(
+      TemplateSectionElementDto templateSectionElement) {
+    if (templateSectionElement == null) return null;
+
+    DocumentAnamnesisQuestionDto documentAnamnesisQuestionDto =
+        this.mapAnamnesisQuestion(templateSectionElement.templateAnamnesisQuestionDto());
+    DocumentTextBlockDto documentTextBlockDto =
+        this.mapTextBlock(templateSectionElement.templateTextBlockDto());
+    DocumentConfirmationDto documentConfirmationDto =
+        this.mapConfirmation(templateSectionElement.templateConfirmationDto());
+
+    return new DocumentSectionElementDto(
+        documentAnamnesisQuestionDto, documentTextBlockDto, documentConfirmationDto);
+  }
+
+  private DocumentAnamnesisQuestionDto mapAnamnesisQuestion(
+      TemplateAnamnesisQuestionDto templateAnamnesisQuestion) {
+    if (templateAnamnesisQuestion == null) return null;
+
+    List<DocumentSubElementMultiSelectDto> documentSubElementMultiSelects =
+        templateAnamnesisQuestion.templateSubElementMultiSelects().stream()
+            .map(this::mapSubElementMultiSelect)
+            .toList();
+    DocumentSubElementTextDto documentSubElementText =
+        mapSubElementText(templateAnamnesisQuestion.templateSubElementText());
+
+    return new DocumentAnamnesisQuestionDto(
+        templateAnamnesisQuestion.questionText(),
+        defaultInitialBooleanAnswer(),
+        documentSubElementMultiSelects,
+        documentSubElementText);
+  }
+
+  private DocumentTextBlockDto mapTextBlock(TemplateTextBlockDto templateTextBlock) {
+    if (templateTextBlock == null) return null;
+    return new DocumentTextBlockDto(templateTextBlock.textField());
+  }
+
+  private DocumentConfirmationDto mapConfirmation(TemplateConfirmationDto templateConfirmation) {
+    if (templateConfirmation == null) return null;
+    return new DocumentConfirmationDto(
+        templateConfirmation.confirmationTextField(), defaultInitialBooleanAnswer());
+  }
+
+  private DocumentSubElementMultiSelectDto mapSubElementMultiSelect(
+      TemplateSubElementMultiSelectDto templateSubElementMultiSelect) {
+    if (templateSubElementMultiSelect == null) return null;
+    return new DocumentSubElementMultiSelectDto(
+        templateSubElementMultiSelect.questionText(), defaultInitialBooleanAnswer());
+  }
+
+  private DocumentSubElementTextDto mapSubElementText(
+      TemplateSubElementTextDto templateSubElementText) {
+    if (templateSubElementText == null) return null;
+    return new DocumentSubElementTextDto(
+        templateSubElementText.questionText(), defaultInitialStringAnswer());
+  }
+
+  private Boolean defaultInitialBooleanAnswer() {
+    return null;
+  }
+
+  private String defaultInitialStringAnswer() {
+    return null;
+  }
+
+  private String serializeDocumentContent(DocumentContentDto documentContentDto) {
+    try {
+      return objectMapper.writeValueAsString(documentContentDto);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Document Content structure is corrupt");
+    }
+  }
+
+  private TemplateContentDto deserializeTemplateContent(String templateContent) {
+    try {
+      return objectMapper.readValue(templateContent, TemplateContentDto.class);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Template content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java
new file mode 100644
index 000000000..6fb6f0651
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentAnamnesisQuestionDto.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentAnamnesisQuestion")
+public record DocumentAnamnesisQuestionDto(
+    @NotNull @Size(max = 200) String questionText,
+    Boolean answer,
+    @NotNull @Valid List<DocumentSubElementMultiSelectDto> subElementMultiSelect,
+    @Valid DocumentSubElementTextDto subElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java
new file mode 100644
index 000000000..ee2b88fcd
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentConfirmationDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "DocumentConfirmation")
+public record DocumentConfirmationDto(
+    @JsonProperty("confirmationTextField") @NotBlank String confirmationTextField,
+    @JsonProperty("answer") Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java
new file mode 100644
index 000000000..6f3bfc827
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentContentDto.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentContent")
+public record DocumentContentDto(
+    @NotNull @Valid @Size(min = 1) List<DocumentSectionDto> sections) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java
new file mode 100644
index 000000000..0b240c20a
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(name = "DocumentSection")
+public record DocumentSectionDto(
+    @Size(max = 200) String sectionTitle,
+    @NotNull @Valid @Size(min = 1) List<DocumentSectionElementDto> sectionElements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java
new file mode 100644
index 000000000..21d48c7c2
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSectionElementDto.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+
+@Schema(name = "DocumentSectionElement")
+public record DocumentSectionElementDto(
+    @Valid DocumentAnamnesisQuestionDto anamnesisQuestion,
+    @Valid DocumentTextBlockDto textBlock,
+    @Valid DocumentConfirmationDto confirmation) {
+
+  @AssertTrue(message = "Only either one of the properties is allowed to be defined")
+  @JsonIgnore
+  @SuppressWarnings("unused")
+  public boolean isSolelyOnePropertyDefined() {
+    return isAnamnesisQuestionSolely() || isTextBlockSolely() || isConfirmationSolely();
+  }
+
+  @JsonIgnore
+  public boolean isAnamnesisQuestionSolely() {
+    return anamnesisQuestion != null && textBlock == null && confirmation == null;
+  }
+
+  @JsonIgnore
+  public boolean isTextBlockSolely() {
+    return textBlock != null && anamnesisQuestion == null && confirmation == null;
+  }
+
+  @JsonIgnore
+  public boolean isConfirmationSolely() {
+    return confirmation != null && textBlock == null && anamnesisQuestion == null;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java
new file mode 100644
index 000000000..02abea1bd
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementMultiSelectDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "DocumentSubElementMultiSelect")
+public record DocumentSubElementMultiSelectDto(
+    @NotNull @Size(max = 200) String questionText, Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java
new file mode 100644
index 000000000..3488d7ab6
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentSubElementTextDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+@Schema(name = "DocumentSubElementText")
+public record DocumentSubElementTextDto(
+    @NotNull @Size(max = 200) String questionText,
+    @Size(max = 4000) String answer) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java
new file mode 100644
index 000000000..47ec68ec4
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/api/DocumentTextBlockDto.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "DocumentTextBlock")
+public record DocumentTextBlockDto(@NotBlank String textField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java
new file mode 100644
index 000000000..a50447e70
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.informationstatement;
+
+import de.eshg.travelmedicine.document.TemplateToDocumentMapper;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class InformationStatementFactory {
+
+  private final TemplateToDocumentMapper templateToDocumentMapper;
+
+  public InformationStatementFactory(TemplateToDocumentMapper templateToDocumentMapper) {
+    this.templateToDocumentMapper = templateToDocumentMapper;
+  }
+
+  public InformationStatement createInformationStatement(InformationStatementTemplate template) {
+    String documentContent = templateToDocumentMapper.transferContent(template.getContent());
+    InformationStatement informationStatement = new InformationStatement();
+    informationStatement.setTitle(template.getTitle());
+    informationStatement.setContent(documentContent);
+    return informationStatement;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
similarity index 53%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
index 6b1a710ae..c1d54bd92 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementMapper.java
@@ -3,10 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation;
+package de.eshg.travelmedicine.document.informationstatement;
 
-import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
 import java.util.Comparator;
 import java.util.List;
 import org.springframework.stereotype.Component;
@@ -24,10 +27,18 @@ public class InformationStatementMapper {
 
   public InformationStatementDto mapInformationStatementToInterfaceType(
       InformationStatement informationStatement) {
+    ObjectMapper objectMapper = new ObjectMapper();
+    DocumentContentDto informationStatementContent;
+    try {
+      informationStatementContent =
+          objectMapper.readValue(informationStatement.getContent(), DocumentContentDto.class);
+    } catch (JsonProcessingException e) {
+      throw new IllegalArgumentException("Content does not match required structure");
+    }
     return new InformationStatementDto(
         informationStatement.getId(),
         informationStatement.getTitle(),
-        informationStatement.getContent(),
+        informationStatementContent,
         informationStatement.isCitizenHasAnswered(),
         informationStatement.getCreatedAt(),
         informationStatement.getModifiedAt());
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java
new file mode 100644
index 000000000..367cbbf0d
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/InformationStatementService.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.informationstatement;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.rest.service.error.NotFoundException;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.notification.NotificationService;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateRepository;
+import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateState;
+import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
+import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetInformationStatementsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class InformationStatementService {
+
+  private final ProcedureAccessor procedureAccessor;
+  private final InformationStatementTemplateRepository informationStatementTemplateRepository;
+  private final InformationStatementMapper informationStatementMapper;
+  private final InformationStatementFactory informationStatementFactory;
+  private final NotificationService notificationService;
+  private final VaccinationConsultationService vaccinationConsultationService;
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public InformationStatementService(
+      ProcedureAccessor procedureAccessor,
+      InformationStatementTemplateRepository informationStatementTemplateRepository,
+      InformationStatementMapper informationStatementMapper,
+      InformationStatementFactory informationStatementFactory,
+      NotificationService notificationService,
+      VaccinationConsultationService vaccinationConsultationService) {
+    this.procedureAccessor = procedureAccessor;
+    this.informationStatementTemplateRepository = informationStatementTemplateRepository;
+    this.informationStatementMapper = informationStatementMapper;
+    this.informationStatementFactory = informationStatementFactory;
+    this.notificationService = notificationService;
+    this.vaccinationConsultationService = vaccinationConsultationService;
+  }
+
+  public DocumentContentDto getInformationStatementForCitizenPortal(
+      UUID citizenUserId, UUID informationStatementId) {
+    InformationStatement informationStatement =
+        findInformationStatement(citizenUserId, informationStatementId);
+
+    if (informationStatement.isCitizenHasAnswered()) {
+      throw new BadRequestException("Information statement already answered.");
+    }
+
+    return informationStatementMapper
+        .mapInformationStatementToInterfaceType(informationStatement)
+        .content();
+  }
+
+  public GetInformationStatementsResponse getInformationStatementsForEmployeePortal(
+      UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
+
+    List<InformationStatementDto> informationStatements =
+        informationStatementMapper.mapInformationStatementsToInterfaceType(
+            vaccinationConsultation.getInformationStatements());
+    return new GetInformationStatementsResponse(procedureId, informationStatements);
+  }
+
+  private InformationStatement findInformationStatement(
+      UUID citizenUserId, UUID informationStatementId) {
+    return procedureAccessor.accessInformationStatement(
+        informationStatementId,
+        null,
+        List.of(new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+  }
+
+  public void patchInformationStatementForCitizenPortal(
+      UUID citizenUserId,
+      UUID informationStatementId,
+      DocumentContentDto patchInformationStatementContent) {
+
+    InformationStatement statementToPatch =
+        findInformationStatement(citizenUserId, informationStatementId);
+
+    if (statementToPatch.isCitizenHasAnswered()) {
+      throw new BadRequestException("Information statement already answered.");
+    }
+
+    statementToPatch.setContent(toJsonString(patchInformationStatementContent));
+    statementToPatch.setCitizenHasAnswered(true);
+  }
+
+  public List<UUID> addInformationStatements(
+      UUID procedureId, PostInformationStatementsRequest request) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkNotClosed);
+
+    List<InformationStatement> newStatements =
+        request.templateIds().stream()
+            .map(
+                templateID -> {
+                  InformationStatementTemplate template =
+                      informationStatementTemplateRepository
+                          .findById(templateID)
+                          .orElseThrow(
+                              () -> new NotFoundException("No such template: " + templateID));
+                  if (template.getState() != InformationStatementTemplateState.FINAL)
+                    throw new BadRequestException(
+                        "The template can't be used until it's in its FINAL state.");
+                  return template;
+                })
+            .map(informationStatementFactory::createInformationStatement)
+            .toList();
+
+    vaccinationConsultation.getInformationStatements().addAll(newStatements);
+    newStatements.forEach(s -> s.setVaccinationConsultation(vaccinationConsultation));
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewInformationStatement(
+          vaccinationConsultationService.patientOf(vaccinationConsultation));
+    }
+    informationStatementTemplateRepository.flush();
+    return newStatements.stream().map(InformationStatement::getId).toList();
+  }
+
+  public void deleteInformationStatement(UUID procedureId, UUID informationStatementId) {
+    InformationStatement informationStatement =
+        procedureAccessor.accessInformationStatement(
+            informationStatementId, procedureId, ProcedureAccessor.checkNotClosed);
+
+    VaccinationConsultation vaccinationConsultation =
+        informationStatement.getVaccinationConsultation();
+
+    vaccinationConsultation.getInformationStatements().remove(informationStatement);
+  }
+
+  private String toJsonString(Object content) {
+    try {
+      return objectMapper.writeValueAsString(content);
+    } catch (JsonProcessingException e) {
+      throw new BadRequestException("Content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
similarity index 72%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
index 89cb5baa4..011cab066 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/api/InformationStatementDto.java
@@ -3,9 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.api;
+package de.eshg.travelmedicine.document.informationstatement.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import java.time.Instant;
@@ -15,7 +17,7 @@ import java.util.UUID;
 public record InformationStatementDto(
     @NotNull UUID id,
     @NotNull @Size(max = 200) String title,
-    @NotNull @Size(max = 4000) String content,
+    @NotNull @Valid DocumentContentDto content,
     @NotNull boolean citizenHasAnswered,
     @NotNull Instant createdAt,
     @NotNull Instant modifiedAt) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
similarity index 62%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
index fa55ff83c..46bc72bc9 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatementRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/InformationStatementRepository.java
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.persistence.entity;
+package de.eshg.travelmedicine.document.informationstatement.persistence;
 
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
 import java.util.UUID;
 import org.springframework.data.jpa.repository.JpaRepository;
 
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
similarity index 93%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
index db4dc4f9f..1fe4bdd67 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/InformationStatement.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/informationstatement/persistence/entity/InformationStatement.java
@@ -3,11 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.vaccinationconsultation.persistence.entity;
+package de.eshg.travelmedicine.document.informationstatement.persistence.entity;
 
 import de.eshg.domain.model.GloballyUniqueEntityBase;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EntityListeners;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
similarity index 86%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
index 13f36d22f..cf49ef0e6 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryController.java
@@ -3,10 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory;
+package de.eshg.travelmedicine.document.medicalhistory;
 
 import de.eshg.rest.service.security.config.BaseUrls;
-import de.eshg.travelmedicine.medicalhistory.api.PatchMedicalHistoryRequest;
+import de.eshg.travelmedicine.document.medicalhistory.api.PatchMedicalHistoryRequest;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
@@ -39,6 +39,6 @@ public class MedicalHistoryController {
   public void patchMedicalHistory(
       @PathVariable("id") UUID id,
       @RequestBody @Valid PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
-    medicalHistoryService.patchMedicalHistory(id, patchMedicalHistoryRequest);
+    medicalHistoryService.patchMedicalHistoryForEmployeePortal(id, patchMedicalHistoryRequest);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java
new file mode 100644
index 000000000..5a7879e6c
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.medicalhistory;
+
+import de.eshg.travelmedicine.document.TemplateToDocumentMapper;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MedicalHistoryFactory {
+
+  private final TemplateToDocumentMapper templateToDocumentMapper;
+
+  public MedicalHistoryFactory(TemplateToDocumentMapper templateToDocumentMapper) {
+    this.templateToDocumentMapper = templateToDocumentMapper;
+  }
+
+  public MedicalHistory createMedicalHistory(MedicalHistoryTemplate template) {
+    String documentContent = templateToDocumentMapper.transferContent(template.getContent());
+    MedicalHistory medicalHistory = new MedicalHistory();
+    medicalHistory.setContent(documentContent);
+    return medicalHistory;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
similarity index 64%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
index bcd1f3c19..80ddd1fe1 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryMapper.java
@@ -3,14 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory;
+package de.eshg.travelmedicine.document.medicalhistory;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.ProcedureStepService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import java.time.Instant;
@@ -20,12 +20,12 @@ public class MedicalHistoryMapper {
 
   public static MedicalHistoryDto toInterfaceType(MedicalHistory medicalHistory, ProcedureStep ps) {
     ObjectMapper objectMapper = new ObjectMapper();
-    MedicalHistoryContentDto medicalHistoryContent;
+    DocumentContentDto medicalHistoryContent;
     try {
       medicalHistoryContent =
-          objectMapper.readValue(medicalHistory.getContent(), MedicalHistoryContentDto.class);
+          objectMapper.readValue(medicalHistory.getContent(), DocumentContentDto.class);
     } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
+      throw new IllegalArgumentException("Content does not match required structure");
     }
     return new MedicalHistoryDto(
         medicalHistory.getId(),
@@ -41,15 +41,15 @@ public class MedicalHistoryMapper {
   }
 
   private static Instant getAppointment(ProcedureStep ps) {
-    return ProcedureStepService.getAppointment(ps);
+    return ProcedureStepService.getStartDateOrEarliestDateFromAppointment(ps);
   }
 
-  public static MedicalHistoryContentDto contentToInterfaceType(MedicalHistory medicalHistory) {
+  public static DocumentContentDto contentToInterfaceType(MedicalHistory medicalHistory) {
     ObjectMapper objectMapper = new ObjectMapper();
-    MedicalHistoryContentDto medicalHistoryContent;
+    DocumentContentDto medicalHistoryContent;
     try {
       medicalHistoryContent =
-          objectMapper.readValue(medicalHistory.getContent(), MedicalHistoryContentDto.class);
+          objectMapper.readValue(medicalHistory.getContent(), DocumentContentDto.class);
     } catch (JsonProcessingException e) {
       throw new BadRequestException("Content does not match required structure");
     }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java
new file mode 100644
index 000000000..0a6f94bbc
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/MedicalHistoryService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.document.medicalhistory;
+
+import static de.eshg.travelmedicine.document.DocumentDtoHelper.isDocumentContentCompletelyAnswered;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.eshg.rest.service.error.BadRequestException;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.PatchMedicalHistoryRequest;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
+import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MedicalHistoryService {
+
+  private final ProcedureAccessor procedureAccessor;
+  private final ObjectMapper objectMapper = new ObjectMapper();
+
+  public MedicalHistoryService(ProcedureAccessor procedureAccessor) {
+    this.procedureAccessor = procedureAccessor;
+  }
+
+  public GetMedicalHistoriesResponse getMedicalHistoriesForEmployeePortal(UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
+
+    List<MedicalHistoryDto> medicalHistories =
+        vaccinationConsultation.getProcedureSteps().stream()
+            .filter(ps -> ps.getMedicalHistory() != null)
+            .map(ps -> MedicalHistoryMapper.toInterfaceType(ps.getMedicalHistory(), ps))
+            .sorted(Comparator.comparing(MedicalHistoryDto::appointment))
+            .toList();
+    return new GetMedicalHistoriesResponse(
+        vaccinationConsultation.getExternalId(), medicalHistories);
+  }
+
+  public DocumentContentDto getMedicalHistoryForCitizenPortal(
+      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
+    ProcedureStep procedureStep =
+        procedureAccessor.accessProcedureStep(
+            procedureStepId,
+            procedureId,
+            List.of(
+                new ProcedureAccessor.CheckNotClosed(),
+                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+
+    if (procedureStep.getMedicalHistory().isCitizenHasAnswered()) {
+      throw new BadRequestException("Medical history already answered.");
+    }
+
+    return MedicalHistoryMapper.contentToInterfaceType(procedureStep.getMedicalHistory());
+  }
+
+  public void patchMedicalHistoryForEmployeePortal(
+      UUID medicalHistoryId, PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
+    MedicalHistory medicalHistory =
+        procedureAccessor.accessMedicalHistory(
+            medicalHistoryId, null, ProcedureAccessor.checkNotClosed);
+
+    String oldContent = medicalHistory.getContent();
+    String newContent = toJsonString(patchMedicalHistoryRequest.medicalHistoryContent());
+    if (!oldContent.equals(newContent)) {
+      medicalHistory.setContent(newContent);
+      medicalHistory.setCitizenHasAnswered(true);
+    }
+
+    medicalHistory.setNote(patchMedicalHistoryRequest.note());
+    medicalHistory.setCompletelyAnswered(
+        isDocumentContentCompletelyAnswered(patchMedicalHistoryRequest.medicalHistoryContent()));
+  }
+
+  public void patchMedicalHistoryForCitizenPortal(
+      UUID citizenUserId,
+      UUID procedureId,
+      UUID procedureStepId,
+      DocumentContentDto patchMedicalHistoryContent) {
+    ProcedureStep procedureStep =
+        procedureAccessor.accessProcedureStep(
+            procedureStepId,
+            procedureId,
+            List.of(
+                new ProcedureAccessor.CheckNotClosed(),
+                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
+    MedicalHistory medicalHistory = procedureStep.getMedicalHistory();
+    if (medicalHistory.isCitizenHasAnswered()) {
+      throw new BadRequestException("Medical history already answered by citizen.");
+    }
+
+    medicalHistory.setContent(toJsonString(patchMedicalHistoryContent));
+    medicalHistory.setCompletelyAnswered(
+        isDocumentContentCompletelyAnswered(patchMedicalHistoryContent));
+    medicalHistory.setCitizenHasAnswered(true);
+  }
+
+  private String toJsonString(Object content) {
+    try {
+      return objectMapper.writeValueAsString(content);
+    } catch (JsonProcessingException e) {
+      throw new BadRequestException("Content does not match required structure");
+    }
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
similarity index 77%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
index 81e5e7c4f..abd39e870 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/MedicalHistoryDto.java
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.api;
+package de.eshg.travelmedicine.document.medicalhistory.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
@@ -17,7 +18,7 @@ public record MedicalHistoryDto(
     @NotNull UUID procedureStepId,
     @NotNull Instant appointment,
     @NotNull boolean followUp,
-    @NotNull @Valid MedicalHistoryContentDto medicalHistoryContent,
+    @NotNull @Valid DocumentContentDto medicalHistoryContent,
     @NotNull boolean isCompletelyAnswered,
     @NotNull boolean citizenHasAnswered,
     String note,
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
similarity index 55%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
index 4b5230c65..deb1c6ea8 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/PatchMedicalHistoryRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/api/PatchMedicalHistoryRequest.java
@@ -3,12 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.api;
+package de.eshg.travelmedicine.document.medicalhistory.api;
 
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
 public record PatchMedicalHistoryRequest(
-    @NotNull @Valid MedicalHistoryContentDto medicalHistoryContent,
-    @Size(max = 4000) String note) {}
+    @NotNull @Valid DocumentContentDto medicalHistoryContent, @Size(max = 4000) String note) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
similarity index 83%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
index ed1fb0bec..00438c0d9 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/MedicalHistoryRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/MedicalHistoryRepository.java
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.persistence;
+package de.eshg.travelmedicine.document.medicalhistory.persistence;
 
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
 import java.util.Optional;
 import java.util.UUID;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
similarity index 96%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
index ded6b0244..c253e987e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/persistence/entity/MedicalHistory.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/document/medicalhistory/persistence/entity/MedicalHistory.java
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-package de.eshg.travelmedicine.medicalhistory.persistence.entity;
+package de.eshg.travelmedicine.document.medicalhistory.persistence.entity;
 
 import de.eshg.domain.model.GloballyUniqueEntityBase;
 import de.eshg.lib.common.DataSensitivity;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java
deleted file mode 100644
index 9fc4c6d98..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryHelper.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory;
-
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionElementDataDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySectionElementDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySubElementMultiSelectDto;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistorySubElementTextDto;
-import java.util.List;
-
-public class MedicalHistoryHelper {
-
-  private MedicalHistoryHelper() {
-    throw new IllegalStateException("Utility class");
-  }
-
-  public static boolean isMedicalHistoryCompletelyAnswered(
-      MedicalHistoryContentDto medicalHistoryContentDto) {
-    for (MedicalHistorySectionDto section : medicalHistoryContentDto.medicalHistorySections()) {
-      if (!isSectionCompletelyAnswered(section)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private static boolean isSectionCompletelyAnswered(MedicalHistorySectionDto section) {
-    for (MedicalHistorySectionElementDto sectionElement : section.medicalHistorySectionElements()) {
-      MedicalHistorySectionElementDataDto data = sectionElement.medicalHistorySectionElement();
-
-      // simple question
-      if (data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() == null
-          && (!isCompletelyAnsweredSimpleQuestion(data))) {
-        return false;
-      }
-
-      // free text question
-      if (data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() != null
-          && (!isCompletelyAnsweredFreeTextQuestion(data))) {
-        return false;
-      }
-
-      // multiple choice question without free text question
-      if (!data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() == null
-          && (!isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(data))) {
-        return false;
-      }
-
-      // multiple choice question with free text question
-      if (!data.medicalHistorySubElementMultiSelects().isEmpty()
-          && data.medicalHistorySubElementText() != null
-          && (!isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(data))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private static boolean isCompletelyAnsweredSimpleQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // simple question should be answered with yes or no
-    return data.answer() != null;
-  }
-
-  private static boolean isCompletelyAnsweredFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // free text / open question should be answered with no or have (when answering the question
-    // with yes) a non-blank free text field
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && isSubelementTextCompletelyAnswered(data.medicalHistorySubElementText()));
-  }
-
-  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithoutFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // multiple choice question should be answered with no or have (when answering the question with
-    // yes) a non-blank free text field and/or at least one marked multiple choice answer
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && isMultiSelectCompletelyAnswered(data.medicalHistorySubElementMultiSelects()));
-  }
-
-  private static boolean isCompletelyAnsweredMultipleChoiceQuestionWithFreeTextQuestion(
-      MedicalHistorySectionElementDataDto data) {
-    // multiple choice question should be answered with no or have (when answering the question with
-    // yes) a non-blank free text field and/or at least one marked multiple choice answer
-    return Boolean.FALSE.equals(data.answer())
-        || (Boolean.TRUE.equals(data.answer())
-            && (isSubelementTextCompletelyAnswered(data.medicalHistorySubElementText())
-                || isMultiSelectCompletelyAnswered(data.medicalHistorySubElementMultiSelects())));
-  }
-
-  private static boolean isMultiSelectCompletelyAnswered(
-      List<MedicalHistorySubElementMultiSelectDto> medicalHistorySubElementMultiSelects) {
-    return medicalHistorySubElementMultiSelects.stream()
-        .anyMatch(multiSelect -> Boolean.TRUE.equals(multiSelect.answer()));
-  }
-
-  private static boolean isSubelementTextCompletelyAnswered(MedicalHistorySubElementTextDto text) {
-    if (text == null) {
-      return false;
-    }
-    return text.answer() != null && !text.answer().isBlank();
-  }
-}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java
deleted file mode 100644
index 97a41da66..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/MedicalHistoryService.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory;
-
-import static de.eshg.travelmedicine.medicalhistory.MedicalHistoryHelper.isMedicalHistoryCompletelyAnswered;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
-import de.eshg.travelmedicine.medicalhistory.api.PatchMedicalHistoryRequest;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
-import de.eshg.travelmedicine.vaccinationconsultation.ProcedureAccessor;
-import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultation;
-import java.util.Comparator;
-import java.util.List;
-import java.util.UUID;
-import org.springframework.stereotype.Service;
-
-@Service
-public class MedicalHistoryService {
-
-  private final ProcedureAccessor procedureAccessor;
-
-  public MedicalHistoryService(ProcedureAccessor procedureAccessor) {
-    this.procedureAccessor = procedureAccessor;
-  }
-
-  public void patchMedicalHistory(
-      UUID medicalHistoryId, PatchMedicalHistoryRequest patchMedicalHistoryRequest) {
-    MedicalHistory medicalHistory =
-        procedureAccessor.accessMedicalHistory(
-            medicalHistoryId, null, ProcedureAccessor.checkNotClosed);
-
-    ObjectMapper objectMapper = new ObjectMapper();
-    try {
-      medicalHistory.setContent(
-          objectMapper.writeValueAsString(patchMedicalHistoryRequest.medicalHistoryContent()));
-    } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
-    }
-    medicalHistory.setNote(patchMedicalHistoryRequest.note());
-    medicalHistory.setCompletelyAnswered(
-        isMedicalHistoryCompletelyAnswered(patchMedicalHistoryRequest.medicalHistoryContent()));
-  }
-
-  public GetMedicalHistoriesResponse getMedicalHistories(
-      VaccinationConsultation vaccinationConsultation) {
-    List<MedicalHistoryDto> medicalHistories =
-        vaccinationConsultation.getProcedureSteps().stream()
-            .filter(ps -> ps.getMedicalHistory() != null)
-            .map(ps -> MedicalHistoryMapper.toInterfaceType(ps.getMedicalHistory(), ps))
-            .sorted(Comparator.comparing(MedicalHistoryDto::appointment))
-            .toList();
-    return new GetMedicalHistoriesResponse(
-        vaccinationConsultation.getExternalId(), medicalHistories);
-  }
-}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java
deleted file mode 100644
index 5486e7efa..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistoryContentDto.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistoryContent")
-public record MedicalHistoryContentDto(
-    @NotNull @Valid @Size(min = 1) @JsonProperty("sections")
-        List<MedicalHistorySectionDto> medicalHistorySections) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java
deleted file mode 100644
index 01d23ac00..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionDto.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistorySection")
-public record MedicalHistorySectionDto(
-    @JsonProperty("sectionTitle") @Size(max = 200) String sectionTitle,
-    @NotNull @Valid @Size(min = 1) @JsonProperty("sectionElements")
-        List<MedicalHistorySectionElementDto> medicalHistorySectionElements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java
deleted file mode 100644
index fdc3fe442..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDataDto.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import java.util.List;
-
-@Schema(name = "MedicalHistorySectionElementData")
-public record MedicalHistorySectionElementDataDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer,
-    @NotNull @Valid @JsonProperty("subElementMultiSelect")
-        List<MedicalHistorySubElementMultiSelectDto> medicalHistorySubElementMultiSelects,
-    @Valid @JsonProperty("subElementText")
-        MedicalHistorySubElementTextDto medicalHistorySubElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java
deleted file mode 100644
index 36db6b7bc..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySectionElementDto.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySectionElement")
-public record MedicalHistorySectionElementDto(
-    @NotNull @Size(max = 200) @JsonProperty("elementType") String elementType,
-    @NotNull @Valid @JsonProperty("elementData")
-        MedicalHistorySectionElementDataDto medicalHistorySectionElement) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java
deleted file mode 100644
index 138ffd4c8..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementMultiSelectDto.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySubElementMultiSelect")
-public record MedicalHistorySubElementMultiSelectDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java
deleted file mode 100644
index 663c00fac..000000000
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/medicalhistory/api/MedicalHistorySubElementTextDto.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-package de.eshg.travelmedicine.medicalhistory.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@Schema(name = "MedicalHistorySubElementText")
-public record MedicalHistorySubElementTextDto(
-    @NotNull @Size(max = 200) String questionText,
-    @Size(max = 4000) @JsonProperty("answer")
-        String answer) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
index b0d52a9ca..854d2a7d5 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationService.java
@@ -7,8 +7,13 @@ package de.eshg.travelmedicine.notification;
 
 import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
+import java.time.Instant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.stereotype.Service;
 import org.springframework.web.util.UriComponentsBuilder;
 
@@ -16,42 +21,127 @@ import org.springframework.web.util.UriComponentsBuilder;
 public class NotificationService {
 
   private static final String CITIZEN_PORTAL_RMBI_LOGIN_PATH = "impfberatung/meine-termine";
+  private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
 
   private final MailClient mailClient;
   private final ModuleClientAuthenticator moduleClientAuthenticator;
   private final NotificationProperties notificationProperties;
   private final String citizenPortalUrl;
+  private final NotificationText notificationText;
+  private final SecurityContextHolderStrategy securityContextHolderStrategy =
+      SecurityContextHolder.getContextHolderStrategy();
 
   public NotificationService(
       MailClient mailClient,
       ModuleClientAuthenticator moduleClientAuthenticator,
       NotificationProperties notificationProperties,
-      @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl) {
+      @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl,
+      NotificationText notificationText) {
     this.mailClient = mailClient;
     this.moduleClientAuthenticator = moduleClientAuthenticator;
     this.notificationProperties = notificationProperties;
     this.citizenPortalUrl = citizenPortalUrl;
-    ;
+    this.notificationText = notificationText;
   }
 
-  public void onNewCitizenProcedure(
-      String accessCode, PatientDto patientDto, ProcedureStep procedureStep) {
-    String text =
-        NotificationText.getNewCitizenProcedureBody(
+  public void notifyNewCitizenProcedure(
+      PatientDto patientDto, Instant firstAppointment, String accessCode) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewCitizenProcedureSubject(),
+        notificationText.getNewCitizenProcedureBody(
             patientDto.firstName(),
             patientDto.lastName(),
-            procedureStep.getAppointment().getAppointmentStart(),
+            firstAppointment,
             buildLoginUrl(accessCode),
             accessCode,
-            notificationProperties.greeting());
-
-    moduleClientAuthenticator.doWithModuleClientAuthentication(
-        () ->
-            mailClient.sendMail(
-                patientDto.emailAddresses().getFirst(),
-                notificationProperties.fromAddress(),
-                NotificationText.getNewCitizenProcedureSubject(),
-                text));
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyBookedByCitizen(PatientDto patientDto, Instant appointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getBookingByCitizenSubject(),
+        notificationText.getBookingByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            appointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyBookedByEmployee(PatientDto patientDto, Instant appointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getBookingByEmployeeSubject(),
+        notificationText.getBookingByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            appointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyCancelledByCitizen(PatientDto patientDto, Instant cancelledAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getCancellationByCitizenSubject(),
+        notificationText.getCancellationByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            cancelledAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyCancelledByEmployee(PatientDto patientDto, Instant cancelledAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getCancellationByEmployeeSubject(),
+        notificationText.getCancellationByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            cancelledAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyRebookedByCitizen(
+      PatientDto patientDto, Instant previousAppointment, Instant newAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getRebookingByCitizenSubject(),
+        notificationText.getRebookingByCitizenBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            previousAppointment,
+            newAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyRebookedByEmployee(
+      PatientDto patientDto, Instant previousAppointment, Instant newAppointment) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getRebookingByEmployeeSubject(),
+        notificationText.getRebookingByEmployeeBody(
+            patientDto.firstName(),
+            patientDto.lastName(),
+            previousAppointment,
+            newAppointment,
+            notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyNewInformationStatement(PatientDto patientDto) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewInformationStatementSubject(),
+        notificationText.getNewInformationStatementBody(
+            patientDto.firstName(), patientDto.lastName(), notificationProperties.greeting()),
+        patientDto);
+  }
+
+  public void notifyNewFollowUpAppointment(PatientDto patientDto) {
+    sendMailWithModuleClientAuthentication(
+        notificationText.getNewFollowUpAppointmentSubject(),
+        notificationText.getNewFollowUpAppointmentBody(
+            patientDto.firstName(), patientDto.lastName(), notificationProperties.greeting()),
+        patientDto);
   }
 
   private String buildLoginUrl(String accessCode) {
@@ -61,4 +151,23 @@ public class NotificationService {
         .build()
         .toUriString();
   }
+
+  private void sendMailWithModuleClientAuthentication(
+      String subject, String body, PatientDto patientDto) {
+    SecurityContext previousContext = securityContextHolderStrategy.getContext();
+    try {
+      securityContextHolderStrategy.clearContext();
+      moduleClientAuthenticator.doWithModuleClientAuthentication(
+          () -> doSendMail(subject, body, patientDto));
+    } finally {
+      securityContextHolderStrategy.setContext(previousContext);
+    }
+  }
+
+  private void doSendMail(String subject, String body, PatientDto patientDto) {
+    log.info("send mail(s): " + subject);
+    for (String emailAddress : patientDto.emailAddresses()) {
+      mailClient.sendMail(emailAddress, notificationProperties.fromAddress(), subject, body);
+    }
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
index 291b97bb0..5ad942dcb 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/notification/NotificationText.java
@@ -5,53 +5,224 @@
 
 package de.eshg.travelmedicine.notification;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.Locale;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.FileCopyUtils;
 
+@Component
 public class NotificationText {
-  private static final DateTimeFormatter APPOINTMENT_START_FORMAT =
+
+  private final DateTimeFormatter appointmentStartFormat =
       DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm", Locale.GERMAN);
 
-  private static final String NEW_CITIZEN_PROCEDURE_SUBJECT = "Deine Terminbuchung bei uns!";
-  private static final String NEW_CITIZEN_PROCEDURE_BODY =
-      """
-      Sehr geehrte(r) %s %s,
+  @Value("${de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject}")
+  private String newCitizenProcedureSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_citizen_procedure.body}")
+  private Resource newCitizenProcedureBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_citizen.subject}")
+  private String bookingByCitizenSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_citizen.body}")
+  private Resource bookingByCitizenBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_employee.subject}")
+  private String bookingByEmployeeSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.booking_by_employee.body}")
+  private Resource bookingByEmployeeBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject}")
+  private String cancellationByCitizenSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_citizen.body}")
+  private Resource cancellationByCitizenBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject}")
+  private String cancellationByEmployeeSubject;
 
-    wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr erfolgreich eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
-    Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
-    %s
+  @Value("${de.eshg.travel-medicine.notification.template.cancellation_by_employee.body}")
+  private Resource cancellationByEmployeeBodyTemplate;
 
-    Anmeldecode: %s
-    Zur Verifikation wird Ihr Geburtsdatum benötigt.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject}")
+  private String rebookingByCitizenSubject;
 
-    Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Stornierungen verwenden Sie bitte den angegebenen Link.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_citizen.body}")
+  private Resource rebookingByCitizenBodyTemplate;
 
-    Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf Sie bald bei uns begrüßen zu dürfen.
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject}")
+  private String rebookingByEmployeeSubject;
 
-      Mit freundlichen Grüßen,
+  @Value("${de.eshg.travel-medicine.notification.template.rebooking_by_employee.body}")
+  private Resource rebookingByEmployeeBodyTemplate;
 
-      %s""";
+  @Value("${de.eshg.travel-medicine.notification.template.new_information_statement.subject}")
+  private String newInformationStatementSubject;
 
-  public static String getNewCitizenProcedureSubject() {
-    return NEW_CITIZEN_PROCEDURE_SUBJECT;
+  @Value("${de.eshg.travel-medicine.notification.template.new_information_statement.body}")
+  private Resource newInformationStatementBodyTemplate;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject}")
+  private String newFollowUpAppointmentSubject;
+
+  @Value("${de.eshg.travel-medicine.notification.template.new_follow_up_appointment.body}")
+  private Resource newFollowUpAppointmentBodyTemplate;
+
+  private String formatAppointmentStart(Instant appointmentStart) {
+    return appointmentStartFormat.format(appointmentStart.atZone(ZoneId.of("Europe/Berlin")));
+  }
+
+  public String getNewCitizenProcedureSubject() {
+    return newCitizenProcedureSubject;
   }
 
-  public static String getNewCitizenProcedureBody(
+  public String getNewCitizenProcedureBody(
       String firstName,
       String lastName,
       Instant appointmentStart,
       String loginUrl,
       String accessCode,
       String greeting) {
-    return String.format(
-        NEW_CITIZEN_PROCEDURE_BODY,
+
+    String templateBody = readTemplateBody(newCitizenProcedureBodyTemplate);
+
+    return java.lang.String.format(
+        templateBody,
         firstName,
         lastName,
-        APPOINTMENT_START_FORMAT.format(appointmentStart.atZone(ZoneId.of("Europe/Berlin"))),
+        formatAppointmentStart(appointmentStart),
         loginUrl,
         accessCode,
         greeting);
   }
+
+  public String getBookingByCitizenSubject() {
+    return bookingByCitizenSubject;
+  }
+
+  public String getBookingByCitizenBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(bookingByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getBookingByEmployeeSubject() {
+    return bookingByEmployeeSubject;
+  }
+
+  public String getBookingByEmployeeBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(bookingByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getCancellationByCitizenSubject() {
+    return cancellationByCitizenSubject;
+  }
+
+  public String getCancellationByCitizenBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(cancellationByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getCancellationByEmployeeSubject() {
+    return cancellationByEmployeeSubject;
+  }
+
+  public String getCancellationByEmployeeBody(
+      String firstName, String lastName, Instant appointmentStart, String greeting) {
+
+    String templateBody = readTemplateBody(cancellationByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody, firstName, lastName, formatAppointmentStart(appointmentStart), greeting);
+  }
+
+  public String getRebookingByCitizenSubject() {
+    return rebookingByCitizenSubject;
+  }
+
+  public String getRebookingByCitizenBody(
+      String firstName,
+      String lastName,
+      Instant previousAppointmentStart,
+      Instant newAppointmentStart,
+      String greeting) {
+
+    String templateBody = readTemplateBody(rebookingByCitizenBodyTemplate);
+    return java.lang.String.format(
+        templateBody,
+        firstName,
+        lastName,
+        formatAppointmentStart(previousAppointmentStart),
+        formatAppointmentStart(newAppointmentStart),
+        greeting);
+  }
+
+  public String getRebookingByEmployeeSubject() {
+    return rebookingByEmployeeSubject;
+  }
+
+  public String getRebookingByEmployeeBody(
+      String firstName,
+      String lastName,
+      Instant previousAppointmentStart,
+      Instant newAppointmentStart,
+      String greeting) {
+
+    String templateBody = readTemplateBody(rebookingByEmployeeBodyTemplate);
+    return java.lang.String.format(
+        templateBody,
+        firstName,
+        lastName,
+        formatAppointmentStart(previousAppointmentStart),
+        formatAppointmentStart(newAppointmentStart),
+        greeting);
+  }
+
+  public String getNewInformationStatementSubject() {
+    return newInformationStatementSubject;
+  }
+
+  public String getNewInformationStatementBody(String firstName, String lastName, String greeting) {
+
+    String templateBody = readTemplateBody(newInformationStatementBodyTemplate);
+    return java.lang.String.format(templateBody, firstName, lastName, greeting);
+  }
+
+  public String getNewFollowUpAppointmentSubject() {
+    return newFollowUpAppointmentSubject;
+  }
+
+  public String getNewFollowUpAppointmentBody(String firstName, String lastName, String greeting) {
+
+    String templateBody = readTemplateBody(newFollowUpAppointmentBodyTemplate);
+    return java.lang.String.format(templateBody, firstName, lastName, greeting);
+  }
+
+  private static String readTemplateBody(Resource bodyTemplateResource) {
+    try (Reader reader =
+        new InputStreamReader(bodyTemplateResource.getInputStream(), StandardCharsets.UTF_8)) {
+      return FileCopyUtils.copyToString(reader);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
similarity index 84%
rename from backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java
rename to backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
index 6678ac9e2..35d345f66 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDataDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateAnamnesisQuestionDto.java
@@ -12,10 +12,9 @@ import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import java.util.List;
 
-@Schema(name = "TemplateSectionElementData")
-public record TemplateSectionElementDataDto(
+@Schema(name = "TemplateAnamnesisQuestion")
+public record TemplateAnamnesisQuestionDto(
     @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer,
     @NotNull @Valid @JsonProperty("subElementMultiSelect")
         List<TemplateSubElementMultiSelectDto> templateSubElementMultiSelects,
     @Valid @JsonProperty("subElementText") TemplateSubElementTextDto templateSubElementText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java
new file mode 100644
index 000000000..1798607ed
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateConfirmationDto.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.template.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "TemplateConfirmation")
+public record TemplateConfirmationDto(
+    @JsonProperty("confirmationTextField") @NotBlank String confirmationTextField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
index 32ef19acc..6d9ee618c 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSectionElementDto.java
@@ -5,14 +5,46 @@
 
 package de.eshg.travelmedicine.template.api;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.AssertTrue;
 
 @Schema(name = "TemplateSectionElement")
 public record TemplateSectionElementDto(
-    @NotNull @Size(max = 200) @JsonProperty("elementType") String elementType,
-    @NotNull @Valid @JsonProperty("elementData")
-        TemplateSectionElementDataDto templateSectionElementData) {}
+    @JsonProperty("anamnesisQuestion") @Valid
+        TemplateAnamnesisQuestionDto templateAnamnesisQuestionDto,
+    @JsonProperty("textBlock") @Valid TemplateTextBlockDto templateTextBlockDto,
+    @JsonProperty("confirmation") @Valid TemplateConfirmationDto templateConfirmationDto) {
+
+  @AssertTrue(message = "Only either one of the properties is allowed to be defined")
+  @JsonIgnore
+  @SuppressWarnings("unused")
+  public boolean isSolelyOnePropertyDefined() {
+    return isTemplateAnamnesisQuestionSolely()
+        || isTemplateTextBlockSolely()
+        || isTemplateConfirmationSolely();
+  }
+
+  @JsonIgnore
+  private boolean isTemplateAnamnesisQuestionSolely() {
+    return templateAnamnesisQuestionDto != null
+        && templateTextBlockDto == null
+        && templateConfirmationDto == null;
+  }
+
+  @JsonIgnore
+  private boolean isTemplateTextBlockSolely() {
+    return templateTextBlockDto != null
+        && templateAnamnesisQuestionDto == null
+        && templateConfirmationDto == null;
+  }
+
+  @JsonIgnore
+  private boolean isTemplateConfirmationSolely() {
+    return templateConfirmationDto != null
+        && templateTextBlockDto == null
+        && templateAnamnesisQuestionDto == null;
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
index 84237cd51..8551303eb 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementMultiSelectDto.java
@@ -12,5 +12,4 @@ import jakarta.validation.constraints.Size;
 
 @Schema(name = "TemplateSubElementMultiSelect")
 public record TemplateSubElementMultiSelectDto(
-    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText,
-    @JsonProperty("answer") Boolean answer) {}
+    @NotNull @Size(max = 200) @JsonProperty("questionText") String questionText) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
index 97c132017..ea61b72d2 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateSubElementTextDto.java
@@ -5,13 +5,11 @@
 
 package de.eshg.travelmedicine.template.api;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
 @Schema(name = "TemplateSubElementText")
 public record TemplateSubElementTextDto(
-    @NotNull @Size(max = 200) String questionText,
-    @Size(max = 4000) @JsonProperty("answer")
-        String answer) {} // no min size as reset answers use empty string
+    @NotNull @Size(max = 200)
+        String questionText) {} // no min size as reset answers use empty string
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java
new file mode 100644
index 000000000..9ba4c93a3
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/api/TemplateTextBlockDto.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.template.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+
+@Schema(name = "TemplateTextBlock")
+public record TemplateTextBlockDto(@JsonProperty("textField") @NotBlank String textField) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
index 17f90ce6d..3ce07b229 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/MedicalHistoryTemplateMapper.java
@@ -30,7 +30,7 @@ public class MedicalHistoryTemplateMapper {
       medicalHistoryTemplateContent =
           OBJECT_MAPPER.readValue(medicalHistoryTemplate.getContent(), TemplateContentDto.class);
     } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
+      throw new IllegalArgumentException("Content does not match required structure");
     }
     return new MedicalHistoryTemplateDto(
         medicalHistoryTemplate.getId(),
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
index f2ed88b2b..1d2dc73c5 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/template/medicalhistorytemplate/persistence/CreateMedicalHistoryTemplateTask.java
@@ -37,7 +37,7 @@ public class CreateMedicalHistoryTemplateTask {
       return; // A template already exists
     }
 
-    transactionHelper.executeInTransaction(
+    transactionHelper.executeInNewTransaction(
         () -> {
           String content = getInitialMedicalHistoryAsString();
 
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java
new file mode 100644
index 000000000..e000eda32
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestHelperUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.testhelper;
+
+import de.eshg.travelmedicine.document.api.DocumentAnamnesisQuestionDto;
+import de.eshg.travelmedicine.document.api.DocumentConfirmationDto;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionDto;
+import de.eshg.travelmedicine.document.api.DocumentSectionElementDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementMultiSelectDto;
+import de.eshg.travelmedicine.document.api.DocumentSubElementTextDto;
+import java.util.List;
+
+public class TestHelperUtil {
+  static DocumentContentDto answerDocumentContent(DocumentContentDto document) {
+    List<DocumentSectionDto> sectionList =
+        document.sections().stream()
+            .map(
+                s -> {
+                  List<DocumentSectionElementDto> documentSectionElements =
+                      s.sectionElements().stream()
+                          .map(
+                              e ->
+                                  new DocumentSectionElementDto(
+                                      answerAnamnesisQuestion(e.anamnesisQuestion()),
+                                      e.textBlock(),
+                                      answerConfirmation(e.confirmation())))
+                          .toList();
+                  return new DocumentSectionDto(s.sectionTitle(), documentSectionElements);
+                })
+            .toList();
+    return new DocumentContentDto(sectionList);
+  }
+
+  static DocumentAnamnesisQuestionDto answerAnamnesisQuestion(
+      DocumentAnamnesisQuestionDto anamnesisQuestion) {
+    if (anamnesisQuestion == null) {
+      return null;
+    }
+
+    List<DocumentSubElementMultiSelectDto> subElementMultiSelectList = List.of();
+    if (!anamnesisQuestion.subElementMultiSelect().isEmpty()) {
+      subElementMultiSelectList =
+          anamnesisQuestion.subElementMultiSelect().stream()
+              .map(e -> new DocumentSubElementMultiSelectDto(e.questionText(), true))
+              .toList();
+    }
+
+    DocumentSubElementTextDto subElementText = null;
+    if (anamnesisQuestion.subElementText() != null) {
+      subElementText =
+          new DocumentSubElementTextDto(
+              anamnesisQuestion.subElementText().questionText(), "Offene Antwort");
+    }
+    return new DocumentAnamnesisQuestionDto(
+        anamnesisQuestion.questionText(), true, subElementMultiSelectList, subElementText);
+  }
+
+  static DocumentConfirmationDto answerConfirmation(DocumentConfirmationDto documentConfirmation) {
+    if (documentConfirmation == null) {
+      return null;
+    }
+    return new DocumentConfirmationDto(documentConfirmation.confirmationTextField(), true);
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
index 76575b1e3..b75919d7e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateAdministrativeService.java
@@ -26,12 +26,14 @@ import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.otherservicetemplate.OtherServiceTemplateService;
 import de.eshg.travelmedicine.otherservicetemplate.api.OtherServiceTemplateDto;
 import de.eshg.travelmedicine.otherservicetemplate.api.PostPutOtherServiceTemplateRequest;
+import de.eshg.travelmedicine.template.api.TemplateAnamnesisQuestionDto;
+import de.eshg.travelmedicine.template.api.TemplateConfirmationDto;
 import de.eshg.travelmedicine.template.api.TemplateContentDto;
 import de.eshg.travelmedicine.template.api.TemplateSectionDto;
-import de.eshg.travelmedicine.template.api.TemplateSectionElementDataDto;
 import de.eshg.travelmedicine.template.api.TemplateSectionElementDto;
 import de.eshg.travelmedicine.template.api.TemplateSubElementMultiSelectDto;
 import de.eshg.travelmedicine.template.api.TemplateSubElementTextDto;
+import de.eshg.travelmedicine.template.api.TemplateTextBlockDto;
 import de.eshg.travelmedicine.template.informationstatementtemplate.InformationStatementTemplateService;
 import de.eshg.travelmedicine.template.informationstatementtemplate.api.InformationStatementTemplateDto;
 import de.eshg.travelmedicine.template.informationstatementtemplate.api.InformationStatementTemplateRequest;
@@ -383,7 +385,7 @@ public class TestPopulateAdministrativeService {
                   "Empty Template Title",
                   DRAFT,
                   null,
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto standardDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -394,7 +396,7 @@ public class TestPopulateAdministrativeService {
                       diseases.get(CHOLERA_DISEASE_KEY),
                       diseases.get(MALARIA_DISEASE_KEY),
                       diseases.get(MEASLES_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto choleraFinalDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -402,7 +404,7 @@ public class TestPopulateAdministrativeService {
                   "Cholera Final Template Title",
                   FINAL,
                   List.of(diseases.get(CHOLERA_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
       InformationStatementTemplateDto choleraDraftDto =
           informationStatementTemplateService.createInformationStatementTemplate(
               new InformationStatementTemplateRequest(
@@ -410,7 +412,7 @@ public class TestPopulateAdministrativeService {
                   "Cholera Draft Template Title",
                   DRAFT,
                   List.of(diseases.get(CHOLERA_DISEASE_KEY)),
-                  createTemplateContent(false)));
+                  createTemplateContent()));
 
       Map<String, UUID> informationStatementTemplates = new LinkedHashMap<>();
       informationStatementTemplates.put(EMPTY_IST_KEY, emptyDto.id());
@@ -423,65 +425,58 @@ public class TestPopulateAdministrativeService {
     return Map.of();
   }
 
-  private TemplateContentDto createTemplateContent(boolean answered) {
-    Boolean closedAnswer = answered ? true : null;
-    String openAnswer = answered ? "Antworttext" : null;
+  private TemplateContentDto createTemplateContent() {
     return new TemplateContentDto(
         List.of(
             new TemplateSectionDto(
                 "1. Section Titel",
                 List.of(
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
-                            "1. Section, 1. Frage, keine Subelemente",
-                            closedAnswer,
-                            List.of(),
-                            null)),
+                        new TemplateAnamnesisQuestionDto(
+                            "1. Section, 1. Frage, keine Subelemente", List.of(), null),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 2. Frage, SubElementMultiSelect",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "1. Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "2. Antwortoption", closedAnswer)),
-                            null)),
+                                new TemplateSubElementMultiSelectDto("1. Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("2. Antwortoption")),
+                            null),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 3. Frage, SubElementText",
-                            closedAnswer,
                             List.of(),
-                            new TemplateSubElementTextDto("3. Frage, offene Angabe", openAnswer))),
+                            new TemplateSubElementTextDto("3. Frage, offene Angabe")),
+                        null,
+                        null),
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "1. Section, 4. Frage, kombiniert",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "1. Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "2. Antwortoption", closedAnswer)),
+                                new TemplateSubElementMultiSelectDto("1. Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("2. Antwortoption")),
                             new TemplateSubElementTextDto(
-                                "4. Frage, offene Angabe in Subelementen", openAnswer))))),
+                                "4. Frage, offene Angabe in Subelementen")),
+                        null,
+                        null),
+                    new TemplateSectionElementDto(
+                        null, new TemplateTextBlockDto("Textfeld\nmit Inhalt"), null),
+                    new TemplateSectionElementDto(
+                        null, null, new TemplateConfirmationDto("1. Section, Bestätigungsfeld")))),
             new TemplateSectionDto(
                 "2. Section Titel",
                 List.of(
                     new TemplateSectionElementDto(
-                        "option",
-                        new TemplateSectionElementDataDto(
+                        new TemplateAnamnesisQuestionDto(
                             "2. Section, 1. Frage",
-                            closedAnswer,
                             List.of(
-                                new TemplateSubElementMultiSelectDto(
-                                    "Eine Antwortoption", closedAnswer),
-                                new TemplateSubElementMultiSelectDto(
-                                    "Noch eine Antwortoption", closedAnswer)),
-                            new TemplateSubElementTextDto(
-                                "Sonstige Antwortoption", openAnswer)))))));
+                                new TemplateSubElementMultiSelectDto("Eine Antwortoption"),
+                                new TemplateSubElementMultiSelectDto("Noch eine Antwortoption")),
+                            new TemplateSubElementTextDto("Sonstige Antwortoption")),
+                        null,
+                        null)))));
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
index 91106ec0a..a8f2fd37e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TestPopulateProcedureService.java
@@ -5,23 +5,27 @@
 
 package de.eshg.travelmedicine.testhelper;
 
+import static de.eshg.lib.procedure.model.ProcedureStatusDto.*;
 import static de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT;
+import static de.eshg.travelmedicine.testhelper.TestHelperUtil.answerDocumentContent;
 
 import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto;
 import de.eshg.base.user.UserApi;
 import de.eshg.base.user.api.GetUsersResponse;
 import de.eshg.base.user.api.UserDto;
 import de.eshg.lib.keycloak.TechnicalGroup;
-import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.testhelper.ConditionalOnTestHelperEnabled;
 import de.eshg.travelmedicine.certificate.CertificateService;
 import de.eshg.travelmedicine.certificate.api.CertificateTypeDto;
 import de.eshg.travelmedicine.certificate.api.PostPutCertificateRequest;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
+import de.eshg.travelmedicine.document.api.DocumentContentDto;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.testhelper.api.CertificatePopulationDto;
 import de.eshg.travelmedicine.testhelper.api.CitizenPortalCredentialsDto;
+import de.eshg.travelmedicine.testhelper.api.InformationStatementPopulationDto;
 import de.eshg.travelmedicine.testhelper.api.InitialStepPopulationDto;
 import de.eshg.travelmedicine.testhelper.api.OtherServicePopulationDto;
 import de.eshg.travelmedicine.testhelper.api.PostPopulateProcedureRequest;
@@ -31,6 +35,7 @@ import de.eshg.travelmedicine.testhelper.api.VaccinationPopulationDto;
 import de.eshg.travelmedicine.vaccinationconsultation.CitizenAccessCodeUserClient;
 import de.eshg.travelmedicine.vaccinationconsultation.ProcedureStepService;
 import de.eshg.travelmedicine.vaccinationconsultation.VaccinationConsultationService;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
@@ -39,11 +44,13 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsult
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStepRepository;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultationRepository;
 import java.time.LocalDate;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
 import org.springframework.security.core.context.SecurityContext;
@@ -61,6 +68,7 @@ public class TestPopulateProcedureService {
   private final ProcedureStepService procedureStepService;
   private final ProcedureStepRepository procedureStepRepository;
   private final CertificateService certificateService;
+  private final InformationStatementService informationStatementService;
 
   private final CitizenAccessCodeUserClient citizenAccessCodeUserClient;
   private final UserApi userApi;
@@ -74,6 +82,7 @@ public class TestPopulateProcedureService {
       ProcedureStepService procedureStepService,
       ProcedureStepRepository procedureStepRepository,
       CertificateService certificateService,
+      InformationStatementService informationStatementService,
       UserApi userApi,
       CitizenAccessCodeUserClient citizenAccessCodeUserClient,
       TravelMedicineFeatureToggle featureToggle) {
@@ -82,6 +91,7 @@ public class TestPopulateProcedureService {
     this.procedureStepService = procedureStepService;
     this.procedureStepRepository = procedureStepRepository;
     this.certificateService = certificateService;
+    this.informationStatementService = informationStatementService;
     this.citizenAccessCodeUserClient = citizenAccessCodeUserClient;
     this.userApi = userApi;
     this.featureToggle = featureToggle;
@@ -95,6 +105,8 @@ public class TestPopulateProcedureService {
     UUID procedureId;
     Map<String, UUID> stepMap = new LinkedHashMap<>();
     Map<String, UUID> serviceMap = new LinkedHashMap<>();
+    Map<String, UUID> informationStatementMap;
+    UUID citizenUserId = null;
     CitizenPortalCredentialsDto citizenPortalCredentials = null;
 
     // 1. check request
@@ -105,17 +117,24 @@ public class TestPopulateProcedureService {
       PostCitizenVaccinationConsultationRequest citizenProcedureRequest =
           populateProcedureRequest.citizenProcedureData();
       procedureId = populateCitizenVaccinationConsultation(citizenProcedureRequest);
-      citizenPortalCredentials = createCredentials(procedureId, citizenProcedureRequest);
+      citizenUserId = getCitizenUserId(procedureId);
+      citizenPortalCredentials = createCredentials(citizenUserId, citizenProcedureRequest);
     } else {
       procedureId =
           populateEmployeeVaccinationConsultation(populateProcedureRequest.procedureData());
     }
 
-    // 3. add services
+    // 3. start procedure
+    if (isCitizenPortal
+        && Arrays.asList(OPEN, CLOSED).contains(populateProcedureRequest.targetState())) {
+      startProcedure(procedureId);
+    }
+
+    // 4. add services
     serviceMap.putAll(populateVaccinations(procedureId, populateProcedureRequest.vaccinations()));
     serviceMap.putAll(populateOtherServices(procedureId, populateProcedureRequest.otherServices()));
 
-    // 4. deal with initial procedure step
+    // 5. deal with initial procedure step
     InitialStepPopulationDto initialStep = populateProcedureRequest.initialStep();
     if (initialStep != null) {
       UUID initialStepId =
@@ -132,27 +151,31 @@ public class TestPopulateProcedureService {
       }
     }
 
-    // 5. populate steps
+    // 6. populate steps
     stepMap.putAll(
         populateSteps(procedureId, populateProcedureRequest.procedureSteps(), serviceMap));
-    // 6. cancel appointments
-    cancelAppointments(procedureId, stepMap, populateProcedureRequest.cancelSteps());
+    // 7. cancel appointments
+    cancelAppointments(procedureId, citizenUserId, stepMap, populateProcedureRequest.cancelSteps());
 
-    // 7. perform services
+    // 8. perform services
     executeVaccinations(procedureId, serviceMap, populateProcedureRequest.executeVaccinations());
     executeOtherServices(procedureId, serviceMap, populateProcedureRequest.executeOtherServices());
 
-    // 8. add certificates
+    // 9. add certificates
     populateCertificates(procedureId, populateProcedureRequest.certificates(), stepMap, serviceMap);
 
-    // 9. add information statements
-    populateInformationStatements(procedureId, populateProcedureRequest.informationStatements());
+    // 10. add information statements
+    informationStatementMap =
+        populateInformationStatements(
+            procedureId, citizenUserId, populateProcedureRequest.informationStatements());
 
-    // 10. close the procedure
-    changeProcedureStatus(procedureId, populateProcedureRequest.statusChange());
+    // 11. close the procedure
+    if (Objects.equals(CLOSED, populateProcedureRequest.targetState())) {
+      closeProcedure(procedureId);
+    }
 
     return new PostPopulateProcedureResponse(
-        procedureId, stepMap, serviceMap, citizenPortalCredentials);
+        procedureId, stepMap, serviceMap, informationStatementMap, citizenPortalCredentials);
   }
 
   private boolean isCitizenPortal(PostPopulateProcedureRequest createProcedureRequest) {
@@ -190,19 +213,27 @@ public class TestPopulateProcedureService {
     }
   }
 
+  private UUID getCitizenUserId(UUID procedureId) {
+    return vaccinationConsultationRepository
+        .findByExternalId(procedureId)
+        .orElseThrow()
+        .getCitizenUserId();
+  }
+
   private CitizenPortalCredentialsDto createCredentials(
-      UUID procedureId, PostCitizenVaccinationConsultationRequest citizenProcedureRequest) {
+      UUID citizenUserId, PostCitizenVaccinationConsultationRequest citizenProcedureRequest) {
     LocalDate dateOfBirth = citizenProcedureRequest.patient().dateOfBirth();
-    UUID citizenUserId =
-        vaccinationConsultationRepository
-            .findByExternalId(procedureId)
-            .orElseThrow()
-            .getCitizenUserId();
+
     CitizenAccessCodeUserDto citizenAccessCode =
         citizenAccessCodeUserClient.getCitizenAccessCode(citizenUserId);
     return new CitizenPortalCredentialsDto(citizenAccessCode.accessCode(), dateOfBirth);
   }
 
+  private void startProcedure(UUID procedureId) {
+    vaccinationConsultationService.acceptDraftVaccinationConsultation(
+        procedureId, new PatchAcceptDraftRequest(null));
+  }
+
   private Map<String, UUID> populateVaccinations(
       UUID procedureId, List<VaccinationPopulationDto> vaccinationPopulations) {
     Map<String, UUID> serviceMap = new LinkedHashMap<>();
@@ -213,8 +244,7 @@ public class TestPopulateProcedureService {
               throw new BadRequestException("Series are not supported by test data population");
             UUID vaccinationId =
                 vaccinationConsultationService
-                    .createServices(
-                        procedureId, null, List.of(population.request()), List.of()) // no a
+                    .createServices(procedureId, null, List.of(population.request()), List.of())
                     .getFirst();
             serviceMap.put(population.serviceKey(), vaccinationId);
           });
@@ -276,7 +306,7 @@ public class TestPopulateProcedureService {
   }
 
   private void cancelAppointments(
-      UUID procedureId, Map<String, UUID> stepMap, List<String> stepKeys) {
+      UUID procedureId, UUID citizenUserId, Map<String, UUID> stepMap, List<String> stepKeys) {
     if (stepKeys == null) {
       return;
     }
@@ -285,12 +315,7 @@ public class TestPopulateProcedureService {
       if (stepId == null) {
         throw new IllegalArgumentException("Unknown step key");
       }
-      UUID citizenUserId =
-          vaccinationConsultationRepository
-              .findByExternalId(procedureId)
-              .orElseThrow()
-              .getCitizenUserId();
-      vaccinationConsultationService.cancelAppointment(citizenUserId, procedureId, stepId);
+      vaccinationConsultationService.cancelAppointmentByCitizen(citizenUserId, procedureId, stepId);
     }
   }
 
@@ -348,18 +373,42 @@ public class TestPopulateProcedureService {
     }
   }
 
-  private void populateInformationStatements(
-      UUID procedureId, PostInformationStatementsRequest informationStatements) {
+  private Map<String, UUID> populateInformationStatements(
+      UUID procedureId,
+      UUID citizenUserId,
+      List<InformationStatementPopulationDto> informationStatementPopulations) {
+    Map<String, UUID> informationStatementMap = new LinkedHashMap<>();
     if (featureToggle.isNewFeatureEnabled(CITIZEN_PORTAL_INFORMATION_STATEMENT)
-        && informationStatements != null) {
-      vaccinationConsultationService.addInformationStatements(procedureId, informationStatements);
+        && informationStatementPopulations != null) {
+      informationStatementPopulations.forEach(
+          informationStatementPopulationDto -> {
+            UUID informationStatementId =
+                informationStatementService
+                    .addInformationStatements(
+                        procedureId,
+                        new PostInformationStatementsRequest(
+                            List.of(informationStatementPopulationDto.templateId())))
+                    .getFirst();
+
+            informationStatementMap.put(
+                informationStatementPopulationDto.key(), informationStatementId);
+
+            if (informationStatementPopulationDto.answered() != null
+                && informationStatementPopulationDto.answered()
+                && citizenUserId != null) {
+              DocumentContentDto documentContent =
+                  informationStatementService.getInformationStatementForCitizenPortal(
+                      citizenUserId, informationStatementId);
+              informationStatementService.patchInformationStatementForCitizenPortal(
+                  citizenUserId, informationStatementId, answerDocumentContent(documentContent));
+            }
+          });
     }
+    return informationStatementMap;
   }
 
-  private void changeProcedureStatus(UUID procedureId, ProcedureStatusDto procedureStatusDto) {
-    if (procedureStatusDto != null) {
-      vaccinationConsultationService.updateProcedureStatus(procedureId, procedureStatusDto);
-    }
+  private void closeProcedure(UUID procedureId) {
+    vaccinationConsultationService.updateProcedureStatus(procedureId, CLOSED);
   }
 
   private List<UUID> getPhysicians() {
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java
new file mode 100644
index 000000000..d88319c0e
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/InformationStatementPopulationDto.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.testhelper.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.util.UUID;
+
+@Schema(name = "InformationStatementPopulation")
+public record InformationStatementPopulationDto(
+    @NotNull UUID templateId, @NotBlank String key, Boolean answered) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
index 78216195d..8d54f6c14 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureRequest.java
@@ -7,7 +7,6 @@ package de.eshg.travelmedicine.testhelper.api;
 
 import de.eshg.lib.procedure.model.ProcedureStatusDto;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
-import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsultationRequest;
 import jakarta.validation.Valid;
 import java.util.List;
@@ -23,5 +22,5 @@ public record PostPopulateProcedureRequest(
     List<String> executeVaccinations,
     List<String> executeOtherServices,
     @Valid List<CertificatePopulationDto> certificates,
-    @Valid PostInformationStatementsRequest informationStatements,
-    ProcedureStatusDto statusChange) {}
+    @Valid List<InformationStatementPopulationDto> informationStatements,
+    ProcedureStatusDto targetState) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
index e027edd05..6a49a2336 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/api/PostPopulateProcedureResponse.java
@@ -14,4 +14,5 @@ public record PostPopulateProcedureResponse(
     @NotNull UUID procedureId,
     @Valid @NotNull Map<String, UUID> procedureStepsCreated,
     @Valid @NotNull Map<String, UUID> servicesCreated,
+    @Valid @NotNull Map<String, UUID> informationStatementsCreated,
     @Valid CitizenPortalCredentialsDto credentials) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
index 1b2c17a90..e4f31808a 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/AppointmentDetailsMapper.java
@@ -7,10 +7,12 @@ package de.eshg.travelmedicine.vaccinationconsultation;
 
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VcService;
 import java.time.LocalDate;
+import java.util.List;
 
 public class AppointmentDetailsMapper {
   private AppointmentDetailsMapper() {}
@@ -18,7 +20,8 @@ public class AppointmentDetailsMapper {
   public static GetAppointmentDetailsResponse mapToDetails(
       AppointmentSummaryDto appointmentSummary,
       PatientDto patientDto,
-      ProcedureStep procedureStep) {
+      ProcedureStep procedureStep,
+      List<InformationStatementSummaryDto> informationStatementSummaries) {
     String lastName = patientDto.lastName();
     String firstName = patientDto.firstName();
     LocalDate dateOfBirth = patientDto.dateOfBirth();
@@ -37,6 +40,7 @@ public class AppointmentDetailsMapper {
         firstName,
         dateOfBirth,
         isMedicalHistoryCompletelyAnswered,
-        citizenHasAnswered);
+        citizenHasAnswered,
+        informationStatementSummaries);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
index bd72b5aed..86c7e649d 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/CitizenAccessCodeUserClient.java
@@ -28,4 +28,8 @@ public class CitizenAccessCodeUserClient {
   public CitizenAccessCodeUserDto getCitizenAccessCode(UUID citizenUserId) {
     return citizenAccessCodeUserApi.getCitizenAccessCodeUser(citizenUserId);
   }
+
+  public void deleteCitizenAccessCodeUser(UUID citizenUserId) {
+    citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(citizenUserId);
+  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java
new file mode 100644
index 000000000..0a883d28a
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/InformationStatementSummaryMapper.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation;
+
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
+import java.util.List;
+
+public class InformationStatementSummaryMapper {
+
+  public static List<InformationStatementSummaryDto> mapToInterfaceType(
+      List<InformationStatement> informationStatements) {
+    return informationStatements.stream()
+        .map(
+            statement ->
+                new InformationStatementSummaryDto(
+                    statement.getId(), statement.getTitle(), statement.isCitizenHasAnswered()))
+        .toList();
+  }
+}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
index 5267effc9..3a4184e2f 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/PersonClient.java
@@ -9,16 +9,19 @@ import de.eshg.base.address.AddressDto;
 import de.eshg.base.address.DomesticAddressDto;
 import de.eshg.base.centralfile.PersonApi;
 import de.eshg.base.centralfile.api.DataOriginDto;
+import de.eshg.base.centralfile.api.DeleteFileStatesRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateRequest;
 import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse;
 import de.eshg.base.centralfile.api.person.ExternalAddPersonFileStateRequest;
-import de.eshg.base.centralfile.api.person.GetPersonDiffResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStateResponse;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesRequest;
 import de.eshg.base.centralfile.api.person.GetPersonFileStatesResponse;
+import de.eshg.base.centralfile.api.person.GetReferencePersonResponse;
 import de.eshg.base.centralfile.api.person.PersonDetailsDto;
 import de.eshg.base.centralfile.api.person.PutPersonRequest;
+import de.eshg.base.centralfile.api.person.SearchReferencePersonsResponse;
 import de.eshg.base.centralfile.api.person.SyncFileStateRequest;
+import de.eshg.base.centralfile.api.person.UpdateReferencePersonRequest;
 import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.ErrorCode;
 import de.eshg.rest.service.error.ErrorResponse;
@@ -26,6 +29,8 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonAddressDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonSyncDto;
 import jakarta.validation.Valid;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -113,9 +118,9 @@ public class PersonClient {
     }
   }
 
-  public UUID updatePersonInCentralFile(UUID id, PatientDto patient) {
+  public UUID updatePersonInCentralFile(UUID fileStateId, PatientDto patient) {
     GetPersonFileStatesResponse getPersonFileStatesResponse =
-        personApi.getPersonFileStates(new GetPersonFileStatesRequest(List.of(id)));
+        personApi.getPersonFileStates(new GetPersonFileStatesRequest(List.of(fileStateId)));
 
     AddPersonFileStateResponse personFileStateResponse =
         getPersonFileStatesResponse.personFileStates().getFirst();
@@ -124,15 +129,128 @@ public class PersonClient {
     PutPersonRequest putPersonRequest = createPutPersonRequest(patient, billingAddress);
 
     AddPersonFileStateResponse addPersonFileStateResponse =
-        personApi.updatePersonFileStateAndReference(id, putPersonRequest);
+        personApi.updatePersonFileStateAndReference(fileStateId, putPersonRequest);
     return addPersonFileStateResponse.id();
   }
 
+  public UUID createInternalReferencePerson(UUID fileStateId) {
+
+    GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(fileStateId);
+
+    PutPersonRequest putPersonRequest = mapToPutPersonRequest(personFromCentralFile);
+
+    AddPersonFileStateResponse addPersonFileStateResponse =
+        personApi.updatePersonFileStateAndReference(fileStateId, putPersonRequest);
+    return addPersonFileStateResponse.id();
+  }
+
+  public UUID updatePersonAndCreateFileState(UUID referencePersonId, UUID oldFileStateId) {
+    GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(oldFileStateId);
+    SearchReferencePersonsResponse searchReferencePersons =
+        personApi.searchReferencePersons(
+            personFromCentralFile.firstName(),
+            personFromCentralFile.lastName(),
+            personFromCentralFile.dateOfBirth());
+    GetReferencePersonResponse referencePerson =
+        searchReferencePersons.persons().stream()
+            .filter(p -> p.id().equals(referencePersonId))
+            .findFirst()
+            .orElseThrow(() -> new BadRequestException("Reference person not found."));
+    boolean dataAdded =
+        addEmailAndPhoneNumberToReferencePerson(referencePerson, personFromCentralFile);
+    PersonDetailsDto personDetailsDto = mapToPersonDetailsDto(referencePerson);
+    AddPersonFileStateResponse addPersonFileStateResponse;
+    if (dataAdded) {
+      UpdateReferencePersonRequest updateReferencePersonRequest =
+          new UpdateReferencePersonRequest(personDetailsDto, referencePerson.version());
+      addPersonFileStateResponse =
+          personApi.updateReferencePerson(referencePersonId, updateReferencePersonRequest);
+    } else {
+      AddPersonFileStateRequest addPersonRequest = mapToAddPeronRequest(referencePerson);
+      addPersonFileStateResponse = personApi.addPersonFileState(addPersonRequest);
+    }
+
+    return addPersonFileStateResponse.id();
+  }
+
+  private AddPersonFileStateRequest mapToAddPeronRequest(
+      GetReferencePersonResponse referencePerson) {
+    return new AddPersonFileStateRequest(
+        referencePerson.id(),
+        referencePerson.title(),
+        referencePerson.salutation(),
+        referencePerson.gender(),
+        referencePerson.firstName().trim(),
+        referencePerson.lastName().trim(),
+        referencePerson.dateOfBirth(),
+        referencePerson.nameAtBirth(),
+        referencePerson.placeOfBirth(),
+        referencePerson.countryOfBirth(),
+        referencePerson.emailAddresses(),
+        referencePerson.phoneNumbers(),
+        referencePerson.contactAddress(),
+        referencePerson.differentBillingAddress(),
+        DataOriginDto.MANUAL);
+  }
+
+  private boolean addEmailAndPhoneNumberToReferencePerson(
+      GetReferencePersonResponse referencePerson,
+      GetPersonFileStateResponse personFromCentralFile) {
+    boolean mailAdded =
+        addEmailsToReferencePerson(referencePerson, personFromCentralFile.emailAddresses());
+    boolean phoneNumberAdded =
+        addPhoneNumbersToReferencePerson(referencePerson, personFromCentralFile.phoneNumbers());
+    return (mailAdded || phoneNumberAdded);
+  }
+
+  private boolean addPhoneNumbersToReferencePerson(
+      GetReferencePersonResponse referencePerson, List<String> phoneNumbers) {
+    boolean phoneNumberAdded = false;
+    HashSet<String> referenceNumbers =
+        referencePerson.phoneNumbers().stream()
+            .map(this::normalizePhoneNumber)
+            .collect(Collectors.toCollection(HashSet::new));
+
+    for (String phoneNumber : phoneNumbers) {
+      String normalizedNumber = normalizePhoneNumber(phoneNumber);
+      if (!referenceNumbers.contains(normalizedNumber)) {
+        referencePerson.phoneNumbers().add(phoneNumber);
+        phoneNumberAdded = true;
+      }
+    }
+    return phoneNumberAdded;
+  }
+
+  private String normalizePhoneNumber(String phoneNumber) {
+
+    phoneNumber = phoneNumber.replaceAll("[^\\d.]", "");
+    if (phoneNumber.startsWith("00")) {
+      phoneNumber = phoneNumber.substring(2);
+    }
+    return phoneNumber;
+  }
+
+  private boolean addEmailsToReferencePerson(
+      GetReferencePersonResponse referencePerson, List<String> emails) {
+    boolean mailsAdded = false;
+    for (String email : emails) {
+      if (!referencePerson.emailAddresses().contains(email)) {
+        referencePerson.emailAddresses().add(email);
+        mailsAdded = true;
+      }
+    }
+    return mailsAdded;
+  }
+
   public PatientSync getPersonFromCentralFile(UUID id) {
     GetPersonFileStateResponse personFromCentralFile = personApi.getPersonFileState(id);
     return mapToPatientStatusDto(personFromCentralFile);
   }
 
+  public PatientDto getPatientFromCentralFile(UUID id) {
+    return getPersonFromCentralFile(id).patient();
+  }
+
   public Map<UUID, PatientDto> getPersonsFromCentralFile(List<UUID> ids) {
     if (ids.isEmpty()) {
       return Map.of();
@@ -149,9 +267,10 @@ public class PersonClient {
         .collect(Collectors.toMap(AddPersonFileStateResponse::id, PersonClient::mapToPatientDto));
   }
 
-  public long getPersonReferenceVersion(UUID fileStateId) {
-    GetPersonDiffResponse personDiff = personApi.getPersonDiff(fileStateId);
-    return personDiff.referenceVersion();
+  public void markExternalPersonForDeletion(UUID fileStateId) {
+    DeleteFileStatesRequest deleteFileStatesRequest =
+        new DeleteFileStatesRequest(new LinkedHashSet<>(List.of(fileStateId)));
+    personApi.markPersonFileStateForDeletion(deleteFileStatesRequest);
   }
 
   private PutPersonRequest createPutPersonRequest(PatientDto patient, AddressDto billingAddress) {
@@ -210,6 +329,44 @@ public class PersonClient {
         mapAddressToPerson(addPersonFileStateResponse.contactAddress()));
   }
 
+  private PutPersonRequest mapToPutPersonRequest(GetPersonFileStateResponse personFromCentralFile) {
+    return new PutPersonRequest(mapToPersonDetailsDto(personFromCentralFile));
+  }
+
+  private PersonDetailsDto mapToPersonDetailsDto(GetPersonFileStateResponse personFromCentralFile) {
+    return new PersonDetailsDto(
+        personFromCentralFile.title(),
+        personFromCentralFile.salutation(),
+        personFromCentralFile.gender(),
+        personFromCentralFile.firstName().trim(),
+        personFromCentralFile.lastName().trim(),
+        personFromCentralFile.dateOfBirth(),
+        personFromCentralFile.nameAtBirth(),
+        personFromCentralFile.placeOfBirth(),
+        personFromCentralFile.countryOfBirth(),
+        personFromCentralFile.emailAddresses(),
+        personFromCentralFile.phoneNumbers(),
+        personFromCentralFile.contactAddress(),
+        personFromCentralFile.differentBillingAddress());
+  }
+
+  private PersonDetailsDto mapToPersonDetailsDto(GetReferencePersonResponse referencePerson) {
+    return new PersonDetailsDto(
+        referencePerson.title(),
+        referencePerson.salutation(),
+        referencePerson.gender(),
+        referencePerson.firstName().trim(),
+        referencePerson.lastName().trim(),
+        referencePerson.dateOfBirth(),
+        referencePerson.nameAtBirth(),
+        referencePerson.placeOfBirth(),
+        referencePerson.countryOfBirth(),
+        referencePerson.emailAddresses(),
+        referencePerson.phoneNumbers(),
+        referencePerson.contactAddress(),
+        referencePerson.differentBillingAddress());
+  }
+
   private static PersonAddressDto mapAddressToPerson(de.eshg.base.address.AddressDto address) {
     if (address == null) {
       return null;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
index db11d556e..8da0d7289 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureAccessor.java
@@ -10,10 +10,10 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.travelmedicine.certificate.persistence.entity.Certificate;
 import de.eshg.travelmedicine.certificate.persistence.entity.CertificateRepository;
-import de.eshg.travelmedicine.medicalhistory.persistence.MedicalHistoryRepository;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatementRepository;
+import de.eshg.travelmedicine.document.informationstatement.persistence.InformationStatementRepository;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.MedicalHistoryRepository;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherServiceRepository;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
@@ -52,6 +52,15 @@ public class ProcedureAccessor {
     }
   }
 
+  public static class CheckIsDraft implements ProcedureCheck {
+    @Override
+    public void applyCheck(VaccinationConsultation vaccinationConsultation)
+        throws BadRequestException {
+      if (vaccinationConsultation.getProcedureStatus() != ProcedureStatus.DRAFT)
+        throw new BadRequestException("The procedure (vaccination consultation) is not draft.");
+    }
+  }
+
   public static class CheckCitizenUserId implements ProcedureCheck {
 
     private final UUID citizenUserId;
@@ -72,6 +81,7 @@ public class ProcedureAccessor {
   // frequently used checker series
   public static final List<ProcedureCheck> noChecks = Collections.emptyList();
   public static final List<ProcedureCheck> checkNotClosed = List.of(new CheckNotClosed());
+  public static final List<ProcedureCheck> checkIsDraft = List.of(new CheckIsDraft());
 
   private final VaccinationConsultationRepository vaccinationConsultationRepository;
   private final ProcedureStepRepository procedureStepRepository;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
index af8d6ab5d..daab59531 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepController.java
@@ -69,6 +69,6 @@ public class ProcedureStepController {
   @Operation(summary = "Cancel an appointment from employee portal.")
   @Transactional
   public void deleteAppointmentEp(@PathVariable("procedureStepId") UUID procedureStepId) {
-    procedureStepService.cancelAppointment(procedureStepId);
+    procedureStepService.cancelAppointmentByEmployee(procedureStepId);
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
index 838a628c0..7f5300160 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/ProcedureStepService.java
@@ -9,7 +9,9 @@ import de.eshg.lib.appointmentblock.api.AppointmentTypeDto;
 import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.rest.service.error.BadRequestException;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryFactory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.notification.NotificationService;
 import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplate;
 import de.eshg.travelmedicine.template.medicalhistorytemplate.persistence.entity.MedicalHistoryTemplateRepository;
 import de.eshg.travelmedicine.util.MappingUtil;
@@ -17,6 +19,7 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingType
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetProcedureStepServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAppointmentRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchEarliestDateRequest;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostProcedureStepRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.ProcedureStepServiceDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
@@ -46,6 +49,9 @@ public class ProcedureStepService {
 
   private final ProcedureAccessor procedureAccessor;
   private final AppointmentBookingTypeMapper appointmentBookingTypeMapper;
+  private final MedicalHistoryFactory medicalHistoryFactory;
+  private final NotificationService notificationService;
+  private final PersonClient personClient;
 
   public ProcedureStepService(
       ProcedureStepRepository procedureStepRepository,
@@ -53,13 +59,19 @@ public class ProcedureStepService {
       ServiceRepository serviceRepository,
       AppointmentService appointmentService,
       ProcedureAccessor procedureAccessor,
-      AppointmentBookingTypeMapper appointmentBookingTypeMapper) {
+      AppointmentBookingTypeMapper appointmentBookingTypeMapper,
+      MedicalHistoryFactory medicalHistoryFactory,
+      NotificationService notificationService,
+      PersonClient personClient) {
     this.procedureStepRepository = procedureStepRepository;
     this.medicalHistoryTemplateRepository = medicalHistoryTemplateRepository;
     this.serviceRepository = serviceRepository;
     this.appointmentService = appointmentService;
     this.procedureAccessor = procedureAccessor;
     this.appointmentBookingTypeMapper = appointmentBookingTypeMapper;
+    this.medicalHistoryFactory = medicalHistoryFactory;
+    this.notificationService = notificationService;
+    this.personClient = personClient;
   }
 
   public MedicalHistory createMedicalHistory(boolean followUp) {
@@ -67,12 +79,12 @@ public class ProcedureStepService {
         followUp
             ? medicalHistoryTemplateRepository.findByFollowUpFlagIsTrue()
             : medicalHistoryTemplateRepository.findByMainFlagIsTrue();
-    MedicalHistory medicalHistory = new MedicalHistory();
-    medicalHistory.setContent(template.map(MedicalHistoryTemplate::getContent).orElse("{}"));
-    return medicalHistory;
+    return medicalHistoryFactory.createMedicalHistory(
+        template.orElseThrow(
+            () -> new IllegalStateException("no suitable medical history template found")));
   }
 
-  public static Instant getAppointment(ProcedureStep ps) {
+  public static Instant getStartDateOrEarliestDateFromAppointment(ProcedureStep ps) {
     if (ps.getUserDefinedAppointment() != null) {
       return ps.getUserDefinedAppointment().getAppointmentStart();
     } else if (ps.getAppointment() != null) {
@@ -80,6 +92,19 @@ public class ProcedureStepService {
     } else return ps.getEarliestDate().atStartOfDay().toInstant(ZoneOffset.UTC);
   }
 
+  public static Instant getStartDateFromAppointment(ProcedureStep ps) {
+    if (ps.getUserDefinedAppointment() != null) {
+      return ps.getUserDefinedAppointment().getAppointmentStart();
+    } else if (ps.getAppointment() != null) {
+      return ps.getAppointment().getAppointmentStart();
+    } else return null;
+  }
+
+  public PatientDto patientOf(VaccinationConsultation vaccinationConsultation) {
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    return personClient.getPatientFromCentralFile(patientId);
+  }
+
   // --- end of util methods
 
   public UUID createProcedureStep(UUID externalId, PostProcedureStepRequest procedureStepRequest) {
@@ -135,6 +160,11 @@ public class ProcedureStepService {
     }
 
     vaccinationConsultation.getProcedureSteps().add(procedureStep);
+
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewFollowUpAppointment(patientOf(vaccinationConsultation));
+    }
+
     return procedureStep.getExternalId();
   }
 
@@ -211,6 +241,7 @@ public class ProcedureStepService {
     procedureStep.setAppointmentType(
         MappingUtil.mapEnum(AppointmentType.class, appointmentRequest.appointmentType()));
 
+    Instant previousAppointment = getStartDateFromAppointment(procedureStep);
     procedureStep.setAppointment(null);
     procedureStep.setUserDefinedAppointment(null);
 
@@ -227,6 +258,18 @@ public class ProcedureStepService {
           appointmentRequest.appointmentStart(),
           appointmentRequest.durationInMinutes());
     }
+
+    Instant newAppointment = getStartDateFromAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      if (previousAppointment != null)
+        notificationService.notifyRebookedByEmployee(
+            patientOf(vaccinationConsultation), previousAppointment, newAppointment);
+      else
+        notificationService.notifyBookedByEmployee(
+            patientOf(vaccinationConsultation), newAppointment);
+    }
   }
 
   private boolean checkForAppointmentChanges(
@@ -288,7 +331,7 @@ public class ProcedureStepService {
     procedureStep.setEarliestDate(patchEarliestDateRequest.earliestDate());
   }
 
-  public void cancelAppointment(UUID procedureStepId) {
+  public void cancelAppointmentByEmployee(UUID procedureStepId) {
     ProcedureStep procedureStep =
         procedureAccessor.accessProcedureStep(
             procedureStepId, null, List.of(new ProcedureAccessor.CheckNotClosed()));
@@ -300,6 +343,15 @@ public class ProcedureStepService {
       throw new BadRequestException(
           "It is only possible to cancel appointments of a procedure created in citizen portal.");
     }
+
+    Instant cancelledAppointment =
+        ProcedureStepService.getStartDateOrEarliestDateFromAppointment(procedureStep);
     appointmentService.cancelAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyCancelledByEmployee(
+          patientOf(vaccinationConsultation), cancelledAppointment);
+    }
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
index 7c8eb89bb..a79072b01 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationController.java
@@ -10,14 +10,18 @@ import de.eshg.rest.service.security.config.BaseUrls;
 import de.eshg.travelmedicine.certificate.CertificateService;
 import de.eshg.travelmedicine.certificate.api.GetCertificatesResponse;
 import de.eshg.travelmedicine.certificate.api.PostPutCertificateRequest;
+import de.eshg.travelmedicine.document.informationstatement.InformationStatementService;
+import de.eshg.travelmedicine.document.medicalhistory.MedicalHistoryService;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeature;
 import de.eshg.travelmedicine.featuretoggle.TravelMedicineFeatureToggle;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentOverviewResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAssignableServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAvailableAppointmentsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.GetInformationStatementsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetStepsWithAppliedServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchServiceAssignmentRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationPatientRequest;
@@ -67,6 +71,7 @@ public class VaccinationConsultationController {
   public static final String ASSIGNABLE_SERVICES_URL = "/assignable-services";
   public static final String ASSIGN_STEP_URL = "/assign-step";
   public static final String UNASSIGN_STEP_URL = "/unassign-step";
+  public static final String ACCEPT_DRAFT_URL = "/accept-draft";
   public static final String CERTIFICATES_URL = "/certificates";
   public static final String MEDICAL_HISTORY_URL = "/medical-histories";
   public static final String STEPS_WITH_APPLIED_SERVICES = "/stepsWithAppliedServices";
@@ -77,16 +82,22 @@ public class VaccinationConsultationController {
   private final VaccinationConsultationService vaccinationConsultationService;
   private final ProcedureStepService procedureStepService;
   private final CertificateService certificateService;
+  private final InformationStatementService informationStatementService;
+  private final MedicalHistoryService medicalHistoryService;
 
   public VaccinationConsultationController(
       VaccinationConsultationService vaccinationConsultationService,
       ProcedureStepService procedureStepService,
       CertificateService certificateService,
-      TravelMedicineFeatureToggle featureToggle) {
+      TravelMedicineFeatureToggle featureToggle,
+      InformationStatementService informationStatementService,
+      MedicalHistoryService medicalHistoryService) {
     this.vaccinationConsultationService = vaccinationConsultationService;
     this.procedureStepService = procedureStepService;
     this.certificateService = certificateService;
     this.featureToggle = featureToggle;
+    this.informationStatementService = informationStatementService;
+    this.medicalHistoryService = medicalHistoryService;
   }
 
   @GetMapping(path = APPOINTMENT_OVERVIEW)
@@ -95,10 +106,8 @@ public class VaccinationConsultationController {
           "Get list of all procedure appointment summaries in a time range, sorted by appointment date")
   @Transactional(readOnly = true)
   public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(
-      @RequestParam(name = "dateRangeStart") LocalDate dateRangeStart,
-      @RequestParam(name = "dateRangeEnd") LocalDate dateRangeEnd) {
-    return vaccinationConsultationService.getAllProcedureAppointmentSummaries(
-        dateRangeStart, dateRangeEnd);
+      @RequestParam(name = "date") LocalDate date) {
+    return vaccinationConsultationService.getAllProcedureAppointmentSummaries(date);
   }
 
   @PostMapping()
@@ -144,6 +153,23 @@ public class VaccinationConsultationController {
     return vaccinationConsultationService.getVaccinationConsultationDetails(procedureId);
   }
 
+  @DeleteMapping(path = "/{procedureId}")
+  @Operation(summary = "Aboard a draft vaccination consultation")
+  @Transactional
+  public void abortDraftVaccinationConsultation(@PathVariable("procedureId") UUID procedureId) {
+    vaccinationConsultationService.abortDraftVaccinationConsultation(procedureId);
+  }
+
+  @PatchMapping(path = "/{procedureId}" + ACCEPT_DRAFT_URL)
+  @Operation(summary = "Accept a draft vaccination consultation")
+  @Transactional
+  public void acceptDraftVaccinationConsultation(
+      @PathVariable("procedureId") UUID procedureId,
+      @Valid @RequestBody PatchAcceptDraftRequest acceptDraftRequest) {
+    vaccinationConsultationService.acceptDraftVaccinationConsultation(
+        procedureId, acceptDraftRequest);
+  }
+
   @Operation(summary = "Search VaccinationConsultation, max. 50 results.")
   @GetMapping("")
   @Transactional(readOnly = true)
@@ -261,7 +287,7 @@ public class VaccinationConsultationController {
   @Transactional
   public GetMedicalHistoriesResponse getMedicalHistories(
       @PathVariable("procedureId") UUID procedureId) {
-    return vaccinationConsultationService.getMedicalHistories(procedureId);
+    return medicalHistoryService.getMedicalHistoriesForEmployeePortal(procedureId);
   }
 
   @GetMapping(path = "/{procedureId}" + STEPS_WITH_APPLIED_SERVICES)
@@ -290,6 +316,16 @@ public class VaccinationConsultationController {
     vaccinationConsultationService.updateProcedureStatus(procedureId, request);
   }
 
+  @GetMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL)
+  @Operation(summary = "Get information statements for this VaccinationConsultation.")
+  @Transactional
+  public GetInformationStatementsResponse getInformationStatements(
+      @PathVariable("procedureId") UUID procedureId) {
+    featureToggle.assertNewFeatureIsEnabled(
+        TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
+    return informationStatementService.getInformationStatementsForEmployeePortal(procedureId);
+  }
+
   @PostMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL)
   @Operation(summary = "Add information statements to a procedure")
   @Transactional
@@ -298,7 +334,7 @@ public class VaccinationConsultationController {
       @Valid @RequestBody PostInformationStatementsRequest request) {
     featureToggle.assertNewFeatureIsEnabled(
         TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
-    vaccinationConsultationService.addInformationStatements(procedureId, request);
+    informationStatementService.addInformationStatements(procedureId, request);
   }
 
   @DeleteMapping(path = "/{procedureId}" + INFORMATION_STATEMENT_URL + "/{informationStatementId}")
@@ -309,7 +345,7 @@ public class VaccinationConsultationController {
       @PathVariable("informationStatementId") UUID informationStatementId) {
     featureToggle.assertNewFeatureIsEnabled(
         TravelMedicineFeature.CITIZEN_PORTAL_INFORMATION_STATEMENT);
-    vaccinationConsultationService.deleteInformationStatement(procedureId, informationStatementId);
+    informationStatementService.deleteInformationStatement(procedureId, informationStatementId);
   }
 
   @PutMapping("/{procedureId}" + SYNC_PERSON_URL)
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
index 2ef3a1073..55f5f51c3 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationDetailsMapper.java
@@ -13,7 +13,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingType
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentSummaryDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.CreatedByUserTypeDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PersonSyncDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.ServicePlanEntryDto;
@@ -21,7 +20,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.ServiceStatusDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelInformationDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTimeUnitDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTypeDto;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ProcedureStep;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.ServicePlanEntry;
@@ -43,13 +41,10 @@ import org.springframework.stereotype.Component;
 
 @Component
 public class VaccinationConsultationDetailsMapper {
-  private final InformationStatementMapper informationStatementMapper;
   private final AppointmentBookingTypeMapper appointmentBookingTypeMapper;
 
   public VaccinationConsultationDetailsMapper(
-      InformationStatementMapper informationStatementMapper,
       AppointmentBookingTypeMapper appointmentBookingTypeMapper) {
-    this.informationStatementMapper = informationStatementMapper;
     this.appointmentBookingTypeMapper = appointmentBookingTypeMapper;
   }
 
@@ -58,8 +53,7 @@ public class VaccinationConsultationDetailsMapper {
       PatientDto patientDto,
       PersonSyncDto personSync,
       ProcedureStep initialProcedureStep,
-      List<ServicePlanEntry> servicePlan,
-      List<InformationStatement> informationStatements) {
+      List<ServicePlanEntry> servicePlan) {
 
     return new GetVaccinationConsultationDetailsResponse(
         vaccinationConsultation.getExternalId(),
@@ -69,8 +63,7 @@ public class VaccinationConsultationDetailsMapper {
         mapTravelInformationToInterfaceType(vaccinationConsultation),
         MappingUtil.mapEnum(CreatedByUserTypeDto.class, vaccinationConsultation.getCreatedBy()),
         mapToAppointmentSummaryInterfaceType(initialProcedureStep),
-        mapServicePlanToToInterfaceType(servicePlan),
-        mapInformationStatementsToInterfaceType(informationStatements));
+        mapServicePlanToToInterfaceType(servicePlan));
   }
 
   private List<ServicePlanEntryDto> mapServicePlanToToInterfaceType(
@@ -228,10 +221,4 @@ public class VaccinationConsultationDetailsMapper {
         vc.getTravelTimeAmount(),
         MappingUtil.mapEnum(TravelTimeUnitDto.class, vc.getTravelTimeUnit()));
   }
-
-  private List<InformationStatementDto> mapInformationStatementsToInterfaceType(
-      List<InformationStatement> informationStatements) {
-    return informationStatementMapper.mapInformationStatementsToInterfaceType(
-        informationStatements);
-  }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
index cca85fd4b..d7069c008 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationMapper.java
@@ -54,7 +54,9 @@ public class VaccinationConsultationMapper {
     vaccinationConsultation.setTravelTimeAmount(travelInformation.travelTimeAmount());
     vaccinationConsultation.setTravelTimeUnit(
         MappingUtil.mapEnum(TravelTimeUnit.class, travelInformation.travelTimeUnit()));
-    vaccinationConsultation.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+    ProcedureStatus procedureStatus =
+        userType == CreatedByUserType.CITIZEN_PORTAL ? ProcedureStatus.DRAFT : ProcedureStatus.OPEN;
+    vaccinationConsultation.updateProcedureStatus(procedureStatus, clock, auditLogger);
     vaccinationConsultation.setCreatedBy(userType);
 
     VaccinationConsultationTask vaccinationConsultationTask = new VaccinationConsultationTask();
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
index 7aad99d7d..4310eccc7 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/VaccinationConsultationService.java
@@ -5,12 +5,9 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation;
 
-import static de.eshg.travelmedicine.medicalhistory.MedicalHistoryHelper.isMedicalHistoryCompletelyAnswered;
 import static de.eshg.travelmedicine.util.MappingUtil.mapEnum;
 import static de.eshg.travelmedicine.util.TravelMedicineProgressEntryType.PERSON_SYNCHRONIZED;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto;
 import de.eshg.lib.appointmentblock.AppointmentTypeMapper;
 import de.eshg.lib.appointmentblock.api.AppointmentDto;
@@ -27,14 +24,7 @@ import de.eshg.rest.service.error.BadRequestException;
 import de.eshg.rest.service.error.NotFoundException;
 import de.eshg.rest.service.security.CurrentUserHelper;
 import de.eshg.travelmedicine.citizenpublic.api.PostCitizenVaccinationConsultationRequest;
-import de.eshg.travelmedicine.medicalhistory.MedicalHistoryMapper;
-import de.eshg.travelmedicine.medicalhistory.MedicalHistoryService;
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryContentDto;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
 import de.eshg.travelmedicine.notification.NotificationService;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplate;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateRepository;
-import de.eshg.travelmedicine.template.informationstatementtemplate.persistence.entity.InformationStatementTemplateState;
 import de.eshg.travelmedicine.util.MappingUtil;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppliedServiceDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.AppointmentBookingTypeDto;
@@ -46,15 +36,15 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.GetAppointmentOverview
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAssignableServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetAvailableAppointmentsResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetCitizenAppointmentOverviewResponse;
-import de.eshg.travelmedicine.vaccinationconsultation.api.GetMedicalHistoriesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetStepsWithAppliedServicesResponse;
 import de.eshg.travelmedicine.vaccinationconsultation.api.GetVaccinationConsultationDetailsResponse;
+import de.eshg.travelmedicine.vaccinationconsultation.api.InformationStatementSummaryDto;
+import de.eshg.travelmedicine.vaccinationconsultation.api.PatchAcceptDraftRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationPatientRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationConsultationTravelDetailsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatchVaccinationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PatientDto;
-import de.eshg.travelmedicine.vaccinationconsultation.api.PostInformationStatementsRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostOtherServiceRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationConsultationRequest;
 import de.eshg.travelmedicine.vaccinationconsultation.api.PostVaccinationRequest;
@@ -66,7 +56,6 @@ import de.eshg.travelmedicine.vaccinationconsultation.api.TravelTypeDto;
 import de.eshg.travelmedicine.vaccinationconsultation.api.VaccinationConsultationSearchDto;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.AppointmentOverviewEntry;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.CreatedByUserType;
-import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.InformationStatement;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.OtherService;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.Person;
 import de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.PersonRepository;
@@ -97,15 +86,18 @@ import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.HttpClientErrorException;
 
 @Service
 public class VaccinationConsultationService {
+  private static final Logger log = LoggerFactory.getLogger(VaccinationConsultationService.class);
   private final VaccinationConsultationRepository vaccinationConsultationRepository;
   private final ProcedureStepRepository procedureStepRepository;
   private final ProcedureStepService procedureStepService;
-  private final MedicalHistoryService medicalHistoryService;
 
   private final VcServiceService vcServiceService;
   private final ServiceRepository serviceRepository;
@@ -126,7 +118,6 @@ public class VaccinationConsultationService {
   private static final String UPDATE_OUTDATED_PERSON =
       "The patient update failed. Is the person data up-to-date?";
   private final ProcedureAccessor procedureAccessor;
-  private final InformationStatementTemplateRepository informationStatementTemplateRepository;
   private final NotificationService notificationService;
   private final PersonRepository personRepository;
 
@@ -134,7 +125,6 @@ public class VaccinationConsultationService {
       VaccinationConsultationRepository vaccinationConsultationRepository,
       ProcedureStepRepository procedureStepRepository,
       ProcedureStepService procedureStepService,
-      MedicalHistoryService medicalHistoryService,
       VcServiceService vcServiceService,
       ServiceRepository serviceRepository,
       AppointmentService appointmentService,
@@ -147,13 +137,11 @@ public class VaccinationConsultationService {
       Clock clock,
       AuditLogger auditLogger,
       ProcedureAccessor procedureAccessor,
-      InformationStatementTemplateRepository informationStatementTemplateRepository,
       NotificationService notificationService,
       PersonRepository personRepository) {
     this.vaccinationConsultationRepository = vaccinationConsultationRepository;
     this.procedureStepRepository = procedureStepRepository;
     this.procedureStepService = procedureStepService;
-    this.medicalHistoryService = medicalHistoryService;
     this.vcServiceService = vcServiceService;
     this.serviceRepository = serviceRepository;
     this.appointmentService = appointmentService;
@@ -166,11 +154,15 @@ public class VaccinationConsultationService {
     this.clock = clock;
     this.auditLogger = auditLogger;
     this.procedureAccessor = procedureAccessor;
-    this.informationStatementTemplateRepository = informationStatementTemplateRepository;
     this.notificationService = notificationService;
     this.personRepository = personRepository;
   }
 
+  public PatientDto patientOf(VaccinationConsultation vaccinationConsultation) {
+    UUID patientId = vaccinationConsultation.getPatientIdsFromCentralFile().getFirst();
+    return personClient.getPatientFromCentralFile(patientId);
+  }
+
   public UUID createProcedure(PostVaccinationConsultationRequest request) {
     validatePostVaccinationConsultationRequest(request);
 
@@ -228,19 +220,21 @@ public class VaccinationConsultationService {
     vaccinationConsultationRepository.save(vaccinationConsultation);
     procedureStepRepository.save(initialProcedureStep);
 
-    notificationService.onNewCitizenProcedure(
-        citizenAccessCodeUser.accessCode(), request.patient(), initialProcedureStep);
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyNewCitizenProcedure(
+          request.patient(),
+          initialProcedureStep.getAppointment().getAppointmentStart(),
+          citizenAccessCodeUser.accessCode());
+    }
 
     return vaccinationConsultation.getExternalId();
   }
 
-  public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(
-      LocalDate dateRangeStart, LocalDate dateRangeEnd) {
-    Instant start = dateRangeStart.atStartOfDay(clock.getZone()).toInstant();
-    Instant end = dateRangeEnd.atTime(LocalTime.MAX).atZone(clock.getZone()).toInstant();
+  public GetAppointmentOverviewResponse getAllProcedureAppointmentSummaries(LocalDate date) {
+    Instant startOfDay = date.atStartOfDay(clock.getZone()).toInstant();
+    Instant endOfDay = date.atTime(LocalTime.MAX).atZone(clock.getZone()).toInstant();
     List<AppointmentOverviewEntry> appointmentOverview =
-        vaccinationConsultationRepository.findAppointmentOverview(
-            start, end, dateRangeStart, dateRangeEnd);
+        vaccinationConsultationRepository.findAppointmentOverview(startOfDay, endOfDay, date);
     List<UUID> cfsIds =
         appointmentOverview.stream()
             .map(AppointmentOverviewEntry::centralFileStateId)
@@ -257,7 +251,6 @@ public class VaccinationConsultationService {
     if (request.appointmentBookingType() == AppointmentBookingTypeDto.APPOINTMENT_BLOCK) {
       appointmentService.createBlockAppointmentForStep(
           initialProcedureStep, request.appointmentStart(), request.durationInMinutes());
-
     } else if (request.appointmentBookingType() == AppointmentBookingTypeDto.USER_DEFINED) {
       appointmentService.createUserDefinedAppointment(
           initialProcedureStep, request.appointmentStart(), request.durationInMinutes());
@@ -317,6 +310,9 @@ public class VaccinationConsultationService {
   public void updatePatient(UUID externalId, PatchVaccinationConsultationPatientRequest request) {
     VaccinationConsultation vaccinationConsultation =
         procedureAccessor.accessProcedure(externalId, ProcedureAccessor.checkNotClosed);
+    if (vaccinationConsultation.getProcedureStatus() == ProcedureStatus.DRAFT) {
+      throw new BadRequestException("Can't update person in draft status.");
+    }
 
     try {
       UUID patientIdFromCentralFile =
@@ -364,16 +360,12 @@ public class VaccinationConsultationService {
             .findInitialProcedureStep(externalId)
             .orElseThrow(() -> new IllegalStateException("No initial procedure step available"));
 
-    List<InformationStatement> informationStatements =
-        vaccinationConsultation.getInformationStatements();
-
     return vaccinationConsultationDetailsMapper.toInterfaceType(
         vaccinationConsultation,
         patientFromCentralFile.patient(),
         patientFromCentralFile.personSync(),
         initialProcedureStep,
-        servicePlan,
-        informationStatements);
+        servicePlan);
   }
 
   public GetAvailableAppointmentsResponse getAllAvailableAppointments(UUID procedureId) {
@@ -559,51 +551,6 @@ public class VaccinationConsultationService {
     return null;
   }
 
-  public GetMedicalHistoriesResponse getMedicalHistories(UUID procedureId) {
-    VaccinationConsultation vaccinationConsultation =
-        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.noChecks);
-
-    return medicalHistoryService.getMedicalHistories(vaccinationConsultation);
-  }
-
-  public void addInformationStatements(UUID procedureId, PostInformationStatementsRequest request) {
-    VaccinationConsultation vaccinationConsultation =
-        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkNotClosed);
-
-    List<InformationStatement> newStatements =
-        request.templateIds().stream()
-            .map(
-                templateID -> {
-                  InformationStatementTemplate template =
-                      informationStatementTemplateRepository
-                          .findById(templateID)
-                          .orElseThrow(
-                              () -> new NotFoundException("No such template: " + templateID));
-                  if (template.getState() != InformationStatementTemplateState.FINAL)
-                    throw new BadRequestException(
-                        "The template can't be used until it's in its FINAL state.");
-                  return template;
-                })
-            .map(
-                template ->
-                    new InformationStatement(template.getTitle(), "content from the template"))
-            .toList();
-
-    vaccinationConsultation.getInformationStatements().addAll(newStatements);
-    newStatements.forEach(s -> s.setVaccinationConsultation(vaccinationConsultation));
-  }
-
-  public void deleteInformationStatement(UUID procedureId, UUID informationStatementId) {
-    InformationStatement informationStatement =
-        procedureAccessor.accessInformationStatement(
-            informationStatementId, procedureId, ProcedureAccessor.checkNotClosed);
-
-    VaccinationConsultation vaccinationConsultation =
-        informationStatement.getVaccinationConsultation();
-
-    vaccinationConsultation.getInformationStatements().remove(informationStatement);
-  }
-
   private static String assembleServiceDescription(VcService service) {
     return switch (service) {
       case Vaccination vaccination ->
@@ -643,7 +590,7 @@ public class VaccinationConsultationService {
 
                   return new StepWithAppliedServicesDto(
                       procedureStep.getId(),
-                      ProcedureStepService.getAppointment(procedureStep),
+                      ProcedureStepService.getStartDateOrEarliestDateFromAppointment(procedureStep),
                       appliedServices);
                 })
             .sorted(Comparator.comparing(StepWithAppliedServicesDto::appointmentDateTime))
@@ -800,51 +747,16 @@ public class VaccinationConsultationService {
     AppointmentSummaryDto summaryDto =
         vaccinationConsultationDetailsMapper.mapToAppointmentSummaryInterfaceType(procedureStep);
 
-    return AppointmentDetailsMapper.mapToDetails(summaryDto, patient, procedureStep);
-  }
-
-  public MedicalHistoryContentDto getMedicalHistory(
-      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
-    ProcedureStep procedureStep =
-        procedureAccessor.accessProcedureStep(
-            procedureStepId,
-            procedureId,
-            List.of(
-                new ProcedureAccessor.CheckNotClosed(),
-                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
-
-    return MedicalHistoryMapper.contentToInterfaceType(procedureStep.getMedicalHistory());
-  }
-
-  public void patchMedicalHistory(
-      UUID citizenUserId,
-      UUID procedureId,
-      UUID procedureStepId,
-      MedicalHistoryContentDto patchMedicalHistoryContent) {
-    ProcedureStep procedureStep =
-        procedureAccessor.accessProcedureStep(
-            procedureStepId,
-            procedureId,
-            List.of(
-                new ProcedureAccessor.CheckNotClosed(),
-                new ProcedureAccessor.CheckCitizenUserId(citizenUserId)));
-    MedicalHistory medicalHistory = procedureStep.getMedicalHistory();
-    if (medicalHistory.isCitizenHasAnswered()) {
-      throw new BadRequestException("Medical history already answered by citizen.");
-    }
+    List<InformationStatementSummaryDto> informationStatementSummaries =
+        InformationStatementSummaryMapper.mapToInterfaceType(
+            procedureStep.getVaccinationConsultation().getInformationStatements());
 
-    ObjectMapper objectMapper = new ObjectMapper();
-    try {
-      medicalHistory.setContent(objectMapper.writeValueAsString(patchMedicalHistoryContent));
-    } catch (JsonProcessingException e) {
-      throw new BadRequestException("Content does not match required structure");
-    }
-    medicalHistory.setCompletelyAnswered(
-        isMedicalHistoryCompletelyAnswered(patchMedicalHistoryContent));
-    medicalHistory.setCitizenHasAnswered(true);
+    return AppointmentDetailsMapper.mapToDetails(
+        summaryDto, patient, procedureStep, informationStatementSummaries);
   }
 
-  public void cancelAppointment(UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
+  public void cancelAppointmentByCitizen(
+      UUID citizenUserId, UUID procedureId, UUID procedureStepId) {
     ProcedureStep procedureStep =
         procedureAccessor.accessProcedureStep(
             procedureStepId,
@@ -856,10 +768,17 @@ public class VaccinationConsultationService {
       throw new BadRequestException(
           "Appointment has accomplished services and cannot be cancelled.");
     }
+    Instant cancelledAppointment = ProcedureStepService.getStartDateFromAppointment(procedureStep);
     appointmentService.cancelAppointment(procedureStep);
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      notificationService.notifyCancelledByCitizen(
+          patientOf(vaccinationConsultation), cancelledAppointment);
+    }
   }
 
-  public void bookCitizenAppointment(
+  public void bookCitizenAppointmentByCitizen(
       UUID citizenUserId, UUID procedureId, UUID procedureStepId, AppointmentDto appointmentDto) {
 
     ProcedureStep procedureStep =
@@ -873,16 +792,20 @@ public class VaccinationConsultationService {
       throw new BadRequestException(
           "Appointment has accomplished services and cannot be rebooked.");
     }
+    Instant newAppointmentStart = appointmentDto.start();
     if (procedureStep.getEarliestDate() != null) {
       if (procedureStep
           .getEarliestDate()
           .atStartOfDay(clock.getZone())
           .toInstant()
-          .isAfter(appointmentDto.start())) {
+          .isAfter(newAppointmentStart)) {
         throw new BadRequestException(
             "Appointment has accomplished services and cannot be rebooked.");
       }
     }
+
+    Instant previousAppointmentStart =
+        ProcedureStepService.getStartDateFromAppointment(procedureStep);
     boolean rebook = false;
     if (procedureStep.getAppointment() != null
         || procedureStep.getUserDefinedAppointment() != null) {
@@ -890,14 +813,13 @@ public class VaccinationConsultationService {
       procedureStep.setAppointment(null);
       procedureStep.setUserDefinedAppointment(null);
     }
-    int remainingBookings = procedureStep.getBookingsRemaining();
 
+    int remainingBookings = procedureStep.getBookingsRemaining();
     if (remainingBookings > 0) {
       appointmentService.createBlockAppointmentForStep(
           procedureStep,
-          appointmentDto.start(),
-          Math.toIntExact(
-              ChronoUnit.MINUTES.between(appointmentDto.start(), appointmentDto.end())));
+          newAppointmentStart,
+          Math.toIntExact(ChronoUnit.MINUTES.between(newAppointmentStart, appointmentDto.end())));
 
     } else {
       throw new BadRequestException("No more bookings available. 2 rebookings max. allowed.");
@@ -906,5 +828,64 @@ public class VaccinationConsultationService {
     if (rebook) {
       procedureStep.setBookingsRemaining(remainingBookings - 1);
     }
+
+    VaccinationConsultation vaccinationConsultation = procedureStep.getVaccinationConsultation();
+    if (vaccinationConsultation.getCreatedBy() == CreatedByUserType.CITIZEN_PORTAL) {
+      if (rebook) {
+        notificationService.notifyRebookedByCitizen(
+            patientOf(vaccinationConsultation), previousAppointmentStart, newAppointmentStart);
+      } else {
+        notificationService.notifyBookedByCitizen(
+            patientOf(vaccinationConsultation), newAppointmentStart);
+      }
+    }
+  }
+
+  public void abortDraftVaccinationConsultation(UUID procedureId) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkIsDraft);
+    UUID centralFileStateId =
+        vaccinationConsultation.getRelatedPersons().getFirst().getCentralFileStateId();
+    personClient.markExternalPersonForDeletion(centralFileStateId);
+    if (vaccinationConsultation.getCitizenUserId() != null)
+      try {
+        citizenAccessCodeUserClient.deleteCitizenAccessCodeUser(
+            vaccinationConsultation.getCitizenUserId());
+      } catch (Exception e) {
+        log.warn("Error while deleting citizen access code user.", e);
+      }
+    List<VcService> services =
+        serviceRepository.findAllByVaccinationConsultationExternalIdOrderById(
+            vaccinationConsultation.getExternalId());
+    serviceRepository.deleteAll(services);
+    procedureStepRepository.deleteAll(vaccinationConsultation.getProcedureSteps());
+    vaccinationConsultationRepository.deleteById(vaccinationConsultation.getId());
+  }
+
+  public void acceptDraftVaccinationConsultation(
+      UUID procedureId, PatchAcceptDraftRequest acceptDraftRequest) {
+    VaccinationConsultation vaccinationConsultation =
+        procedureAccessor.accessProcedure(procedureId, ProcedureAccessor.checkIsDraft);
+
+    Person person = vaccinationConsultation.getRelatedPersons().getFirst();
+    UUID newFileState;
+    if (acceptDraftRequest.referencePersonId() == null) {
+      newFileState = createInternalReferencePerson(person.getCentralFileStateId());
+    } else {
+      newFileState =
+          personClient.updatePersonAndCreateFileState(
+              acceptDraftRequest.referencePersonId(), person.getCentralFileStateId());
+    }
+    person.setCentralFileStateId(newFileState);
+    vaccinationConsultation.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger);
+  }
+
+  private UUID createInternalReferencePerson(UUID fileStateId) {
+
+    try {
+      return personClient.createInternalReferencePerson(fileStateId);
+    } catch (HttpClientErrorException.BadRequest e) {
+      throw new BadRequestException(e.getMessage());
+    }
   }
 }
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
index 77d8fe9cd..73529a2ae 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetAppointmentDetailsResponse.java
@@ -9,6 +9,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
+import java.util.List;
 
 public record GetAppointmentDetailsResponse(
     @NotNull @Valid AppointmentSummaryDto summaryDto,
@@ -18,4 +19,5 @@ public record GetAppointmentDetailsResponse(
     @NotBlank String firstName,
     @NotNull LocalDate dateOfBirth,
     @NotNull boolean isMedicalHistoryCompletelyAnswered,
-    @NotNull boolean citizenHasAnswered) {}
+    @NotNull boolean medicalHistoryCitizenHasAnswered,
+    @NotNull @Valid List<InformationStatementSummaryDto> informationStatementSummaries) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java
new file mode 100644
index 000000000..c5dc381b3
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetInformationStatementsResponse.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import de.eshg.travelmedicine.document.informationstatement.api.InformationStatementDto;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.UUID;
+
+public record GetInformationStatementsResponse(
+    @NotNull UUID procedureId,
+    @NotNull @Valid List<InformationStatementDto> informationStatements) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
index 78c64338a..fed72b228 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetMedicalHistoriesResponse.java
@@ -5,7 +5,7 @@
 
 package de.eshg.travelmedicine.vaccinationconsultation.api;
 
-import de.eshg.travelmedicine.medicalhistory.api.MedicalHistoryDto;
+import de.eshg.travelmedicine.document.medicalhistory.api.MedicalHistoryDto;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import java.util.List;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
index 4a84ffb4c..2fe3444df 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/GetVaccinationConsultationDetailsResponse.java
@@ -19,5 +19,4 @@ public record GetVaccinationConsultationDetailsResponse(
     @NotNull @Valid TravelInformationDto travelInformation,
     @NotNull CreatedByUserTypeDto createdByUserType,
     @NotNull @Valid AppointmentSummaryDto initialAppointment,
-    @NotNull @Valid List<ServicePlanEntryDto> servicePlanList,
-    @NotNull @Valid List<InformationStatementDto> informationStatements) {}
+    @NotNull @Valid List<ServicePlanEntryDto> servicePlanList) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java
new file mode 100644
index 000000000..d5b8bc8a7
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/InformationStatementSummaryDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.UUID;
+
+@Schema(name = "InformationStatementSummary")
+public record InformationStatementSummaryDto(
+    @NotNull UUID id,
+    @NotNull @Size(max = 200) String title,
+    @NotNull boolean citizenHasAnswered) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java
new file mode 100644
index 000000000..fd5b3cf0f
--- /dev/null
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/api/PatchAcceptDraftRequest.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+package de.eshg.travelmedicine.vaccinationconsultation.api;
+
+import java.util.UUID;
+
+public record PatchAcceptDraftRequest(UUID referencePersonId) {}
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
index 38fc17e95..756d4f783 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/ProcedureStep.java
@@ -11,7 +11,7 @@ import de.eshg.lib.appointmentblock.persistence.AppointmentType;
 import de.eshg.lib.appointmentblock.persistence.entity.Appointment;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
-import de.eshg.travelmedicine.medicalhistory.persistence.entity.MedicalHistory;
+import de.eshg.travelmedicine.document.medicalhistory.persistence.entity.MedicalHistory;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
index 65b78479b..b3c0e8b85 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultation.java
@@ -9,6 +9,8 @@ import de.eshg.lib.common.CountryCode;
 import de.eshg.lib.common.DataSensitivity;
 import de.eshg.lib.common.SensitivityLevel;
 import de.eshg.lib.procedure.domain.model.Procedure;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement;
+import de.eshg.travelmedicine.document.informationstatement.persistence.entity.InformationStatement_;
 import jakarta.persistence.CascadeType;
 import jakarta.persistence.Column;
 import jakarta.persistence.ElementCollection;
@@ -77,7 +79,7 @@ public class VaccinationConsultation
   @OneToMany(
       fetch = FetchType.LAZY,
       mappedBy = InformationStatement_.VACCINATION_CONSULTATION,
-      cascade = CascadeType.PERSIST,
+      cascade = {CascadeType.PERSIST, CascadeType.REMOVE},
       orphanRemoval = true)
   @OrderBy
   private final List<InformationStatement> informationStatements = new ArrayList<>();
diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
index 94d7a7726..8c1fe387e 100644
--- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
+++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/vaccinationconsultation/persistence/entity/VaccinationConsultationRepository.java
@@ -33,13 +33,12 @@ public interface VaccinationConsultationRepository
       from VaccinationConsultation vc inner join vc.relatedPersons person inner join vc.procedureSteps ps left join ps.appointment a left join ps.userDefinedAppointment uda
       where a.appointmentStart between :startInstant and :endInstant
       or uda.appointmentStart between :startInstant and :endInstant
-      or (ps.earliestDate between :startLocalDate and :endLocalDate and a.appointmentStart is null and uda.appointmentStart is null)
+      or (ps.earliestDate = :startLocalDate and a.appointmentStart is null and uda.appointmentStart is null)
       order by ps.id""")
   List<AppointmentOverviewEntry> findAppointmentOverview(
       @Param("startInstant") Instant startInstant,
       @Param("endInstant") Instant endInstant,
-      @Param("startLocalDate") LocalDate startLocalDate,
-      @Param("endLocalDate") LocalDate endLocalDate);
+      @Param("startLocalDate") LocalDate startLocalDate);
 
   @Query(
       "select new de.eshg.travelmedicine.vaccinationconsultation.persistence.entity.VaccinationConsultationSearch(v.externalId, person.centralFileStateId, v.travelStartDate, "
diff --git a/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties b/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties
new file mode 100644
index 000000000..f5921fcde
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/application-health-department-frankfurt.properties
@@ -0,0 +1,40 @@
+de.eshg.travel-medicine.notification.fromAddress=info.reisemedizin@stadt-frankfurt.de
+de.eshg.travel-medicine.notification.greeting=Ihr Impfberatungsteam der Stadt Frankfurt
+
+# can be set individually to overwrite base department infos
+# de.eshg.travel-medicine.department-info.name=
+# de.eshg.travel-medicine.department-info.abbreviation=
+# de.eshg.travel-medicine.department-info.street=
+# de.eshg.travel-medicine.department-info.houseNumber=
+# de.eshg.travel-medicine.department-info.postalCode=
+# de.eshg.travel-medicine.department-info.city=
+# de.eshg.travel-medicine.department-info.country=
+# de.eshg.travel-medicine.department-info.phoneNumber=
+# de.eshg.travel-medicine.department-info.homepage=
+# de.eshg.travel-medicine.department-info.email=
+# de.eshg.travel-medicine.department-info.latitude=
+# de.eshg.travel-medicine.department-info.longitude=
+
+de.eshg.travel-medicine.opening-hours.de[0]=Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)
+de.eshg.travel-medicine.opening-hours.de[1]=Mo bis Do 08:00 bis 12:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[2]=Telefonische Terminvereinbarung zu folgenden Zeiten:
+de.eshg.travel-medicine.opening-hours.de[3]=Mo bis Mi 14:00 bis 15:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[4]=Fr 08:00 bis 10:00 Uhr
+
+de.eshg.travel-medicine.opening-hours.en[0]=Consultation hours only with appointment (by telephone or email)
+de.eshg.travel-medicine.opening-hours.en[1]=Mon to Thu 8:00 am to 12:00 pm
+de.eshg.travel-medicine.opening-hours.en[2]=Make an appointment by telephone at the following times:
+de.eshg.travel-medicine.opening-hours.en[3]=Mon to Wed 2:00 pm to 3:00 pm
+de.eshg.travel-medicine.opening-hours.en[4]=Fri 8:00 am to 10:00 am
+
+de.eshg.travel-medicine.notification.templates.path=notifications/ga_frankfurt/de
+
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject=Bestätigung Ihrer Terminbuchung
+de.eshg.travel-medicine.notification.template.booking_by_citizen.subject=Bestätigung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_employee.subject=Buchung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject=Bestätigung: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject=Terminabsage: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject=Bestätigung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject=Umbuchung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.new_information_statement.subject=Neuer Aufklärungsbogen: Es steht ein neuer Aufklärungsbogen bereit!
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject=Neuer Folgetermin wurde eingestellt!
diff --git a/backend/travel-medicine/src/main/resources/application.properties b/backend/travel-medicine/src/main/resources/application.properties
index 15d1817b7..c284378d2 100644
--- a/backend/travel-medicine/src/main/resources/application.properties
+++ b/backend/travel-medicine/src/main/resources/application.properties
@@ -42,3 +42,47 @@ de.eshg.travel-medicine.privacy-policy-location=classpath:pdf_templates/privacy_
 # de.eshg.travel-medicine.department-info.email=
 # de.eshg.travel-medicine.department-info.latitude=
 # de.eshg.travel-medicine.department-info.longitude=
+
+de.eshg.travel-medicine.opening-hours.de[0]=Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)
+de.eshg.travel-medicine.opening-hours.de[1]=Mo bis Do 08:00 bis 12:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[2]=Telefonische Terminvereinbarung zu folgenden Zeiten:
+de.eshg.travel-medicine.opening-hours.de[3]=Mo bis Mi 14:00 bis 15:00 Uhr
+de.eshg.travel-medicine.opening-hours.de[4]=Fr 08:00 bis 10:00 Uhr
+
+de.eshg.travel-medicine.opening-hours.en[0]=Consultation hours only with appointment (by telephone or email)
+de.eshg.travel-medicine.opening-hours.en[1]=Mon to Thu 8:00 am to 12:00 pm
+de.eshg.travel-medicine.opening-hours.en[2]=Make an appointment by telephone at the following times:
+de.eshg.travel-medicine.opening-hours.en[3]=Mon to Wed 2:00 pm to 3:00 pm
+de.eshg.travel-medicine.opening-hours.en[4]=Fri 8:00 am to 10:00 am
+
+
+# standard notifications (by mail)
+
+de.eshg.travel-medicine.notification.templates.path=notifications/default/de
+
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.subject=Bestätigung Ihrer Terminbuchung
+de.eshg.travel-medicine.notification.template.new_citizen_procedure.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_citizen_procedure.txt
+
+de.eshg.travel-medicine.notification.template.booking_by_citizen.subject=Bestätigung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/booking_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.booking_by_employee.subject=Buchung: Ihr Termin wurde gebucht!
+de.eshg.travel-medicine.notification.template.booking_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/booking_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.subject=Bestätigung: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/cancellation_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.subject=Terminabsage: Ihr Termin wurde abgesagt!
+de.eshg.travel-medicine.notification.template.cancellation_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/cancellation_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.subject=Bestätigung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_citizen.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/rebooking_by_citizen.txt
+
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.subject=Umbuchung: Ihr Termin wurde umgebucht!
+de.eshg.travel-medicine.notification.template.rebooking_by_employee.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/rebooking_by_employee.txt
+
+de.eshg.travel-medicine.notification.template.new_information_statement.subject=Neuer Aufklärungsbogen: Es steht ein neuer Aufklärungsbogen bereit!
+de.eshg.travel-medicine.notification.template.new_information_statement.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_information_statement.txt
+
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.subject=Neuer Folgetermin wurde eingestellt!
+de.eshg.travel-medicine.notification.template.new_follow_up_appointment.body=classpath:${de.eshg.travel-medicine.notification.templates.path}/new_follow_up_appointment.txt
diff --git a/backend/travel-medicine/src/main/resources/initial_medical_history.json b/backend/travel-medicine/src/main/resources/initial_medical_history.json
index b07344706..61bcae0e9 100644
--- a/backend/travel-medicine/src/main/resources/initial_medical_history.json
+++ b/backend/travel-medicine/src/main/resources/initial_medical_history.json
@@ -3,113 +3,84 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Fühlen Sie sich heute krank?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Hatten Sie in den letzten Wochen Fieber, eine Operation oder einen Unfall?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, bitte erläutern:",
-              "answer": null
+              "questionText": "Wenn ja, bitte erläutern:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben Sie in den letzten 4 Wochen Impfungen erhalten?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche und wann:",
-              "answer": null
+              "questionText": "Wenn ja, welche und wann:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben Sie Allergien (Unverträglichkeiten) wie z.B gegen Hühnereiweiß, Impfstoffe oder Medikamente?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Hatten Sie schon einmal starke Impfreaktionen oder -komplikationen?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Haben oder hatten Sie chronische Erkrankungen?",
-            "answer": null,
             "subElementMultiSelect": [
               {
-                "questionText": "Herzerkrankung",
-                "answer": null
+                "questionText": "Herzerkrankung"
               },
               {
-                "questionText": "Lebererkrankung",
-                "answer": null
+                "questionText": "Lebererkrankung"
               },
               {
-                "questionText": "Nierenerkrankung",
-                "answer": null
+                "questionText": "Nierenerkrankung"
               },
               {
-                "questionText": "Krebsleiden",
-                "answer": null
+                "questionText": "Krebsleiden"
               },
               {
-                "questionText": "seelische Erkrankung",
-                "answer": null
+                "questionText": "seelische Erkrankung"
               },
               {
-                "questionText": "Bluthochdruck",
-                "answer": null
+                "questionText": "Bluthochdruck"
               },
               {
-                "questionText": "Diabetes",
-                "answer": null
+                "questionText": "Diabetes"
               },
               {
-                "questionText": "Anfallsleiden (Epilepsie)",
-                "answer": null
+                "questionText": "Anfallsleiden (Epilepsie)"
               }
             ],
             "subElementText": {
-              "questionText": "Folgende Erkrankungen:",
-              "answer": null
+              "questionText": "Folgende Erkrankungen:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Nehmen Sie regelmäßig Medikamente ein?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         }
@@ -118,32 +89,23 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
-            "questionText": "Haben Sie eine HIV-lnfektion?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+          "anamnesisQuestion": {
+            "questionText": "Haben Sie eine HIV-Infektion?",
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
-            "questionText": "Erhalten Sie oder Erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+          "anamnesisQuestion": {
+            "questionText": "Erhalten Sie oder erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?",
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Erhalten Sie oder erhielten Sie in den letzten Monaten immunsupprimierende Medikamente (Medikamente, die die Abwehr gegen Infektionskrankheiten schwächen), wie z. B. Cortison, Antikörper o. a.?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, welche:",
-              "answer": null
+              "questionText": "Wenn ja, welche:"
             }
           }
         }
@@ -152,33 +114,24 @@
     {
       "sectionElements": [
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Sind Sie schwanger?",
-            "answer": null,
             "subElementMultiSelect": [],
             "subElementText": {
-              "questionText": "Wenn ja, in der Woche:",
-              "answer": null
+              "questionText": "Wenn ja, in der Woche:"
             }
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Ist eine eine Schwangerschaft geplant?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         },
         {
-          "elementType": "option",
-          "elementData": {
+          "anamnesisQuestion": {
             "questionText": "Stillen Sie?",
-            "answer": null,
-            "subElementMultiSelect": [],
-            "subElementText": null
+            "subElementMultiSelect": []
           }
         }
       ]
diff --git a/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml b/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml
new file mode 100644
index 000000000..3137206c7
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0036_delete_templates_and_documents.xml
@@ -0,0 +1,19 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="delete_all_templates_and_update_medical_history_entries-with-relations">
+    <sql>
+      DELETE FROM information_statement_templates_to_diseases;
+      DELETE FROM information_statement_template;
+      DELETE FROM medical_history_template;
+      DELETE FROM information_statement;
+      UPDATE medical_history SET content = '{"sections":[{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Fühlen Sie sich heute krank?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Hatten Sie in den letzten Wochen Fieber, eine Operation oder einen Unfall?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, bitte erläutern:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben Sie in den letzten 4 Wochen Impfungen erhalten?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche und wann:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben Sie Allergien (Unverträglichkeiten) wie z.B gegen Hühnereiweiß, Impfstoffe oder Medikamente?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Hatten Sie schon einmal starke Impfreaktionen oder -komplikationen?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Haben oder hatten Sie chronische Erkrankungen?","answer":null,"subElementMultiSelect":[{"questionText":"Herzerkrankung","answer":null},{"questionText":"Lebererkrankung","answer":null},{"questionText":"Nierenerkrankung","answer":null},{"questionText":"Krebsleiden","answer":null},{"questionText":"seelische Erkrankung","answer":null},{"questionText":"Bluthochdruck","answer":null},{"questionText":"Diabetes","answer":null},{"questionText":"Anfallsleiden (Epilepsie)","answer":null}],"subElementText":{"questionText":"Folgende Erkrankungen:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Nehmen Sie regelmäßig Medikamente ein?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null}]},{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Haben Sie eine HIV-Infektion?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Erhalten Sie oder erhielten Sie in den letzten Monaten Strahlentherapie oder Chemotherapie (Krebsbehandlung)?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Erhalten Sie oder erhielten Sie in den letzten Monaten immunsupprimierende Medikamente (Medikamente, die die Abwehr gegen Infektionskrankheiten schwächen), wie z. B. Cortison, Antikörper o. a.?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, welche:","answer":null}},"textBlock":null,"confirmation":null}]},{"sectionTitle":null,"sectionElements":[{"anamnesisQuestion":{"questionText":"Sind Sie schwanger?","answer":null,"subElementMultiSelect":[],"subElementText":{"questionText":"Wenn ja, in der Woche:","answer":null}},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Ist eine eine Schwangerschaft geplant?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null},{"anamnesisQuestion":{"questionText":"Stillen Sie?","answer":null,"subElementMultiSelect":[],"subElementText":null},"textBlock":null,"confirmation":null}]}]}';
+    </sql>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml b/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml
new file mode 100644
index 000000000..eb3ee2ff1
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0037_add_system_progress_entry_keydocument.xml
@@ -0,0 +1,14 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729781669307-1">
+        <addColumn tableName="system_progress_entry">
+            <column name="key_document_type" type="text"/>
+            <column name="key_document_version" type="int4"/>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml b/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml
new file mode 100644
index 000000000..01650cd8b
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0038_cemetery_sequence.xml
@@ -0,0 +1,11 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1729858144081-1">
+    <ext:migrateAutoIncrementToSequence tableName="cemetery"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml b/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml
new file mode 100644
index 000000000..232646271
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0039_move_subject_and_message_text_to_mail_metadata.xml
@@ -0,0 +1,46 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+    <changeSet author="GA-Lotse" id="1729858943767-1">
+        <addColumn tableName="mail_meta_data">
+            <column name="message_text" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+            <column name="subject" type="text" defaultValue="">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="mail_meta_data_aud">
+            <column name="message_text" type="text"/>
+            <column name="subject" type="text"/>
+        </addColumn>
+        <sql>
+        UPDATE mail_meta_data
+        SET subject      = COALESCE(manual_progress_entry.subject, mail_meta_data.subject),
+            message_text = COALESCE(manual_progress_entry.message_text, mail_meta_data.message_text)
+        FROM progress_entry,
+             manual_progress_entry
+        WHERE mail_meta_data.mail_id = progress_entry.file_id
+          AND manual_progress_entry.id = progress_entry.id
+        </sql>
+        <sql>
+        UPDATE mail_meta_data_aud
+        SET subject      = manual_progress_entry_aud.subject,
+            message_text = manual_progress_entry_aud.message_text
+        FROM manual_progress_entry_aud,
+             progress_entry
+        WHERE progress_entry.file_id = mail_meta_data_aud.mail_id
+          AND manual_progress_entry_aud.id = progress_entry.id
+        </sql>
+        <dropDefaultValue tableName="mail_meta_data" columnName="subject"/>
+        <dropDefaultValue tableName="mail_meta_data" columnName="message_text"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry"/>
+        <dropColumn columnName="message_text" tableName="manual_progress_entry_aud"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry"/>
+        <dropColumn columnName="subject" tableName="manual_progress_entry_aud"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml b/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml
new file mode 100644
index 000000000..4cf74a39c
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/migrations/0040_add_gdpr_validation_task.xml
@@ -0,0 +1,43 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<!--
+ Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
+  <changeSet author="GA-Lotse" id="1730725475130-1">
+    <ext:createPostgresEnumType name="gdprvalidationtaskstatus" values="CLOSED, OPEN"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-2">
+    <ext:createPostgresEnumType name="gdprvalidationtasktype" values="RIGHT_OF_ACCESS, RIGHT_TO_ERASURE"/>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-3">
+    <createTable tableName="gdpr_validation_task">
+      <column autoIncrement="true" name="id" type="BIGINT">
+        <constraints nullable="false" primaryKey="true" primaryKeyName="pk_gdpr_validation_task"/>
+      </column>
+      <column name="version" type="BIGINT">
+        <constraints nullable="false"/>
+      </column>
+      <column name="closed_at" type="TIMESTAMP WITH TIME ZONE"/>
+      <column name="created_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="modified_at" type="TIMESTAMP WITH TIME ZONE">
+        <constraints nullable="false"/>
+      </column>
+      <column name="procedure_id" type="UUID">
+        <constraints nullable="false"/>
+      </column>
+      <column name="status" type="GDPRVALIDATIONTASKSTATUS">
+        <constraints nullable="false"/>
+      </column>
+      <column name="type" type="GDPRVALIDATIONTASKTYPE">
+        <constraints nullable="false"/>
+      </column>
+    </createTable>
+  </changeSet>
+  <changeSet author="GA-Lotse" id="1730725475130-4">
+    <addUniqueConstraint columnNames="procedure_id" constraintName="gdpr_validation_task_procedure_id_key" tableName="gdpr_validation_task"/>
+  </changeSet>
+</databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/migrations/changelog.xml b/backend/travel-medicine/src/main/resources/migrations/changelog.xml
index 29c17dbc7..4b03dc184 100644
--- a/backend/travel-medicine/src/main/resources/migrations/changelog.xml
+++ b/backend/travel-medicine/src/main/resources/migrations/changelog.xml
@@ -43,5 +43,10 @@
   <include file="migrations/0033_set_earliest_date_to_ps.xml"/>
   <include file="migrations/0034_add_citizen_has_answered_to_infostmt.xml"/>
   <include file="migrations/0035_make_file_and_manual_progress_entry_owning_side_of_approval_requests.xml"/>
+  <include file="migrations/0036_delete_templates_and_documents.xml"/>
+  <include file="migrations/0037_add_system_progress_entry_keydocument.xml"/>
+  <include file="migrations/0038_cemetery_sequence.xml"/>
+  <include file="migrations/0039_move_subject_and_message_text_to_mail_metadata.xml"/>
+  <include file="migrations/0040_add_gdpr_validation_task.xml"/>
 
 </databaseChangeLog>
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt
new file mode 100644
index 000000000..b7d0f7213
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_citizen.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben einen Termin für %s Uhr gebucht. 
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt
new file mode 100644
index 000000000..05d3c877a
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/booking_by_employee.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+wir haben für Sie einen Termin am %s Uhr gebucht.
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt
new file mode 100644
index 000000000..a6154cc46
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr abgesagt. Ihre Absage haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
\ No newline at end of file
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt
new file mode 100644
index 000000000..b91686fcb
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/cancellation_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+leider müssen wir Ihre Terminbuchung für den %s Uhr absagen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt
new file mode 100644
index 000000000..77df5c8ad
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_citizen_procedure.txt
@@ -0,0 +1,16 @@
+Sehr geehrte(r) %s %s,
+
+wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr bei uns eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
+Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie, dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
+%s
+
+Anmeldecode: %s
+Zur Verifikation wird Ihr Geburtsdatum benötigt.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Absagen verwenden Sie bitte den angegebenen Link.
+
+Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf, Sie bald bei uns begrüßen zu dürfen.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt
new file mode 100644
index 000000000..b44c01ada
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_follow_up_appointment.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+es wurde für Sie ein neuer Folgetermin eingestellt. Bitte buchen Sie einen für Sie passenden Zeitpunkt über das Online-Portal.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt
new file mode 100644
index 000000000..a8f25fc67
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/new_information_statement.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+für Ihren Besuch bei uns steht ein neuer Aufklärungsbogen zum Online-Ausfüllen bereit. Ab jetzt können Sie diesen einmalig ausfüllen und absenden.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt
new file mode 100644
index 000000000..82529b618
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr verschoben auf den %s Uhr. Ihre Umbuchung haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt
new file mode 100644
index 000000000..3825f4b0f
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/default/de/rebooking_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Ihre Terminbuchung für den %s Uhr wurde von uns auf den %s Uhr umgebucht.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt
new file mode 100644
index 000000000..b7d0f7213
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_citizen.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben einen Termin für %s Uhr gebucht. 
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt
new file mode 100644
index 000000000..05d3c877a
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/booking_by_employee.txt
@@ -0,0 +1,11 @@
+Sehr geehrte(r) %s %s,
+
+wir haben für Sie einen Termin am %s Uhr gebucht.
+
+Für den Fall, dass Sie diesen Termin nicht wahrnehmen können, denken Sie bitte daran, ihn in unserem Online-Service abzusagen oder zu verschieben.  
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt
new file mode 100644
index 000000000..a6154cc46
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr abgesagt. Ihre Absage haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
\ No newline at end of file
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt
new file mode 100644
index 000000000..b91686fcb
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/cancellation_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+leider müssen wir Ihre Terminbuchung für den %s Uhr absagen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt
new file mode 100644
index 000000000..77df5c8ad
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_citizen_procedure.txt
@@ -0,0 +1,16 @@
+Sehr geehrte(r) %s %s,
+
+wir möchten Ihnen mitteilen, dass Ihre Terminbuchung für den %s Uhr bei uns eingegangen ist. Bitte bewahren Sie diese Email als Bestätigung Ihrer Buchung auf.
+Für den Fall, dass Sie Ihren Termin ändern oder stornieren möchten, bitten wir Sie, dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link:
+%s
+
+Anmeldecode: %s
+Zur Verifikation wird Ihr Geburtsdatum benötigt.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten. Für Änderungen und Absagen verwenden Sie bitte den angegebenen Link.
+
+Vielen Dank, dass Sie unseren Service nutzen. Wir freuen uns darauf, Sie bald bei uns begrüßen zu dürfen.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt
new file mode 100644
index 000000000..b44c01ada
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_follow_up_appointment.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+es wurde für Sie ein neuer Folgetermin eingestellt. Bitte buchen Sie einen für Sie passenden Zeitpunkt über das Online-Portal.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt
new file mode 100644
index 000000000..a8f25fc67
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/new_information_statement.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+für Ihren Besuch bei uns steht ein neuer Aufklärungsbogen zum Online-Ausfüllen bereit. Ab jetzt können Sie diesen einmalig ausfüllen und absenden.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt
new file mode 100644
index 000000000..82529b618
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_citizen.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Sie haben Ihren Termin am %s Uhr verschoben auf den %s Uhr. Ihre Umbuchung haben wir zur Kenntnis genommen.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt
new file mode 100644
index 000000000..3825f4b0f
--- /dev/null
+++ b/backend/travel-medicine/src/main/resources/notifications/ga_frankfurt/de/rebooking_by_employee.txt
@@ -0,0 +1,9 @@
+Sehr geehrte(r) %s %s,
+
+Ihre Terminbuchung für den %s Uhr wurde von uns auf den %s Uhr umgebucht.
+
+Beachten Sie bitte, dass es nicht möglich ist auf diese Email zu antworten.
+
+Mit freundlichen Grüßen,
+
+%s
diff --git a/build.gradle b/build.gradle
index 48c831835..f0a8fefa4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,5 @@
 import com.github.gradle.node.pnpm.task.PnpmTask
+import groovy.io.FileVisitResult
 import org.jetbrains.gradle.ext.JUnit
 
 buildscript {
@@ -20,7 +21,7 @@ if (project.hasProperty('projectVersion')) {
 }
 
 dockerCompose {
-  projectName = "eshg-frontend"
+  projectName = "frontend"
 }
 
 //CAUTION: run ./gradlew wrapper (twice) after changes to this task!
@@ -30,8 +31,30 @@ wrapper {
   distributionType = Wrapper.DistributionType.ALL
 }
 
+Set<File> collectDataTestDirectoriesToExclude() {
+  def dataTestOutput = java.nio.file.Path.of("data/test/output")
+  def dataTestTmp = java.nio.file.Path.of("data/test/tmp")
+  Set<String> directoriesToSkip = ['node_modules', '.next', '.gradle', 'build', 'out']
+  Set<File> dataTestDirectoriesToExclude = new TreeSet<>()
+  project.file(project.rootDir).traverse(
+    type: groovy.io.FileType.DIRECTORIES,
+    maxDepth: 5,
+    preDir: { dir ->
+      if (directoriesToSkip.contains(dir.name)) {
+        return FileVisitResult.SKIP_SUBTREE
+      }
+    }) { File dir ->
+    def path = dir.toPath()
+    if (path.endsWith(dataTestOutput) || path.endsWith(dataTestTmp)) {
+      dataTestDirectoriesToExclude.add(dir)
+    }
+  }
+  return dataTestDirectoriesToExclude
+}
+
 idea {
   module {
+    excludeDirs = collectDataTestDirectoriesToExclude()
     downloadJavadoc = true
     downloadSources = true
   }
@@ -98,14 +121,14 @@ def installDependencies = tasks.register('installDependencies', PnpmTask) {
   args = ['install', '--frozen-lockfile']
 }
 
-tasks.register('installDependency', PnpmTask) {
+tasks.register('updateLockfile', PnpmTask) {
   description = "Install missing dependencies and update the lock file. Use this to add new dependencies."
   inputs.files depsInputs
   outputs.dir nodeModulesDir
   subprojects {
     outputs.dir "${projectDir}/${nodeModulesDir}"
   }
-  args = ['install']
+  args = ['install', '--recursive']
 }
 
 tasks.register('updateDependencies', PnpmTask) {
@@ -118,6 +141,13 @@ tasks.register('updateDependencies', PnpmTask) {
   args = ['update', '--recursive', '--latest']
 }
 
+tasks.register('outdatedDependencies', PnpmTask) {
+  description = 'List all outdated npm dependencies'
+  inputs.files depsInputs
+  outputs.upToDateWhen { true }
+  args = ['outdated', '--recursive']
+}
+
 tasks.register('testCoverage', PnpmTask) {
   environment = ['TZ': 'UTC']
   inputs.file "${rootDir}/config/tsconfig.base.json"
diff --git a/buildSrc/src/main/groovy/next-loading-files.gradle b/buildSrc/src/main/groovy/next-loading-files.gradle
new file mode 100644
index 000000000..2847ec72f
--- /dev/null
+++ b/buildSrc/src/main/groovy/next-loading-files.gradle
@@ -0,0 +1,62 @@
+/**
+ * Workaround for a Next.js bug where the loading.tsx is not applied to pages nested within a route group.
+ * It circumvents the bug by generating separate loading.tsx for each included page.
+ *
+ * See https://github.com/vercel/next.js/issues/69625
+ */
+
+interface NextLoadingFilesConfiguration {
+  ListProperty<String> getIncludeDirs()
+}
+
+def nextLoadingFilesExtension = project.extensions.create('nextLoadingFiles', NextLoadingFilesConfiguration)
+
+def appDir = project.layout.projectDirectory.dir('src/app');
+
+def includeFilesByName = (String fileName) -> {
+  return fileTree(appDir) {
+    include nextLoadingFilesExtension.includeDirs.get().collect { "${it}/**/${fileName}" }
+  }
+}
+
+def cleanLoadingFiles = tasks.register('cleanLoadingFiles', Delete) {
+  delete includeFilesByName('loading.tsx')
+}
+
+def generateLoadingFiles = tasks.register('generateLoadingFiles') { task ->
+  def pageFiles = includeFilesByName('page.tsx')
+  def loadingTemplate = file("${appDir}/loading.template.tsx")
+  def loadingFiles = pageFiles.collect { file("${it.parent}/loading.tsx") }
+
+  inputs.files pageFiles
+  inputs.file loadingTemplate
+  outputs.files loadingFiles
+
+  doFirst {
+    delete includeFilesByName('loading.tsx')
+    def text = loadingTemplate.getText('UTF-8')
+    loadingFiles.each { File loadingFile ->
+      if (!loadingFile.exists()) {
+        loadingFile.createNewFile()
+      }
+
+      loadingFile.setText(text, 'UTF-8')
+    }
+  }
+}
+
+tasks.named('prepareEnvironment') {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named("formatCheck") {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named("lint") {
+  dependsOn generateLoadingFiles
+}
+
+tasks.named('clean') {
+  dependsOn cleanLoadingFiles
+}
diff --git a/buildSrc/src/main/groovy/node.gradle b/buildSrc/src/main/groovy/node.gradle
index 63ad67518..66ce00f0e 100644
--- a/buildSrc/src/main/groovy/node.gradle
+++ b/buildSrc/src/main/groovy/node.gradle
@@ -4,7 +4,7 @@ plugins {
 
 node {
   version = '20.18.0'
-  pnpmVersion = '9.12.1'
+  pnpmVersion = '9.12.3'
   download = true
   workDir = file("${rootProject.projectDir}/.gradle/nodejs")
   pnpmWorkDir = file("${rootProject.projectDir}/.gradle/pnpm")
diff --git a/citizen-portal/.gitignore b/citizen-portal/.gitignore
index e1ac636f8..fb4ad4b15 100644
--- a/citizen-portal/.gitignore
+++ b/citizen-portal/.gitignore
@@ -2,3 +2,7 @@
 .next
 # Usually, this file is ignored. But we need to make sure it exists in CI when running tsc. See https://github.com/vercel/next.js/discussions/47010.
 # next-env.d.ts
+
+# Generated files
+src/app/\[lang\]/(privatpersonen)/**/loading.tsx
+src/app/\[lang\]/(static)/**/loading.tsx
diff --git a/citizen-portal/build.gradle b/citizen-portal/build.gradle
index 5473636fb..c00b28e52 100644
--- a/citizen-portal/build.gradle
+++ b/citizen-portal/build.gradle
@@ -1,6 +1,7 @@
 plugins {
   id 'next-app'
   id 'reverse-proxy'
+  id 'next-loading-files'
 }
 
 next {
@@ -9,6 +10,10 @@ next {
   apiProject = ':citizen-portal-api'
 }
 
+nextLoadingFiles {
+  includeDirs = ['[lang]/(privatpersonen)', '[lang]/(static)']
+}
+
 reverseProxy {
   serviceName = "citizen-portal-reverse-proxy"
   mainConfigFile = "citizen-portal.conf"
diff --git a/citizen-portal/package.json b/citizen-portal/package.json
index 6286648c5..464887f12 100644
--- a/citizen-portal/package.json
+++ b/citizen-portal/package.json
@@ -4,37 +4,55 @@
   "type": "module",
   "private": true,
   "dependencies": {
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/citizen-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@mdx-js/mdx": "3.0.1",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
-    "@types/negotiator": "0.6.3",
-    "server-only": "0.0.1",
-    "i18next": "23.15.2",
-    "i18next-resources-to-backend": "1.2.1",
-    "negotiator": "0.6.3",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "valibot": "0.42.1"
+    "@fontsource/poppins": "catalog:joy",
+    "@fullcalendar/core": "catalog:fullcalendar",
+    "@fullcalendar/daygrid": "catalog:fullcalendar",
+    "@fullcalendar/interaction": "catalog:fullcalendar",
+    "@fullcalendar/react": "catalog:fullcalendar",
+    "@mdx-js/mdx": "3.1.0",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "date-fns": "catalog:common",
+    "formik": "catalog:common",
+    "i18next": "catalog:i18next",
+    "i18next-resources-to-backend": "catalog:i18next",
+    "negotiator": "1.0.0",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-i18next": "catalog:i18next",
+    "remeda": "catalog:common",
+    "server-only": "catalog:common",
+    "valibot": "catalog:common"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
     "@types/mdx": "2.0.13",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@types/negotiator": "0.6.3",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx b/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
index 95c007a60..8fc02b0dc 100644
--- a/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
+++ b/citizen-portal/src/app/[lang]/unternehmen/masernschutz/meldeformular/page.tsx
@@ -47,7 +47,7 @@ export default function CitizenMeaslesProtectionReportCasePage() {
   });
 
   async function handleSubmit(report: ReportMeaslesCase) {
-    await reportCase.mutateAsync(report).catch();
+    await reportCase.mutateAsync(report);
   }
 
   return (
diff --git a/citizen-portal/src/app/loading.template.tsx b/citizen-portal/src/app/loading.template.tsx
new file mode 100644
index 000000000..011b2bd06
--- /dev/null
+++ b/citizen-portal/src/app/loading.template.tsx
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import Loading from "@/app/[lang]/loading";
+
+export default Loading;
diff --git a/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx b/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
index f2c62cf0b..bfdbe0a63 100644
--- a/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
+++ b/citizen-portal/src/lib/baseModule/components/layout/AppLayout.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+/* eslint @next/next/no-head-element: 0 */
 import { ApiProvider } from "@eshg/lib-portal/api/ApiProvider";
 import { SnackbarProvider } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { PropsWithChildren } from "react";
@@ -32,6 +33,9 @@ export function AppLayout({
           flexDirection: "column",
         }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <I18nProvider lang={lang}>
           <ThemeProvider>
             <SnackbarProvider snackbar={CitizenSnackbar}>
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts b/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
index 89b4bc1dc..6f3f9d224 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi.ts
@@ -31,3 +31,12 @@ export function getSelfFreeAppointmentsAsCitizenQuery(
     select: (response) => response.freeAppointments.map(mapAppointment),
   });
 }
+
+export function getOpeningHoursQuery(
+  schoolEntryCitizenApi: SchoolEntryCitizenApi,
+) {
+  return queryOptions({
+    queryKey: schoolEntryCitizenApiQueryKey(["getOpeningHours"]),
+    queryFn: () => schoolEntryCitizenApi.getOpeningHours(),
+  });
+}
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
index a3bc9dbbf..894e56903 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/de/anamnesis.json
@@ -32,8 +32,9 @@
     "siblingBirthYear": "Geburtsjahr Geschwister {{index}}",
     "removeSibling": "entfernen",
     "addSibling": "Weitere Geschwister hinzufügen",
+    "wasInDaycare": "War im Kindergarten",
     "dayCareAndSchool": "Angaben zu Kindertagesstätte und Schule",
-    "dayCareSince": "In Kindertagesstätte seit",
+    "dayCareSince": "seit",
     "dayCareName": "Name der Kindertagesstätte",
     "integrationPlace": "Integrationsplatz in der Kita",
     "earlySupport": "Frühförderung",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
index 735127c7a..cdcab80e6 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/locales/en/anamnesis.json
@@ -32,8 +32,9 @@
     "siblingBirthYear": "Year of Birth of Siblings",
     "removeSibling": "Remove",
     "addSibling": "Add More Siblings",
+    "wasInDaycare": "Was in daycare",
     "dayCareAndSchool": "Information on Daycare and School",
-    "dayCareSince": "In Daycare since",
+    "dayCareSince": "since",
     "dayCareName": "Name of Daycare",
     "integrationPlace": "Integration Place in Daycare",
     "earlySupport": "Early Support",
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
index 9133bd96c..85426dd2b 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/appointment/update-appointment/UpdateAppointmentForm.tsx
@@ -36,9 +36,9 @@ export function UpdateAppointmentForm(props: UpdateAppointmentFormProps) {
 
   async function handleSubmit(values: AppointmentFormValues) {
     if (values.newAppointment) {
-      await updateAppointment
-        .mutateAsync({ newAppointment: values.newAppointment })
-        .catch();
+      await updateAppointment.mutateAsync({
+        newAppointment: values.newAppointment,
+      });
       router.push(citizenRoutes.appointment.index(undefined));
     }
   }
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
index fdb8f2cf6..be2e01ea7 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/CitizenAnamnesisForm.tsx
@@ -88,6 +88,7 @@ interface AdditionalChildInfoValues {
 }
 
 interface DaycareAndSchoolInfoValues {
+  wasInDaycare: ToggleableSectionFormValue;
   inDaycareSince: MonthAndYear;
   daycareName: OptionalFieldValue<string>;
   schoolName: OptionalFieldValue<string>;
@@ -190,6 +191,9 @@ const INITIAL_VALUES: CitizenAnamnesisFormValues = {
     },
   },
   daycareAndSchoolInfo: {
+    wasInDaycare: {
+      show: null,
+    },
     inDaycareSince: { month: null, year: "" },
     daycareName: "",
     schoolName: "",
@@ -277,13 +281,11 @@ export function CitizenAnamnesisForm(props: CitizenAnamnesisFormProps) {
   const citizenRoutes = useCitizenRoutes();
 
   async function handleSubmit(values: CitizenAnamnesisFormValues) {
-    await addCitizenAnamnesis
-      .mutateAsync(mapToRequest(values), {
-        onSuccess: () => {
-          void router.push(citizenRoutes.appointment.index(undefined));
-        },
-      })
-      .catch();
+    await addCitizenAnamnesis.mutateAsync(mapToRequest(values), {
+      onSuccess: () => {
+        void router.push(citizenRoutes.appointment.index(undefined));
+      },
+    });
   }
 
   return (
@@ -402,10 +404,17 @@ function mapToRequest(
           : undefined,
       },
       daycareAndSchoolInfo: {
-        inDaycareSince: mapMonthAndYear(
-          values.daycareAndSchoolInfo.inDaycareSince,
+        wasInDaycare: mapNullableValue(
+          values.daycareAndSchoolInfo.wasInDaycare.show,
+        ),
+        inDaycareSince: onlyIfShown(
+          values.daycareAndSchoolInfo.wasInDaycare,
+          mapMonthAndYear(values.daycareAndSchoolInfo.inDaycareSince),
+        ),
+        daycareName: onlyIfShown(
+          values.daycareAndSchoolInfo.wasInDaycare,
+          mapOptionalValue(values.daycareAndSchoolInfo.daycareName),
         ),
-        daycareName: mapOptionalValue(values.daycareAndSchoolInfo.daycareName),
         schoolName: mapOptionalValue(values.daycareAndSchoolInfo.schoolName),
       },
       developmentInfo: {
@@ -524,6 +533,7 @@ function onlyIfShown<TValue>(
 ): TValue | undefined {
   return section.show ? value : undefined;
 }
+
 function fallbackIfExplicitlyHidden<TValue>(
   section: ToggleableSectionFormValue,
   value: TValue,
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
index 4802d6db7..415d3a76a 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepFour.tsx
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-/* eslint-disable jsx-a11y/aria-props */
 import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { FormLabel, Grid, Typography } from "@mui/joy";
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
index 9b9871058..02af7f300 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/citizenAnamnesis/steps/CitizenAnamnesisStepTwo.tsx
@@ -108,27 +108,32 @@ export function CitizenAnamnesisStepTwo({
         </FieldArray>
       </ToggleableSection>
       <Typography level="h4">{t("additionalInfo.dayCareAndSchool")}</Typography>
-      <Grid container sx={{ flexGrow: 1 }}>
-        <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
-          <Typography level="body-sm">
-            {t("additionalInfo.dayCareSince")}
-          </Typography>
-          <LocalMonthAndYearFields
-            fieldName={daycareAndSchoolInfo("inDaycareSince")}
-            date={values.daycareAndSchoolInfo.inDaycareSince}
-          />
-        </Grid>
-        <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
-          <InputField
-            name={daycareAndSchoolInfo("daycareName")}
-            label={
-              <Typography level="body-sm" component={FormLabel}>
-                {t("additionalInfo.dayCareName")}
-              </Typography>
-            }
-          />
+      <ToggleableSection
+        name={daycareAndSchoolInfo("wasInDaycare.show")}
+        title={t("additionalInfo.wasInDaycare")}
+      >
+        <Grid container sx={{ flexGrow: 1 }}>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <Typography level="body-sm">
+              {t("additionalInfo.dayCareSince")}
+            </Typography>
+            <LocalMonthAndYearFields
+              fieldName={daycareAndSchoolInfo("inDaycareSince")}
+              date={values.daycareAndSchoolInfo.inDaycareSince}
+            />
+          </Grid>
+          <Grid {...byBreakpoint({ mobile: 12, desktop: 6 })}>
+            <InputField
+              name={daycareAndSchoolInfo("daycareName")}
+              label={
+                <Typography level="body-sm" component={FormLabel}>
+                  {t("additionalInfo.dayCareName")}
+                </Typography>
+              }
+            />
+          </Grid>
         </Grid>
-      </Grid>
+      </ToggleableSection>
       <LocalBooleanRadioField
         label={
           <Typography level="title-md">
diff --git a/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx b/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
index 1b555b44d..2dcb164e9 100644
--- a/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
+++ b/citizen-portal/src/lib/businessModules/schoolEntry/pages/landingpage/LandingpageContent.tsx
@@ -11,7 +11,10 @@ import {
   MailOutlineOutlined,
 } from "@mui/icons-material";
 import { Typography } from "@mui/joy";
+import { useSuspenseQuery } from "@tanstack/react-query";
 
+import { useSchoolEntryCitizenApi } from "@/lib/businessModules/schoolEntry/api/clients";
+import { getOpeningHoursQuery } from "@/lib/businessModules/schoolEntry/api/queries/schoolEntryCitizenApi";
 import { useTranslation } from "@/lib/i18n/client";
 import { DepartmentInfo } from "@/lib/shared/api/models/DepartmentInfo";
 import {
@@ -24,10 +27,6 @@ import {
   ContentSheetTitle,
 } from "@/lib/shared/components/layout/contentSheet";
 import { GridColumnStack } from "@/lib/shared/components/layout/grid";
-import {
-  TableListing,
-  TableListingRow,
-} from "@/lib/shared/components/tableListing";
 import {
   formatPostalCodeAndCity,
   formatStreetAndHouseNumber,
@@ -83,22 +82,22 @@ function AddressSection(props: DepartmentInfoProps) {
 }
 
 function OpeningHoursSection() {
-  const { t } = useTranslation(["schoolEntry/overview"]);
+  const { t, i18n } = useTranslation(["schoolEntry/overview"]);
+  const schoolEntryCitizenApi = useSchoolEntryCitizenApi();
+  const { data: openingHours } = useSuspenseQuery(
+    getOpeningHoursQuery(schoolEntryCitizenApi),
+  );
+
+  const openingHoursInSelectedLanguage =
+    i18n.language === "de" ? openingHours.de : openingHours.en;
   return (
     <InfoSection icon={<AccessTimeOutlined />}>
       <InfoSectionTitle>{t("openingHours.title")}</InfoSectionTitle>
-      <TableListing>
-        <TableListingRow label={t("openingHours.days")}>
-          {t("openingHours.hours")}
-          <br />
-          {t("openingHours.remark")}
-        </TableListingRow>
-        <TableListingRow label={t("openingHours.daysShort")}>
-          {t("openingHours.hoursShort")}
-          <br />
-          {t("openingHours.remarkShort")}
-        </TableListingRow>
-      </TableListing>
+      {openingHoursInSelectedLanguage.map((openingHour) => (
+        <p style={{ margin: 0 }} key={openingHour}>
+          {openingHour}
+        </p>
+      ))}
     </InfoSection>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
index 13e889a64..bbcf10b07 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/api/mutations/citizenAuthApi.ts
@@ -5,7 +5,7 @@
 
 import {
   ApiAppointment,
-  ApiMedicalHistoryContent,
+  ApiDocumentContent,
   DeleteAppointmentCpRequest,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
@@ -18,7 +18,7 @@ import { useTranslation } from "@/lib/i18n/client";
 export interface PatchMedicalHistoryRequest {
   procedureId: string;
   procedureStepId: string;
-  medicalHistory: ApiMedicalHistoryContent;
+  medicalHistory: ApiDocumentContent;
 }
 
 export function usePatchCitizenMedicalHistory() {
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts b/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
index c5c3d084e..03e45d3ce 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/api/queries/citizenPublicApi.ts
@@ -17,6 +17,16 @@ export function useGetAllDiseasesCitizen() {
   });
 }
 
+export function useGetAllAppointmentTypesForCitizen() {
+  const citizenPublicApi = useCitizenPublicApi();
+  return useSuspenseQuery({
+    queryKey: citizenPublicApiQueryKey(["getAppointmentTypesForCitizen"]),
+    queryFn: () => citizenPublicApi.getAppointmentTypesForCitizen(),
+    select: (response) => response.appointmentTypeConfigDtos ?? [],
+    refetchOnWindowFocus: false,
+  });
+}
+
 export function useGetFreeAppointmentsForCitizen(
   appointmentType: ApiAppointmentType,
   earliestDate?: Date,
@@ -45,3 +55,12 @@ export function useGetDepartmentInfo() {
     queryFn: () => departmentApi.getDepartmentInfo(),
   });
 }
+
+export function useGetOpeningHours() {
+  const citizenPublicApi = useCitizenPublicApi();
+  return useSuspenseQuery({
+    queryKey: citizenPublicApiQueryKey(["getOpeningHours"]),
+    queryFn: () => citizenPublicApi.getOpeningHours(),
+    refetchOnWindowFocus: false,
+  });
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
index 84dd5d199..db439bb02 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormButtonBar.tsx
@@ -3,21 +3,24 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiTravelType } from "@eshg/citizen-portal-api/travelMedicine";
+import {
+  ApiAppointmentType,
+  ApiTravelType,
+} from "@eshg/citizen-portal-api/travelMedicine";
 import { useFormikContext } from "formik";
 import { useRouter } from "next/navigation";
 
-import { StepKey } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper";
 import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
 import { MultiStepFormButtonBar } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormButtonsBar";
 import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
 import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
 import { useTranslation } from "@/lib/i18n/client";
 
+import { StepKey } from "./AppointmentStepper";
+
 export function AppointmentFormButtonBar() {
-  const { steps, currentStepIndex, totalSteps, isLastStep, onStepChange } =
-    useStepContext();
-  const { values, validateForm, setTouched, setFieldTouched, setErrors } =
+  const { isLastStep, currentNode, goForward, goBack } = useStepContext();
+  const { values, setTouched, validateForm, setFieldTouched, setErrors } =
     useFormikContext<InitialAppointmentFormValues>();
 
   const { t } = useTranslation(["travelMedicine/forms"]);
@@ -26,7 +29,7 @@ export function AppointmentFormButtonBar() {
 
   function isAppointmentPickerInValid() {
     let isInvalid = false;
-    if (steps[currentStepIndex]!.key === StepKey.AppointmentSlotStep) {
+    if (currentNode?.key === StepKey.AppointmentSlotStep) {
       isInvalid = !values.appointmentBlockDate;
     }
     return isInvalid;
@@ -65,28 +68,43 @@ export function AppointmentFormButtonBar() {
   async function handleNextStep() {
     const errors = await handleValidation();
     if (!errors) {
-      if (
-        steps[currentStepIndex]!.key === StepKey.TravelTypeStep &&
-        values.travelInformation.travelType === ApiTravelType.NoTravel
-      ) {
-        if (currentStepIndex < totalSteps) {
-          onStepChange(currentStepIndex + 2);
-          await setTouched({});
+      await setTouched({});
+      switch (currentNode?.key) {
+        case StepKey.AppointmentSlotStep: {
+          if (
+            values.initialStepAppointmentType === ApiAppointmentType.Vaccination
+          ) {
+            goForward(3);
+          } else goForward();
+          break;
+        }
+        case StepKey.TravelTypeStep: {
+          if (values.travelInformation.travelType === ApiTravelType.NoTravel) {
+            goForward(2);
+          } else goForward();
+          break;
+        }
+        default: {
+          goForward();
+          break;
         }
-      } else if (currentStepIndex < totalSteps) {
-        onStepChange(currentStepIndex + 1);
-        await setTouched({});
       }
     }
   }
 
   function handlePrevStep() {
-    if (
-      steps[currentStepIndex]!.key === StepKey.PersonalDataStep &&
-      values.travelInformation.travelType === ApiTravelType.NoTravel
-    ) {
-      if (currentStepIndex > 0) onStepChange(currentStepIndex - 2);
-    } else if (currentStepIndex > 0) onStepChange(currentStepIndex - 1);
+    if (currentNode?.key === StepKey.PersonalDataStep) {
+      if (
+        values.travelInformation.travelType === ApiTravelType.NoTravel &&
+        values.initialStepAppointmentType === ApiAppointmentType.Consultation
+      ) {
+        goBack(2);
+      } else if (
+        values.initialStepAppointmentType === ApiAppointmentType.Vaccination
+      ) {
+        goBack(3);
+      } else goBack();
+    } else goBack();
   }
 
   return (
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx
deleted file mode 100644
index 3e416efc6..000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { OverviewAndAppointmentStepToggle } from "@/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle";
-import { AppointmentReviewStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentReviewFormStep/AppointmentReviewStep";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-
-export function AppointmentFormContent() {
-  const { isLastStep } = useStepContext();
-
-  return (
-    <>
-      {!isLastStep ? (
-        <OverviewAndAppointmentStepToggle />
-      ) : (
-        <AppointmentReviewStep />
-      )}
-    </>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx
deleted file mode 100644
index 87ab3f1de..000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { FormikValues } from "formik";
-import { useRouter } from "next/navigation";
-
-import { usePostCitizenVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/citizenPublicApi";
-import { AppointmentFormContent } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentFormContent";
-import { initialValues } from "@/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory";
-import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
-import { MultiStepFormWrapper } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormWrapper";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-import { mapToApiPostCitizenVaccinationConsultationRequest } from "@/lib/businessModules/travelMedicine/helpers/appointmentFormHelper";
-import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
-import { useTranslation } from "@/lib/i18n/client";
-
-export function AppointmentFormWrapper() {
-  const postCitizenVaccinationConsultation =
-    usePostCitizenVaccinationConsultation();
-
-  const { t } = useTranslation(["travelMedicine/forms"]);
-
-  const router = useRouter();
-  const citizenRoutes = useCitizenRoutes();
-
-  const { currentStepIndex, totalSteps } = useStepContext();
-
-  async function handleSubmit(values: FormikValues, resetForm: () => void) {
-    const request = mapToApiPostCitizenVaccinationConsultationRequest(
-      values as InitialAppointmentFormValues,
-    );
-    await postCitizenVaccinationConsultation.mutateAsync(request, {
-      onSuccess: () => {
-        resetForm();
-        // change when successPage is present
-        router.push(citizenRoutes.overview);
-      },
-    });
-  }
-
-  return (
-    <MultiStepFormWrapper
-      stepperTitle={t("common.stepperTitle", {
-        currentStepIndex: currentStepIndex + 1,
-        totalSteps: totalSteps,
-      })}
-      title={t("common.title")}
-      initialValues={initialValues}
-      onSubmit={(values, { resetForm }) => handleSubmit(values, resetForm)}
-      withLogoutButton={false}
-    >
-      <AppointmentFormContent />
-    </MultiStepFormWrapper>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
index 51fcfc075..00dba8cad 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/AppointmentStepper.tsx
@@ -3,15 +3,28 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { AppointmentFormWrapper } from "@/lib/businessModules/travelMedicine/components/appointment/AppointmentFormWrapper";
+import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
+import { Formik, FormikHelpers } from "formik";
+import { useRouter } from "next/navigation";
+
+import { usePostCitizenVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/citizenPublicApi";
+import { initialValues } from "@/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory";
 import { AppointmentTypeStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep";
 import { PersonalDataStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/PersonalDataStep";
 import { TravelDataStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/TravelDataStep";
 import { TravelTypeStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/TravelTypeStep";
+import { VaccinationStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep";
 import { AppointmentReviewStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentReviewFormStep/AppointmentReviewStep";
 import { AppointmentSlotStep } from "@/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentSlotStep";
+import { AppointmentOverview } from "@/lib/businessModules/travelMedicine/components/appointment/steps/overview/AppointmentOverview";
+import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
+import { MultiStepFormTitle } from "@/lib/businessModules/travelMedicine/components/shared/components/multiStepForm/MultiStepFormWrapper";
 import { DepartmentContextProvider } from "@/lib/businessModules/travelMedicine/components/shared/contexts/DepartmentContext";
 import { StepContextProvider } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
+import { mapToApiPostCitizenVaccinationConsultationRequest } from "@/lib/businessModules/travelMedicine/helpers/appointmentFormHelper";
+import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { useTranslation } from "@/lib/i18n/client";
+import { TwoColumnGrid } from "@/lib/shared/components/layout/grid";
 
 export enum StepKey {
   AppointmentTypeStep = "AppointmentTypeStep",
@@ -19,25 +32,72 @@ export enum StepKey {
   TravelTypeStep = "TravelTypeStep",
   TravelDataStep = "TravelDataStep",
   PersonalDataStep = "PersonalDataStep",
+  VaccinationStep = "VaccinationStep",
   AppointmentReviewStep = "AppointmentReviewStep",
 }
 
+const appointmentFormSteps = [
+  <AppointmentTypeStep key={StepKey.AppointmentTypeStep} />,
+  <AppointmentSlotStep key={StepKey.AppointmentSlotStep} />,
+  <TravelTypeStep key={StepKey.TravelTypeStep} />,
+  <TravelDataStep key={StepKey.TravelDataStep} />,
+  <PersonalDataStep key={StepKey.PersonalDataStep} />,
+  <VaccinationStep key={StepKey.VaccinationStep} />,
+  <AppointmentReviewStep key={StepKey.AppointmentReviewStep} />,
+];
+
 export function AppointmentStepper() {
-  const appointmentFormSteps = [
-    <AppointmentTypeStep key={StepKey.AppointmentTypeStep} />,
-    <AppointmentSlotStep key={StepKey.AppointmentSlotStep} />,
-    <TravelTypeStep key={StepKey.TravelTypeStep} />,
-    <TravelDataStep key={StepKey.TravelDataStep} />,
-    <PersonalDataStep key={StepKey.PersonalDataStep} />,
-    // <VaccinationStep key={"VaccinationStep"} />,
-    <AppointmentReviewStep key={StepKey.AppointmentReviewStep} />,
-  ];
+  const { t } = useTranslation(["travelMedicine/forms"]);
+  const postCitizenVaccinationConsultation =
+    usePostCitizenVaccinationConsultation();
+  const router = useRouter();
+  const citizenRoutes = useCitizenRoutes();
+
+  async function handleSubmit(
+    values: InitialAppointmentFormValues,
+    helpers: FormikHelpers<InitialAppointmentFormValues>,
+  ) {
+    const request = mapToApiPostCitizenVaccinationConsultationRequest(values);
+    await postCitizenVaccinationConsultation.mutateAsync(request, {
+      onSuccess: () => {
+        helpers.resetForm();
+        // change when successPage is present
+        router.push(citizenRoutes.overview);
+      },
+    });
+  }
 
   return (
-    <StepContextProvider steps={appointmentFormSteps}>
-      <DepartmentContextProvider>
-        <AppointmentFormWrapper />
-      </DepartmentContextProvider>
-    </StepContextProvider>
+    <DepartmentContextProvider>
+      <StepContextProvider steps={appointmentFormSteps}>
+        {({ currentNode, currentStepIndex, totalSteps, isLastStep }) => (
+          <>
+            <MultiStepFormTitle
+              title={t("common.title")}
+              stepperTitle={t("common.stepperTitle", {
+                currentStepIndex: currentStepIndex,
+                totalSteps: totalSteps,
+              })}
+              withLogoutButton={false}
+            />
+            <Formik
+              initialValues={initialValues}
+              onSubmit={(values, helpers) => handleSubmit(values, helpers)}
+            >
+              <FormPlus>
+                {!isLastStep ? (
+                  <TwoColumnGrid
+                    content={currentNode}
+                    sidePanel={<AppointmentOverview />}
+                  />
+                ) : (
+                  currentNode
+                )}
+              </FormPlus>
+            </Formik>
+          </>
+        )}
+      </StepContextProvider>
+    </DepartmentContextProvider>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx
deleted file mode 100644
index 3bb8248c9..000000000
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/OverviewAndAppointmentStepToggle.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { AppointmentOverview } from "@/lib/businessModules/travelMedicine/components/appointment/steps/overview/AppointmentOverview";
-import { useStepContext } from "@/lib/businessModules/travelMedicine/components/shared/contexts/StepContext";
-import { TwoColumnGrid } from "@/lib/shared/components/layout/grid";
-
-export function OverviewAndAppointmentStepToggle() {
-  const { steps, currentStepIndex, isShowOverview } = useStepContext();
-
-  return (
-    <>
-      {isShowOverview ? (
-        <TwoColumnGrid
-          content={steps[currentStepIndex]}
-          sidePanel={<AppointmentOverview />}
-        />
-      ) : (
-        steps[currentStepIndex]
-      )}
-    </>
-  );
-}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
index 6d568d06b..99d9cdeed 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/appointmentFormValuesFactory.ts
@@ -4,6 +4,7 @@
  */
 
 import {
+  InitialAppointmentFormValues,
   PatientFormValues,
   TravelInformationFormValues,
 } from "@/lib/businessModules/travelMedicine/components/appointment/types";
@@ -28,7 +29,7 @@ export function createInitialTravelInformation(): TravelInformationFormValues {
   };
 }
 
-export const initialValues = {
+export const initialValues: InitialAppointmentFormValues = {
   patient: createInitialPatient(),
   travelInformation: createInitialTravelInformation(),
   initialStepAppointmentType: "",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
index 61ea5b232..c337819a3 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/AppointmentTypeStep.tsx
@@ -3,13 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiAppointmentType } from "@eshg/citizen-portal-api/travelMedicine";
+import {
+  ApiAppointmentType,
+  ApiAppointmentTypeConfig,
+} from "@eshg/citizen-portal-api/travelMedicine";
 import { Alert } from "@eshg/lib-portal/components/Alert";
 import { List, ListItem, Sheet, Stack, Typography } from "@mui/joy";
 import { useFormikContext } from "formik";
 import { TOptions } from "i18next";
 import { ReactNode, useState } from "react";
 
+import { useGetAllAppointmentTypesForCitizen } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import { InitialAppointmentFormValues } from "@/lib/businessModules/travelMedicine/components/appointment/types";
 import {
   FormSheet,
@@ -23,9 +27,18 @@ import { useDepartmentContext } from "@/lib/businessModules/travelMedicine/compo
 import { useTranslation } from "@/lib/i18n/client";
 
 function handleModalText(
+  allAppointmentTypesForCitizen: ApiAppointmentTypeConfig[],
   modalTitle: string,
   t: (key: string | string[], tOptions?: TOptions) => string,
 ): ReactNode {
+  const vaccinationStandardDuration = allAppointmentTypesForCitizen.find(
+    (type) => type.appointmentTypeDto == ApiAppointmentType.Vaccination,
+  )!.standardDurationInMinutes;
+
+  const consultationStandardDuration = allAppointmentTypesForCitizen.find(
+    (type) => type.appointmentTypeDto == ApiAppointmentType.Consultation,
+  )!.standardDurationInMinutes;
+
   if (
     modalTitle === t("appointmentTypeFormContent.fields.vaccination.modalTitle")
   ) {
@@ -34,6 +47,7 @@ function handleModalText(
         <Typography>
           {t(
             "appointmentTypeFormContent.fields.vaccination.modalTextParagraph1",
+            { appointmentDuration: vaccinationStandardDuration },
           )}
         </Typography>
         <Typography>
@@ -52,6 +66,7 @@ function handleModalText(
         <Typography>
           {t(
             "appointmentTypeFormContent.fields.consultation.modalTextParagraph1",
+            { appointmentDuration: consultationStandardDuration },
           )}
         </Typography>
         <Typography>
@@ -65,14 +80,9 @@ function handleModalText(
     modalTitle === t("appointmentTypeFormContent.confirmation.modalTitle")
   ) {
     return (
-      <>
-        <Typography>
-          {t("appointmentTypeFormContent.confirmation.modalTextParagraph1")}
-        </Typography>
-        <Typography>
-          {t("appointmentTypeFormContent.confirmation.modalTextParagraph2")}
-        </Typography>
-      </>
+      <Typography>
+        {t("appointmentTypeFormContent.confirmation.modalText")}
+      </Typography>
     );
   }
 }
@@ -93,6 +103,9 @@ export function AppointmentTypeStep() {
     void setFieldValue("appointmentBlockDate", "");
   }
 
+  const allAppointmentTypesForCitizen =
+    useGetAllAppointmentTypesForCitizen().data;
+
   return (
     <>
       <FormSheet data-testid="appointment-type-content-form">
@@ -209,7 +222,7 @@ export function AppointmentTypeStep() {
         onClose={() => setIsOpen((isOpen) => !isOpen)}
         open={isOpen}
       >
-        {handleModalText(modalTitle, t)}
+        {handleModalText(allAppointmentTypesForCitizen, modalTitle, t)}
       </InfoModal>
     </>
   );
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
index a4329ea0f..4a873bf06 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/VaccinationStep.tsx
@@ -3,6 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import {
+  LOCALE_OPTION,
+  formatCurrency,
+} from "@eshg/lib-portal/formatters/numbers";
+import { List, ListItem, ListItemContent, Stack, Typography } from "@mui/joy";
+
+import { useGetAllDiseasesCitizen } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import {
   FormSheet,
   FormSheetTitle,
@@ -11,10 +18,32 @@ import { useTranslation } from "@/lib/i18n/client";
 
 export function VaccinationStep() {
   const { t } = useTranslation(["travelMedicine/forms"]);
+  const diseases = useGetAllDiseasesCitizen().data.diseases;
 
   return (
-    <FormSheet>
+    <FormSheet data-testid="vaccination-data-content-form">
       <FormSheetTitle>{t("vaccinationFormContent.title")}</FormSheetTitle>
+      <Stack gap={2}>
+        <Typography level={"body-md"}>
+          {t("vaccinationFormContent.info")}
+        </Typography>
+        <Stack width={"50%"}>
+          <List>
+            {diseases.map((el, index) => (
+              <ListItem key={`vaccine[${el.name}.${index}]`}>
+                <ListItemContent>
+                  <Typography level="body-md">{el.name}</Typography>
+                </ListItemContent>
+                <Typography level="body-sm">
+                  {formatCurrency(el.estimatedFee, {
+                    localOption: LOCALE_OPTION.auto,
+                  })}
+                </Typography>
+              </ListItem>
+            ))}
+          </List>
+        </Stack>
+      </Stack>
     </FormSheet>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
index d0c419612..a1ac3f4a0 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/steps/appointmentSlotStep/AppointmentContent.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiAppointmentType } from "@eshg/citizen-portal-api/travelMedicine";
 import { isAfter, isEqual } from "date-fns";
 import { useFormikContext } from "formik";
 import { useEffect } from "react";
@@ -21,7 +22,7 @@ export function AppointmentContent() {
   const citizenRoutes = useCitizenRoutes();
 
   const freeAppointments = useGetFreeAppointmentsForCitizen(
-    values.initialStepAppointmentType,
+    values.initialStepAppointmentType as ApiAppointmentType,
   ).data;
 
   const filteredAppointments = freeAppointments.appointments.filter(
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
index 433e90529..7f7bfa8c0 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/appointment/types.ts
@@ -14,7 +14,7 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 export interface AppointmentFormValues {
   patient: PatientFormValues;
   travelInformation: TravelInformationFormValues;
-  initialStepAppointmentType: ApiAppointmentType;
+  initialStepAppointmentType: OptionalFieldValue<ApiAppointmentType>;
   appointmentStart: string;
   durationInMinutes: string;
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
index 6209fbc4d..c5d3c7713 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/AppointmentSection.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Button, Typography } from "@mui/joy";
+import { Button, Stack, Typography } from "@mui/joy";
 import { useRouter } from "next/navigation";
 
 import { useCitizenRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
@@ -34,23 +34,25 @@ export function AppointmentSection() {
         {t("appointment.bookAppointmentTitle")}
       </ContentSheetTitle>
       <Typography>{t("appointment.bookAppointmentText")}</Typography>
-      <Button
-        type="submit"
-        onClick={() => {
-          handleBookAppointment();
-        }}
-      >
-        {t("appointment.bookAppointment")}
-      </Button>
-      <Button
-        type="submit"
-        variant="outlined"
-        onClick={() => {
-          handleAppointmentLogin();
-        }}
-      >
-        {t("appointment.myAppointment")}
-      </Button>
+      <Stack direction="column" gap={2}>
+        <Button
+          type="submit"
+          onClick={() => {
+            handleBookAppointment();
+          }}
+        >
+          {t("appointment.bookAppointment")}
+        </Button>
+        <Button
+          type="submit"
+          variant="outlined"
+          onClick={() => {
+            handleAppointmentLogin();
+          }}
+        >
+          {t("appointment.myAppointment")}
+        </Button>
+      </Stack>
     </ContentSheet>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
index 5dba2f3c4..1969bc020 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/landing/LandingpageContent.tsx
@@ -11,7 +11,10 @@ import {
 } from "@mui/icons-material";
 import { Typography } from "@mui/joy";
 
-import { useGetDepartmentInfo } from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
+import {
+  useGetDepartmentInfo,
+  useGetOpeningHours,
+} from "@/lib/businessModules/travelMedicine/api/queries/citizenPublicApi";
 import { useTranslation } from "@/lib/i18n/client";
 import {
   InfoSection,
@@ -23,10 +26,6 @@ import {
   ContentSheetTitle,
 } from "@/lib/shared/components/layout/contentSheet";
 import { GridColumnStack } from "@/lib/shared/components/layout/grid";
-import {
-  TableListing,
-  TableListingRow,
-} from "@/lib/shared/components/tableListing";
 import {
   formatPostalCodeAndCity,
   formatStreetAndHouseNumber,
@@ -51,7 +50,7 @@ export function LandingpageContent() {
   );
 }
 
-function AddressSection(props: DepartmentInfoProps) {
+function AddressSection(props: Readonly<DepartmentInfoProps>) {
   const { t } = useTranslation(["travelMedicine/landing"]);
 
   return (
@@ -69,37 +68,28 @@ function AddressSection(props: DepartmentInfoProps) {
 }
 
 function OpeningHoursSection() {
-  const { t } = useTranslation(["travelMedicine/landing"]);
+  const { t, i18n } = useTranslation(["travelMedicine/landing"]);
+  const { data: openingHours } = useGetOpeningHours();
+  let openingHoursInSelectedLanguage;
+  if (i18n.language === "de") {
+    openingHoursInSelectedLanguage = openingHours.de;
+  } else {
+    openingHoursInSelectedLanguage = openingHours.en;
+  }
 
   return (
     <InfoSection icon={<AccessTimeOutlined />}>
       <InfoSectionTitle>{t("contact.openingHours")}</InfoSectionTitle>
-      <TableListing>
-        <tr>
-          <td colSpan={2} style={{ paddingBottom: 3 }}>
-            {t("contact.consultationHours")}
-          </td>
-        </tr>
-        <TableListingRow label={t("contact.moDoLabel")}>
-          {t("contact.moDoValue")}
-        </TableListingRow>
-        <tr>
-          <td colSpan={2} style={{ paddingTop: 3, paddingBottom: 3 }}>
-            {t("contact.telephoneBooking")}
-          </td>
-        </tr>
-        <TableListingRow label={t("contact.moMiLabel")}>
-          {t("contact.moMiValue")}
-        </TableListingRow>
-        <TableListingRow label={t("contact.frLabel")}>
-          {t("contact.frValue")}
-        </TableListingRow>
-      </TableListing>
+      {openingHoursInSelectedLanguage.map((openingHour) => (
+        <p style={{ margin: 0 }} key={openingHour}>
+          {openingHour}
+        </p>
+      ))}
     </InfoSection>
   );
 }
 
-function ContactDataSection(props: DepartmentInfoProps) {
+function ContactDataSection(props: Readonly<DepartmentInfoProps>) {
   const { t } = useTranslation(["travelMedicine/landing"]);
 
   return (
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
index bd5a334d6..12eddd95c 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistorySidePanel.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistoryContent } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentContent } from "@eshg/citizen-portal-api/travelMedicine";
 import { Button } from "@mui/joy";
 import { Dispatch, SetStateAction } from "react";
 
@@ -13,7 +13,7 @@ import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
 interface MedicalHistorySidePanel {
   currentStep: number;
   setCurrentStep: Dispatch<SetStateAction<number>>;
-  medicalHistory: ApiMedicalHistoryContent;
+  medicalHistory: ApiDocumentContent;
   onRouteBack: () => void;
 }
 
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
index 19c81ebed..bca1e18f6 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/medicalHistory/MedicalHistoryStepper.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistoryContent } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentContent } from "@eshg/citizen-portal-api/travelMedicine";
 import { Alert } from "@eshg/lib-portal/components/Alert";
 import { FormikValues } from "formik";
 import { useRouter, useSearchParams } from "next/navigation";
@@ -54,7 +54,7 @@ export function MedicalHistoryStepper() {
     const request: PatchMedicalHistoryRequest = {
       procedureId: procedureId!,
       procedureStepId: procedureStepId!,
-      medicalHistory: values as ApiMedicalHistoryContent,
+      medicalHistory: values as ApiDocumentContent,
     };
     await patchCitizenMedicalHistory.mutateAsync(request, {
       onSuccess: () => routeBackToDetails(),
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
similarity index 73%
rename from citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx
rename to citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
index bde942f64..6d3e3b52f 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentAnamnesisQuestion } from "@eshg/citizen-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { Stack } from "@mui/joy";
 import { FieldConfig, FieldInputProps } from "formik";
@@ -12,17 +12,17 @@ import { DocumentMultiSelectElement } from "@/lib/businessModules/travelMedicine
 import { DocumentRadioButtonElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement";
 import { DocumentTextareaElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentTextareaElement";
 
-interface DocumentElementProps {
+interface AnamnesisQuestionProps {
   currentStep: number;
   index: number;
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   setFieldValue: SetFieldValueHelper;
   getFieldProps: <Value>(
     props: string | FieldConfig<Value>,
   ) => FieldInputProps<Value>;
 }
 
-export function DocumentElement(props: Readonly<DocumentElementProps>) {
+export function AnamnesisQuestion(props: Readonly<AnamnesisQuestionProps>) {
   return (
     <>
       <DocumentRadioButtonElement
@@ -31,11 +31,11 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
           props.currentStep +
           "].sectionElements[" +
           props.index +
-          "].elementData.answer"
+          "].anamnesisQuestion.answer"
         }
-        label={props.element.elementData.questionText}
+        label={props.anamnesisQuestion.questionText}
         setFieldValue={props.setFieldValue}
-        element={props.element}
+        anamnesisQuestion={props.anamnesisQuestion}
         elementIndex={props.index}
         sectionIndex={props.currentStep}
       />
@@ -45,13 +45,13 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
             props.currentStep +
             "].sectionElements[" +
             props.index +
-            "].elementData.answer",
+            "].anamnesisQuestion.answer",
         ).value as string
       )?.toString() === "true" && (
         <>
-          {props.element.elementData.subElementMultiSelect.length > 0 && (
+          {props.anamnesisQuestion.subElementMultiSelect.length > 0 && (
             <DocumentMultiSelectElement
-              element={props.element}
+              anamnesisQuestion={props.anamnesisQuestion}
               elementIndex={props.index}
               sectionIndex={props.currentStep}
               name={
@@ -59,11 +59,11 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
                 props.currentStep +
                 "].sectionElements[" +
                 props.index +
-                "].elementData.subElementMultiSelect"
+                "].anamnesisQuestion.subElementMultiSelect"
               }
             />
           )}
-          {props.element.elementData.subElementText && (
+          {props.anamnesisQuestion.subElementText && (
             <Stack
               style={{
                 marginLeft: "20px",
@@ -76,9 +76,9 @@ export function DocumentElement(props: Readonly<DocumentElementProps>) {
                   props.currentStep +
                   "].sectionElements[" +
                   props.index +
-                  "].elementData.subElementText.answer"
+                  "].anamnesisQuestion.subElementText.answer"
                 }
-                label={props.element.elementData.subElementText.questionText}
+                label={props.anamnesisQuestion.subElementText.questionText}
               ></DocumentTextareaElement>
             </Stack>
           )}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx
new file mode 100644
index 000000000..8c913ccd9
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement.tsx
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentConfirmation } from "@eshg/citizen-portal-api/travelMedicine";
+import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
+import { Checkbox } from "@mui/joy";
+
+interface ConfirmationElementProps {
+  currentStep: number;
+  index: number;
+  confirmation: ApiDocumentConfirmation;
+  setFieldValue: SetFieldValueHelper;
+}
+
+export function ConfirmationElement({
+  currentStep,
+  index,
+  confirmation,
+  setFieldValue,
+}: Readonly<ConfirmationElementProps>) {
+  const name = `sections[${currentStep}].sectionElements[${index}].confirmation.answer`;
+  return (
+    <Checkbox
+      name={name}
+      onChange={async (event) => setFieldValue(name, event.target.checked)}
+      label={confirmation.confirmationTextField}
+      checked={confirmation.answer}
+    />
+  );
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
index defc54587..d31ca2f07 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentMultiSelectElement.tsx
@@ -4,8 +4,8 @@
  */
 
 import {
-  ApiMedicalHistorySectionElement,
-  ApiMedicalHistorySubElementMultiSelect,
+  ApiDocumentAnamnesisQuestion,
+  ApiDocumentSubElementMultiSelect,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import {
   BaseFieldProps,
@@ -18,22 +18,22 @@ import { useTranslation } from "@/lib/i18n/client";
 
 interface DocumentMultiSelectElementProps
   extends Omit<BaseFieldProps, "required" | "children"> {
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   sectionIndex: number;
   elementIndex: number;
   name: string;
 }
 
 export function DocumentMultiSelectElement({
-  element,
+  anamnesisQuestion,
   sectionIndex,
   elementIndex,
   ...restProps
 }: Readonly<DocumentMultiSelectElementProps>) {
   const { t } = useTranslation(["travelMedicine/document"]);
-  const { input, helpers } = useBaseField<
-    ApiMedicalHistorySubElementMultiSelect[]
-  >({ ...restProps });
+  const { input, helpers } = useBaseField<ApiDocumentSubElementMultiSelect[]>({
+    ...restProps,
+  });
 
   async function handleCheckboxChange(
     event: ChangeEvent<HTMLInputElement>,
@@ -75,7 +75,7 @@ export function DocumentMultiSelectElement({
         aria-labelledby="checkbox-group"
         sx={{ rowGap: 4 }}
       >
-        {element.elementData.subElementMultiSelect.map(
+        {anamnesisQuestion.subElementMultiSelect.map(
           ({ questionText }, index) => (
             <ListItem
               key={"multiselect" + elementIndex + "-" + index}
@@ -92,7 +92,7 @@ export function DocumentMultiSelectElement({
                   sectionIndex +
                   "].sectionElements[" +
                   elementIndex +
-                  "].elementData.subElementMultiSelect[" +
+                  "].anamnesisQuestion.subElementMultiSelect[" +
                   index +
                   "].answer"
                 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
index 4758dbd62..7efb1e075 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentRadioButtonElement.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/citizen-portal-api/travelMedicine";
+import { ApiDocumentAnamnesisQuestion } from "@eshg/citizen-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { Typography, styled } from "@mui/joy";
 
@@ -14,7 +14,7 @@ interface DocumentRadioButtonElementProps {
   name: string;
   label: string;
   setFieldValue: SetFieldValueHelper;
-  element: ApiMedicalHistorySectionElement;
+  anamnesisQuestion: ApiDocumentAnamnesisQuestion;
   sectionIndex: number;
   elementIndex: number;
 }
@@ -22,7 +22,7 @@ interface DocumentRadioButtonElementProps {
 export function DocumentRadioButtonElement({
   name,
   label,
-  element,
+  anamnesisQuestion,
   setFieldValue,
   sectionIndex,
   elementIndex,
@@ -47,19 +47,19 @@ export function DocumentRadioButtonElement({
       ]}
       onChange={async (event) => {
         if (event.target.value === "false" || !event.target.value) {
-          if (element.elementData.subElementText) {
+          if (anamnesisQuestion.subElementText) {
             await setFieldValue(
               "sections[" +
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementText.answer",
+                "].anamnesisQuestion.subElementText.answer",
               "",
             );
           }
           for (
             let i = 0;
-            i < element.elementData.subElementMultiSelect.length;
+            i < anamnesisQuestion.subElementMultiSelect.length;
             i++
           ) {
             await setFieldValue(
@@ -67,7 +67,7 @@ export function DocumentRadioButtonElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 i +
                 "].answer",
               false,
@@ -78,7 +78,7 @@ export function DocumentRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             false,
           );
         } else {
@@ -87,7 +87,7 @@ export function DocumentRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             true,
           );
         }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
index ba75d4118..af2d4ee06 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/DocumentSection.tsx
@@ -4,25 +4,27 @@
  */
 
 import {
-  ApiMedicalHistoryContent,
-  ApiMedicalHistorySection,
+  ApiDocumentContent,
+  ApiDocumentSection,
 } from "@eshg/citizen-portal-api/travelMedicine";
 import { Stack } from "@mui/joy";
 import { useFormikContext } from "formik";
 import { ReactNode } from "react";
 
-import { DocumentElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/DocumentElement";
+import { AnamnesisQuestion } from "@/lib/businessModules/travelMedicine/components/shared/components/document/AnamnesisQuestion";
+import { ConfirmationElement } from "@/lib/businessModules/travelMedicine/components/shared/components/document/ConfirmationElement";
+import { TextBlock } from "@/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock";
 import { ContentSheet } from "@/lib/shared/components/layout/contentSheet";
 
 interface DocumentProps {
   currentStep: number;
-  documentSection: ApiMedicalHistorySection;
+  documentSection: ApiDocumentSection;
   documentBriefing?: ReactNode;
 }
 
 export function DocumentSection(props: Readonly<DocumentProps>) {
   const { setFieldValue, getFieldProps } =
-    useFormikContext<ApiMedicalHistoryContent>();
+    useFormikContext<ApiDocumentContent>();
 
   return (
     <ContentSheet data-testid={`document-section-${props.currentStep}`}>
@@ -30,13 +32,26 @@ export function DocumentSection(props: Readonly<DocumentProps>) {
       <Stack gap={4}>
         {props.documentSection.sectionElements.map((element, index) => (
           <Stack gap={2} key={index} data-testid={`document-element-${index}`}>
-            <DocumentElement
-              currentStep={props.currentStep}
-              index={index}
-              element={element}
-              setFieldValue={setFieldValue}
-              getFieldProps={getFieldProps}
-            />
+            <>
+              {element.anamnesisQuestion && (
+                <AnamnesisQuestion
+                  currentStep={props.currentStep}
+                  index={index}
+                  anamnesisQuestion={element.anamnesisQuestion}
+                  setFieldValue={setFieldValue}
+                  getFieldProps={getFieldProps}
+                />
+              )}
+              {element.confirmation && (
+                <ConfirmationElement
+                  currentStep={props.currentStep}
+                  index={index}
+                  confirmation={element.confirmation}
+                  setFieldValue={setFieldValue}
+                />
+              )}
+              {element.textBlock && <TextBlock textBlock={element.textBlock} />}
+            </>
           </Stack>
         ))}
       </Stack>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx
new file mode 100644
index 000000000..f5e63d670
--- /dev/null
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/components/document/TextBlock.tsx
@@ -0,0 +1,14 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentTextBlock } from "@eshg/citizen-portal-api/travelMedicine";
+import { Box } from "@mui/joy";
+
+interface TextBlockProps {
+  textBlock: ApiDocumentTextBlock;
+}
+export function TextBlock(props: Readonly<TextBlockProps>) {
+  return <Box sx={{ whiteSpace: "pre-wrap" }}>{props.textBlock.textField}</Box>;
+}
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
index a48a22283..dda232008 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/shared/contexts/StepContext.tsx
@@ -3,55 +3,108 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { RequiresChildren } from "@eshg/lib-portal/types/react";
-import { createContext, useCallback, useContext, useState } from "react";
+import {
+  ReactNode,
+  createContext,
+  useCallback,
+  useContext,
+  useMemo,
+  useState,
+} from "react";
+import { isDefined } from "remeda";
 
 interface StepContextProps {
-  steps: JSX.Element[];
   totalSteps: number;
   currentStepIndex: number;
   isFirstStep: boolean;
   isLastStep: boolean;
-  onStepChange: (curStep: number) => void;
   onShowOverviewChange: (showOverview: boolean) => void;
   isShowOverview: boolean;
+  goForward: (numOfPages?: number) => void;
+  goBack: (numOfPages?: number) => void;
+  currentNode?: JSX.Element;
 }
+export const StepContext = createContext<StepContextProps | null>(null);
 
-interface StepContextProviderProps extends RequiresChildren {
+interface StepContextProviderProps {
   steps: JSX.Element[];
+  children: (values: StepContextProps) => ReactNode;
 }
 
-export const StepContext = createContext<StepContextProps | null>(null);
-
-export function StepContextProvider(props: Readonly<StepContextProviderProps>) {
-  const steps = props.steps;
+export function StepContextProvider({
+  steps,
+  children,
+}: StepContextProviderProps) {
   const totalSteps = steps.length;
 
-  const [currentStepIndex, setCurrentStepIndex] = useState(0);
+  const [currentStepIndex, setCurrentStepIndex] = useState(1);
   const [isShowOverview, setIsShowOverview] = useState(true);
-
-  const onStepChange = useCallback((newStep: number) => {
-    setCurrentStepIndex(newStep);
-  }, []);
+  const currentNode = steps[currentStepIndex - 1];
+  if (currentNode === undefined) {
+    throw new Error(
+      `StepFactory[] out of bounds. Tried to access index ${currentStepIndex - 1} but length is ${steps.length}`,
+    );
+  }
 
   const onShowOverviewChange = useCallback((displayOverview: boolean) => {
     setIsShowOverview(displayOverview);
   }, []);
 
+  const goForward = useCallback(
+    function (numOfPages?: number) {
+      if (currentStepIndex < steps.length) {
+        setCurrentStepIndex(
+          (prev) => prev + (isDefined(numOfPages) ? numOfPages : 1),
+        );
+      }
+    },
+    [currentStepIndex, steps, setCurrentStepIndex],
+  );
+
+  const goBack = useCallback(
+    function (numOfPages?: number) {
+      if (currentStepIndex > 1) {
+        setCurrentStepIndex(
+          (prev) => prev - (isDefined(numOfPages) ? numOfPages : 1),
+        );
+      }
+    },
+    [currentStepIndex, setCurrentStepIndex],
+  );
+
+  const isFirstStep = currentStepIndex === 1;
+  const isLastStep = currentStepIndex === totalSteps;
+
+  const contextValue = useMemo(
+    () => ({
+      goForward,
+      goBack,
+      currentStepIndex,
+      totalSteps,
+      isFirstStep,
+      isLastStep,
+      onShowOverviewChange,
+      isShowOverview,
+      currentNode,
+    }),
+    [
+      goForward,
+      goBack,
+      currentStepIndex,
+      totalSteps,
+      onShowOverviewChange,
+      isShowOverview,
+      isFirstStep,
+      isLastStep,
+      currentNode,
+    ],
+  );
+
   return (
-    <StepContext.Provider
-      value={{
-        steps,
-        totalSteps,
-        currentStepIndex,
-        isFirstStep: currentStepIndex === 0,
-        isLastStep: currentStepIndex === totalSteps - 1,
-        onStepChange,
-        onShowOverviewChange,
-        isShowOverview,
-      }}
-    >
-      {props.children}
+    <StepContext.Provider value={contextValue}>
+      {children({
+        ...contextValue,
+      })}
     </StepContext.Provider>
   );
 }
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
index e7a995a4e..b51f73a88 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsAdditionalInformation.tsx
@@ -67,7 +67,9 @@ export function AppointmentDetailsAdditionalInformation(
         </ContentSheetTitle>
         <Box sx={isMobile ? BOX_STYLE_MOBILE : BOX_STYLE}>
           <AppointmentDetailsMedicalHistoryInformation
-            citizenHasAnswered={props.appointmentDetails.citizenHasAnswered}
+            citizenHasAnswered={
+              props.appointmentDetails.medicalHistoryCitizenHasAnswered
+            }
           />
         </Box>
         <InfoSection sx={{ padding: "0 24px 24px 24px" }}>
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
index 0e7c4508a..e6252f4fc 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/components/viewAppointment/AppointmentDetailsSidePanel.tsx
@@ -60,7 +60,7 @@ export function AppointmentDetailsSidePanel({
         {!hasAccomplishedService && (
           <>
             <ContentSheetTitle sx={{ paddingBottom: "8px" }}>
-              {t("sidePanel.title")}
+              {t("sidePanel.title", { context: isBooked().toString() })}
             </ContentSheetTitle>
             {bookingsRemaining() && (
               <Button
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts b/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
index 17c5fb8ec..b649e290f 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/helpers/appointmentFormHelper.ts
@@ -74,7 +74,7 @@ function mapToApiTravelInformation(
     travelTimeUnit: mapOptionalValue(travelInformation.travelTimeUnit),
     travelType: !isEmptyString(travelInformation.travelType)
       ? travelInformation.travelType
-      : ApiTravelType.Unspecified,
+      : ApiTravelType.NoTravel,
   };
 }
 
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
index 995f7035b..c784ab978 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/appointmentDetails.json
@@ -5,7 +5,8 @@
   },
   "title": "Informationen",
   "sidePanel": {
-    "title": "Sie können den Termin nicht wahrnehmen?",
+    "title_false": "Wählen Sie jetzt Ihren Termin",
+    "title_true": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
     "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
index ebda9612b..5b5c172d4 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/forms.json
@@ -7,31 +7,30 @@
   "appointmentTypeFormContent": {
     "title": "Terminart",
     "infoHeader": "Wichtige Informationen zum Termin",
-    "infoTextListItem1": "Bitte bringen Sie Ihre Impfpass mit.",
+    "infoTextListItem1": "Bitte bringen Sie Ihren Impfpass mit.",
     "infoTextListItem2": "Für größere Gruppen (mehr als 3 Personen) nutzen Sie bitte die telefonische Terminvereinbarung unter {{ phoneNumber }}.",
     "confirmation": {
       "label": "Beglaubigung/Apostille ",
       "subtitle": "(Offene Sprechstunde)",
-      "info": "Nur telefonisch unter {{ phoneNumber }} buchbar:",
+      "info": "Keine Terminbuchung erforderlich für:",
       "iconLabel": "Information zu $t(appointmentTypeFormContent.confirmation.label)",
       "modalTitle": "$t(appointmentTypeFormContent.confirmation.label)",
-      "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten.",
-      "modalTextParagraph2": "Bitte Impfpass mitbringen."
+      "modalText": "Für dieses Anliegen ist keine Terminbuchung erforderlich. Bitte kommen Sie zur offenen Sprechstunde vorbei."
     },
     "fields": {
       "error": "Bitte Terminart auswählen.",
       "vaccination": {
-        "label": "Impfung",
+        "label": "Auffrischungsimpfung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.vaccination.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.vaccination.label)",
-        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. 15 Minuten. Auffrischungsimpfungen oder Folgeimpfungen nach erfolgter Erstberatung.",
+        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. {{ appointmentDuration }} Minuten.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       },
       "consultation": {
         "label": "Reiseberatung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.consultation.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.consultation.label)",
-        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
+        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. {{ appointmentDuration }} Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       }
     }
@@ -89,12 +88,12 @@
   },
   "vaccinationFormContent": {
     "title": "Impfungen",
-    "fields": {}
+    "info": "Diese Impfungen werden angeboten:"
   },
   "appointmentInfoSection": {
     "title": "Informationen zum Termin",
     "alertHeader": "Vorbereitungen zum Termin",
-    "alertMessage": "Bitte bringen Sie ihren Impfpass mit",
+    "alertMessage": "Bitte bringen Sie Ihren Impfpass mit",
     "infoText": "Sie erhalten in <t1>den nächsten Minuten</t1> eine <t1>Terminbestätigung</t1> per E-Mail. Dort sind alle Informationen zum Termin enthalten. Sie haben zudem die Möglichkeit den Termin zu ändern oder zu stornieren",
     "requiredDocumentsHeader": "Notwendige Dokumente, welche Sie bitte zum Termin mitbringen, sind:",
     "listItemIdCard": "Personalausweis",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
index f85ee75a0..1c9168705 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/de/landing.json
@@ -4,14 +4,6 @@
     "title": "Kontakt und Erreichbarkeit",
     "address": "Adresse",
     "openingHours": "Öffnungszeiten",
-    "consultationHours": "Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)",
-    "moDoLabel": "Mo bis Do",
-    "moDoValue": "08:00 bis 12:00 Uhr",
-    "telephoneBooking": "Telefonische Terminvereinbarung",
-    "moMiLabel": "Mo bis Mi",
-    "moMiValue": "14:00 bis 15:00 Uhr",
-    "frLabel": "Fr",
-    "frValue": "08:00 bis 10:00 Uhr",
     "contact": "Kontakt",
     "phoneNumber": "Telefon: {{ phoneNumber }}",
     "eMail": "E-Mail:"
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
index 995f7035b..c784ab978 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/appointmentDetails.json
@@ -5,7 +5,8 @@
   },
   "title": "Informationen",
   "sidePanel": {
-    "title": "Sie können den Termin nicht wahrnehmen?",
+    "title_false": "Wählen Sie jetzt Ihren Termin",
+    "title_true": "Sie können den Termin nicht wahrnehmen?",
     "postponeAppointment": "Termin verschieben",
     "bookAppointment": "Termin buchen",
     "cancelAppointment": "Termin absagen",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
index ebda9612b..5b5c172d4 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/forms.json
@@ -7,31 +7,30 @@
   "appointmentTypeFormContent": {
     "title": "Terminart",
     "infoHeader": "Wichtige Informationen zum Termin",
-    "infoTextListItem1": "Bitte bringen Sie Ihre Impfpass mit.",
+    "infoTextListItem1": "Bitte bringen Sie Ihren Impfpass mit.",
     "infoTextListItem2": "Für größere Gruppen (mehr als 3 Personen) nutzen Sie bitte die telefonische Terminvereinbarung unter {{ phoneNumber }}.",
     "confirmation": {
       "label": "Beglaubigung/Apostille ",
       "subtitle": "(Offene Sprechstunde)",
-      "info": "Nur telefonisch unter {{ phoneNumber }} buchbar:",
+      "info": "Keine Terminbuchung erforderlich für:",
       "iconLabel": "Information zu $t(appointmentTypeFormContent.confirmation.label)",
       "modalTitle": "$t(appointmentTypeFormContent.confirmation.label)",
-      "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten.",
-      "modalTextParagraph2": "Bitte Impfpass mitbringen."
+      "modalText": "Für dieses Anliegen ist keine Terminbuchung erforderlich. Bitte kommen Sie zur offenen Sprechstunde vorbei."
     },
     "fields": {
       "error": "Bitte Terminart auswählen.",
       "vaccination": {
-        "label": "Impfung",
+        "label": "Auffrischungsimpfung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.vaccination.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.vaccination.label)",
-        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. 15 Minuten. Auffrischungsimpfungen oder Folgeimpfungen nach erfolgter Erstberatung.",
+        "modalTextParagraph1": "Termindauer für Impfungen in der Regel ca. {{ appointmentDuration }} Minuten.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       },
       "consultation": {
         "label": "Reiseberatung",
         "iconLabel": "Information zu $t(appointmentTypeFormContent.fields.consultation.label)",
         "modalTitle": "$t(appointmentTypeFormContent.fields.consultation.label)",
-        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. 30 Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
+        "modalTextParagraph1": "Termindauer für Beratungsgespräche in der Regel ca. {{ appointmentDuration }} Minuten. Reisemedizinische Beratung mit oder ohne anschließenden Impfungen.",
         "modalTextParagraph2": "Bitte Impfpass mitbringen."
       }
     }
@@ -89,12 +88,12 @@
   },
   "vaccinationFormContent": {
     "title": "Impfungen",
-    "fields": {}
+    "info": "Diese Impfungen werden angeboten:"
   },
   "appointmentInfoSection": {
     "title": "Informationen zum Termin",
     "alertHeader": "Vorbereitungen zum Termin",
-    "alertMessage": "Bitte bringen Sie ihren Impfpass mit",
+    "alertMessage": "Bitte bringen Sie Ihren Impfpass mit",
     "infoText": "Sie erhalten in <t1>den nächsten Minuten</t1> eine <t1>Terminbestätigung</t1> per E-Mail. Dort sind alle Informationen zum Termin enthalten. Sie haben zudem die Möglichkeit den Termin zu ändern oder zu stornieren",
     "requiredDocumentsHeader": "Notwendige Dokumente, welche Sie bitte zum Termin mitbringen, sind:",
     "listItemIdCard": "Personalausweis",
diff --git a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
index f85ee75a0..1c9168705 100644
--- a/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
+++ b/citizen-portal/src/lib/businessModules/travelMedicine/locales/en/landing.json
@@ -4,14 +4,6 @@
     "title": "Kontakt und Erreichbarkeit",
     "address": "Adresse",
     "openingHours": "Öffnungszeiten",
-    "consultationHours": "Sprechzeiten nur nach Terminvereinbarung (telefonisch oder per E-Mail)",
-    "moDoLabel": "Mo bis Do",
-    "moDoValue": "08:00 bis 12:00 Uhr",
-    "telephoneBooking": "Telefonische Terminvereinbarung",
-    "moMiLabel": "Mo bis Mi",
-    "moMiValue": "14:00 bis 15:00 Uhr",
-    "frLabel": "Fr",
-    "frValue": "08:00 bis 10:00 Uhr",
     "contact": "Kontakt",
     "phoneNumber": "Telefon: {{ phoneNumber }}",
     "eMail": "E-Mail:"
diff --git a/citizen-portal/src/lib/i18n/client.ts b/citizen-portal/src/lib/i18n/client.ts
index 9676d87b4..f41a93307 100644
--- a/citizen-portal/src/lib/i18n/client.ts
+++ b/citizen-portal/src/lib/i18n/client.ts
@@ -54,8 +54,8 @@ function useTranslationWrapper(
       const ns = nsIndex >= 0 ? firstKey?.slice(0, nsIndex) : undefined;
       if (ns && !i18n.hasLoadedNamespace(ns)) {
         // eslint-disable-next-line @typescript-eslint/only-throw-error
-        throw new Promise((res) => {
-          void i18n.loadNamespaces(ns, res);
+        throw new Promise((resolve) => {
+          void i18n.loadNamespaces(ns, resolve);
         });
       }
       return t(key, tOptions);
diff --git a/citizen-portal/src/lib/shared/components/layout/page.tsx b/citizen-portal/src/lib/shared/components/layout/page.tsx
index 0bb82bcd1..9e51c0cf5 100644
--- a/citizen-portal/src/lib/shared/components/layout/page.tsx
+++ b/citizen-portal/src/lib/shared/components/layout/page.tsx
@@ -21,9 +21,10 @@ const MainContents = styled("main")({
   display: "contents",
 });
 
-const AlertContainer = styled(PageContent)({
+const AlertContainer = styled(PageContent)(({ theme }) => ({
   paddingBlockEnd: 0,
-});
+  gap: theme.spacing(2),
+}));
 
 interface PageLayoutProps extends RequiresChildren {
   banner?: BannerType;
diff --git a/config/eslint.base.js b/config/eslint.base.js
index 95e664336..7f58928ad 100644
--- a/config/eslint.base.js
+++ b/config/eslint.base.js
@@ -5,6 +5,7 @@
 
 import eslintConfigPrettier from "eslint-config-prettier";
 import eslintPluginImport from "eslint-plugin-import";
+import pluginPromise from "eslint-plugin-promise";
 import eslintPluginUnusedImports from "eslint-plugin-unused-imports";
 import tseslint from "typescript-eslint";
 
@@ -16,6 +17,7 @@ export const restrictRelativeImportsPattern = {
 export const eslintBaseConfig = tseslint.config(
   ...tseslint.configs.recommendedTypeChecked,
   ...tseslint.configs.stylisticTypeChecked,
+  pluginPromise.configs["flat/recommended"],
   eslintConfigPrettier,
   {
     plugins: {
@@ -75,6 +77,12 @@ export const eslintBaseConfig = tseslint.config(
       ],
 
       "import/no-default-export": "error",
+
+      // TODO: These rules are activated by default through the recommended rules of eslint-plugin-promise.
+      // However, there are some errors in the code caused by these rules that cannot be fixed trivially.
+      // Therefore, these rules are disabled for the time being.
+      "promise/catch-or-return": "off",
+      "promise/always-return": "off",
     },
   },
 );
diff --git a/docs/dependency-management.adoc b/docs/dependency-management.adoc
index d34d5f6ad..beda04304 100644
--- a/docs/dependency-management.adoc
+++ b/docs/dependency-management.adoc
@@ -20,7 +20,6 @@ Adding new dependencies can both impact the bundle size and security of our fron
 If after going through above questions you think it's necessary and reasonable to add the new dependency, please approach one of the frontend tech leads.
 The MR introducing the new dependency needs to be approved by a tech lead.
 
-
 == Updating dependencies
 
 === Add a new dependency
@@ -29,25 +28,53 @@ The MR introducing the new dependency needs to be approved by a tech lead.
 
 Add the new dependency to the `package.json` of the subproject, then in the top-level directory run:
 
-`./gradlew installDependency`
+[,bash]
+----
+./gradlew updateLockfile
+----
+
+If the new dependency is needed within multiple subprojects, it can be extracted to one of the catalogs defined in `pnpm-workspace.yaml`. This ensures that all subprojects use the same version of this dependency and eases the management of that dependency.
 
 ==== Backend
 
 See link:../backend/README.md#introducing-new-dependencies[Backend README], chapter "Introducing new dependencies".
 
-=== Updating all dependencies at once
+== Updating pnpm dependencies ==
+
+We use link:https://pnpm.io/catalogs[pnpm Catalogs] to define the versions of shared dependencies in a reusable way and simplify dependency updates.
+
+To automatically update all dependencies, run
+
+[,bash]
+----
+./gradlew updateDependencies
+----
+
+Unfortunately, automatically updating dependencies from catalogs is not yet supported. For these, run
+
+[,bash]
+----
+./gradlew outdatedDependencies
+----
+
+You can now update the outdated dependencies in `pnpm-workspace.yaml`.
+Afterwards, run
+
+[,bash]
+----
+./gradlew updateLockfile
+----
 
-To manually update *all* dependencies in the project, use the following commands:
+== Updating Gradle dependencies
 
-[cols="1,1"]
-|===
-| *Command*                                                | *Description*
-| `./gradlew updateDependencies`                           | Update all pnpm dependencies
-| `+++./gradlew resolveAndLockAll --update-locks '*:*'+++` | Update all Gradle lockfiles
-|===
+To update the lockfiles for all Gradle dependencies, run
 
+[,bash]
+----
+./gradlew resolveAndLockAll --update-locks '*:*'
+----
 
-== Updating gradle ==
+== Updating Gradle ==
 
 The backend should be upgraded first. Depending on whether there were any problems, the backend reference commit needs to updated as well.
 If there were no adjustments necessary, this is optional.
diff --git a/docs/queries-and-mutations.adoc b/docs/queries-and-mutations.adoc
index e60a0db44..bf26427a2 100644
--- a/docs/queries-and-mutations.adoc
+++ b/docs/queries-and-mutations.adoc
@@ -379,8 +379,7 @@ export function CreateProcedureForm(props: CreateProcedureFormProps) {
     await createProcedure
       .mutateAsync(mapFormValues(values), {
         onSuccess: () => router.push(routes.procedures.overview),
-      })
-      .catch();
+      });
   }
 
   return <ProcedureForm onSubmit={handleSubmit} />;
@@ -399,21 +398,24 @@ This should only be necessary in rare cases, so only resort to this hook if the
 
 Each mutation provides two functions for triggering the mutation: `mutate` and `mutateAsync`.
 
-In general, `mutate` should be preferred over `mutateAsync`, because it internally discards errors caused by the mutation, which avoids triggering the nearest error boundary. `onError` handlers are still executed, of course.
+When passing an `onSumbit` handler to Formik, we need the promise returned by `mutateAsync` to enable Formik to correctly update the submission state of a form.
 
-However, we need the promise returned by `mutateAsync` to enable Formik to correctly update the submission state of a form.
-In this case it's important to catch promise rejections to avoid propagating the error to the nearest error boundary. We can do this by using the following pattern:
+However, the returned Promise should not be used to access mutation response data or errors.
+Callbacks like `onSuccess` and `onError` should be used instead.
+
+In general, `mutate` should be preferred over `mutateAsync` when the mutation is not triggered by a form and there is no need for a Promise.
 
 [,ts]
 ----
+// Use mutateAsync and pass a promise to onSubmit
 async function handleSubmit(values: FormValues) {
-  await sendFormData
-    .mutateAsync(values)
-    .catch();
+  await sendFormData.mutateAsync(values);
 }
-----
+return <Form onSubmit={handleSubmit} />;
 
-Alternatively, a `try-catch` block can be used with an empty `catch` block, which is a little bit more verbose.
+// Use mutate when no Promise is needed
+return <button onClick={() => mutation.mutate(values)} />;
+----
 
 For more details about the difference between both functions refer to link:https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutate-or-mutateasync[Mutate or MutateAsync].
 
diff --git a/employee-portal/.env b/employee-portal/.env
index 66435679a..af6a78488 100644
--- a/employee-portal/.env
+++ b/employee-portal/.env
@@ -9,6 +9,7 @@ PUBLIC_CHAT_MANAGEMENT_BACKEND_URL=http://localhost:4000/api/chat-management
 PUBLIC_AUDITLOG_BACKEND_URL=http://localhost:4000/api/auditlog
 PUBLIC_OPENDATA_BACKEND_URL=http://localhost:4000/api/opendata
 PUBLIC_STI_PROTECTION_BACKEND_URL=http://localhost:4000/api/sti-protection
+PUBLIC_MEDICAL_REGISTRY_BACKEND_URL=http://localhost:4000/api/medical-registry
 
 MARKDOWN_PAGE_DIRECTORY=frankfurt
 
diff --git a/employee-portal/.gitignore b/employee-portal/.gitignore
index e83cd919d..92594e366 100644
--- a/employee-portal/.gitignore
+++ b/employee-portal/.gitignore
@@ -1,10 +1,11 @@
 # Next.js
 .next
-src/app/(baseModule)/**/loading.tsx
-src/app/(businessModules)/**/loading.tsx
-
 # Usually, this file is ignored. But we need to make sure it exists in CI when running tsc. See https://github.com/vercel/next.js/discussions/47010.
 # next-env.d.ts
 
 # next-pwa
 public/*.js
+
+## Generated files
+src/app/(baseModule)/**/loading.tsx
+src/app/(businessModules)/**/loading.tsx
diff --git a/employee-portal/build.gradle b/employee-portal/build.gradle
index 9628c3b74..739971d9a 100644
--- a/employee-portal/build.gradle
+++ b/employee-portal/build.gradle
@@ -1,6 +1,7 @@
 plugins {
   id 'next-app'
   id 'reverse-proxy'
+  id 'next-loading-files'
 }
 
 next {
@@ -9,67 +10,11 @@ next {
   apiProject = ':employee-portal-api'
 }
 
+nextLoadingFiles {
+  includeDirs = ['(baseModule)', '(businessModules)']
+}
+
 reverseProxy {
   serviceName = "employee-portal-reverse-proxy"
   mainConfigFile = "employee-portal.conf"
 }
-
-def cleanLoadingFiles = tasks.register("cleanLoadingFiles") {
-  def appRouterRoot = project.layout.projectDirectory.dir("src/app")
-  def loadingFiles = appRouterRoot
-    .asFileTree
-    .matching {
-      include '(baseModule)/**/loading.tsx'
-      include '(businessModules)/**/loading.tsx'
-    }
-
-  inputs.files(loadingFiles)
-  loadingFiles.each { it.delete() }
-}
-
-def generateLoadingFiles = tasks.register("generateLoadingFiles") { task ->
-  mustRunAfter cleanLoadingFiles
-
-  def appRouterRoot = project.layout.projectDirectory.dir("src/app")
-  def allPages = appRouterRoot
-    .asFileTree
-    .matching {
-      include '(baseModule)/**/page.tsx'
-      include '(businessModules)/**/page.tsx'
-    }
-
-  def loadingTemplate = file("src/app/loading.template.tsx")
-
-  inputs.file(loadingTemplate)
-  inputs.files(allPages)
-
-  def loadingFiles = allPages.collect { file("${it.parent}/loading.tsx") }
-  outputs.files(loadingFiles)
-
-  doFirst {
-    def text = loadingTemplate.getText("UTF-8")
-    loadingFiles.each { File loadingFile ->
-      if (!loadingFile.exists()) {
-        loadingFile.createNewFile()
-      }
-
-      loadingFile.setText(text, "UTF-8")
-    }
-  }
-}
-
-tasks.named("prepareEnvironment") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("formatCheck") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("lint") {
-  dependsOn generateLoadingFiles
-}
-
-tasks.named("clean") {
-  dependsOn cleanLoadingFiles
-}
diff --git a/employee-portal/markdown/common/release-notes.md b/employee-portal/markdown/common/release-notes.md
index caf5face2..835e5df11 100644
--- a/employee-portal/markdown/common/release-notes.md
+++ b/employee-portal/markdown/common/release-notes.md
@@ -2,6 +2,28 @@ GA-Lotse ist ein Kooperationsprojekt des Gesundheitsamts Frankfurt am Main mit d
 
 Finanziert von der Europäischen Union – NextGenerationEU
 
+## GA-Lotse 1.2
+
+_04.11.2024_
+
+Dritter Release der Anwendung GA-Lotse.
+
+### Einschulungsuntersuchungen:
+
+* Untersuchungstag
+  * Schließen eines Vorgangs nach Abschluss der Untersuchung
+  * Wiedereröffnung eines geschlossenen Vorgangs
+
+### Begehung:
+
+* Planung
+  * Verwendung von Packlisten um Nichts zu vergessen
+* Ausführung
+  * Unterstützung von Audionotizen in Checklisten
+* Konfiguration
+  * Definition von Packlisten pro Objekttyp
+  * Definition von Audio-Elementen in Definitionen von Checklisten
+
 ## GA-Lotse 1.1
 
 _21.10.2024_
diff --git a/employee-portal/package.json b/employee-portal/package.json
index 5b0e54d3f..989bf96cb 100644
--- a/employee-portal/package.json
+++ b/employee-portal/package.json
@@ -5,57 +5,69 @@
   "private": true,
   "dependencies": {
     "@ducanh2912/next-pwa": "10.2.9",
-    "@emotion/cache": "11.13.1",
-    "@emotion/react": "11.13.3",
-    "@emotion/styled": "11.13.0",
+    "@emotion/react": "catalog:joy",
+    "@emotion/styled": "catalog:joy",
     "@eshg/employee-portal-api": "workspace:*",
     "@eshg/lib-portal": "workspace:*",
-    "@fontsource/poppins": "5.1.0",
-    "@fullcalendar/core": "6.1.15",
-    "@fullcalendar/daygrid": "6.1.15",
-    "@fullcalendar/interaction": "6.1.15",
-    "@fullcalendar/list": "6.1.15",
-    "@fullcalendar/react": "6.1.15",
-    "@fullcalendar/timegrid": "6.1.15",
+    "@fontsource/poppins": "catalog:joy",
+    "@fullcalendar/core": "catalog:fullcalendar",
+    "@fullcalendar/daygrid": "catalog:fullcalendar",
+    "@fullcalendar/interaction": "catalog:fullcalendar",
+    "@fullcalendar/list": "catalog:fullcalendar",
+    "@fullcalendar/react": "catalog:fullcalendar",
+    "@fullcalendar/timegrid": "catalog:fullcalendar",
     "@hello-pangea/dnd": "17.0.0",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@mdx-js/mdx": "3.0.1",
-    "@tanstack/react-query": "5.59.10",
-    "@tanstack/react-table": "8.20.5",
+    "@mdx-js/mdx": "3.1.0",
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "@tanstack/react-table": "catalog:common",
     "compressorjs": "1.2.1",
     "drauu": "0.4.1",
+    "date-fns": "catalog:common",
     "echarts": "5.5.1",
     "echarts-for-react": "3.0.2",
-    "echarts-stat": "1.2.0",
-    "formik": "2.4.6",
-    "hpke-js": "1.4.3",
+    "formik": "catalog:common",
+    "hpke-js": "1.5.0",
     "iso8601-duration": "2.1.2",
     "matrix-js-sdk": "34.3.1",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-error-boundary": "4.0.13",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "react-error-boundary": "catalog:common",
     "react-infinite-scroll-hook": "5.0.1",
-    "react-transition-group": "4.4.5",
-    "server-only": "0.0.1",
-    "use-debounce": "10.0.3",
-    "uuid": "10.0.0",
-    "valibot": "0.42.1"
+    "remeda": "catalog:common",
+    "server-only": "catalog:common",
+    "use-debounce": "catalog:common",
+    "uuid": "catalog:common",
+    "valibot": "catalog:common",
+    "workbox-background-sync": "7.1.0",
+    "workbox-core": "7.1.0",
+    "workbox-window": "7.1.0"
   },
   "devDependencies": {
-    "@next/bundle-analyzer": "14.2.14",
-    "@tanstack/eslint-plugin-query": "5.59.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@next/bundle-analyzer": "catalog:next",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
     "@types/mdx": "2.0.13",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
+    "@types/node": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
     "@types/react-transition-group": "4.4.11",
-    "@types/uuid": "10.0.0",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/employee-portal/src/app/(baseModule)/account/sessions/page.tsx b/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
index cb9504f74..fa8c67950 100644
--- a/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
+++ b/employee-portal/src/app/(baseModule)/account/sessions/page.tsx
@@ -42,7 +42,7 @@ function useColumns() {
   const invalidateUserSessions = useInvalidateUserSessions();
 
   async function invalidateSession(session: string) {
-    await invalidateUserSessions.mutateAsync([session]).catch();
+    await invalidateUserSessions.mutateAsync([session]);
   }
 
   return [
@@ -137,7 +137,7 @@ export default function AccountSecurityPage() {
       .filter((session) => !session.isCurrent)
       .map((session) => session.sessionId);
     if (sessionsToInvalidate.length > 0) {
-      await invalidateUserSessions.mutateAsync(sessionsToInvalidate).catch();
+      await invalidateUserSessions.mutateAsync(sessionsToInvalidate);
     }
   }
 
diff --git a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
index 4fc126a16..393817cfc 100644
--- a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
+++ b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx
@@ -42,17 +42,15 @@ export default function InboxPage() {
   async function onSubmit(values: CreateInboxProcedureValues) {
     await createInboxProcedure(
       values.businessModule as InboxAwareBusinessModule,
-    )
-      .mutateAsync(
-        {
-          request: mapFormValuesToCreateInboxProcedureRequest(values),
-          file: mapValuesToFile(values),
-        },
-        {
-          onSuccess: (response) => setResult(response),
-        },
-      )
-      .catch();
+    ).mutateAsync(
+      {
+        request: mapFormValuesToCreateInboxProcedureRequest(values),
+        file: mapValuesToFile(values),
+      },
+      {
+        onSuccess: (response) => setResult(response),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx b/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
index 93c57eba6..81f45ab45 100644
--- a/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
+++ b/employee-portal/src/app/(baseModule)/resources/[id]/page.tsx
@@ -55,7 +55,7 @@ export default function ResourceDetailsPage({
               });
             },
           }}
-          isTodayAvaliable={eventsOfToday.length === 0}
+          isTodayAvailable={eventsOfToday.length === 0}
         />
       </MainContentLayout>
     </StickyToolbarLayout>
diff --git a/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx b/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
index a64065c9b..f30106b2b 100644
--- a/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
+++ b/employee-portal/src/app/(businessModules)/inspection/teamview/page.tsx
@@ -8,7 +8,6 @@
 import { ApiBusinessModule, ApiUserRole } from "@eshg/employee-portal-api/base";
 
 import { Teamview } from "@/lib/baseModule/components/task/Teamview";
-import { useFetchTasksForTeamViewOptions } from "@/lib/businessModules/inspection/api/queries/useFetchTasksForTeamViewOptions";
 import { moduleUserGroup } from "@/lib/businessModules/inspection/shared/moduleUserGroup";
 import { RestrictedPage } from "@/lib/shared/components/RestrictedPage";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
@@ -23,7 +22,6 @@ export default function InspectionTeamviewPage() {
           <Teamview
             groupName={moduleUserGroup.group}
             businessModule={ApiBusinessModule.Inspection}
-            useFetchTasksForTeamViewOptions={useFetchTasksForTeamViewOptions}
           />
         </RestrictedPage>
       </MainContentLayout>
diff --git a/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx b/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx
new file mode 100644
index 000000000..eaddfb9a1
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/medical-registry/procedures/page.tsx
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { MedicalRegistryProceduresTable } from "@/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable";
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+export default function MedicalRegistryProceduresPage() {
+  return (
+    <StickyToolbarLayout toolbar={<Toolbar title="Berufskartei" />}>
+      <MainContentLayout fullViewportHeight>
+        <MedicalRegistryProceduresTable />
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
index 73f0b3464..02d000567 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/anamnesis/page.tsx
@@ -75,9 +75,9 @@ export default function SchoolEntryAnamnesisPage(
   const updateAnamnesis = useUpdateAnamnesis();
 
   async function handleSubmit(values: AnamnesisFormValues) {
-    await updateAnamnesis
-      .mutateAsync(mapToRequest(procedureId, values, anamnesis.version))
-      .catch();
+    await updateAnamnesis.mutateAsync(
+      mapToRequest(procedureId, values, anamnesis.version),
+    );
   }
 
   return (
@@ -190,6 +190,7 @@ function parseDaycareAndSchoolInfo(
   daycareAndSchoolInfo: ApiDaycareAndSchoolInfo,
 ) {
   return {
+    wasInDaycare: parseOptionalValue(daycareAndSchoolInfo.wasInDaycare),
     inDaycareSince: parseMonthAndYear(daycareAndSchoolInfo.inDaycareSince),
     daycareName: parseOptionalValue(daycareAndSchoolInfo.daycareName),
     schoolName: parseOptionalValue(daycareAndSchoolInfo.schoolName),
@@ -389,8 +390,15 @@ function mapAdditionalChildInfo(values: AdditionalChildInfoValues) {
 
 function mapDaycareAndSchoolInfo(values: DaycareAndSchoolInfoValues) {
   return {
-    inDaycareSince: mapMonthAndYear(values.inDaycareSince),
-    daycareName: mapOptionalValue(values.daycareName),
+    wasInDaycare: mapOptionalValue(values.wasInDaycare),
+    inDaycareSince:
+      values.wasInDaycare === true
+        ? mapMonthAndYear(values.inDaycareSince)
+        : undefined,
+    daycareName:
+      values.wasInDaycare === true
+        ? mapOptionalValue(values.daycareName)
+        : undefined,
     schoolName: mapOptionalValue(values.schoolName),
   };
 }
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
index 78958cc4d..433859daf 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/development-screening/page.tsx
@@ -63,15 +63,9 @@ export default function SchoolEntryDevelopmentScreeningPage(
     useUpdateDevelopmentScreeningResult();
 
   async function handleSubmit(formValues: DevelopmentScreeningFormValues) {
-    await updateDevelopmentScreeningResult
-      .mutateAsync(
-        mapToRequest(
-          procedureId,
-          formValues,
-          developmentScreeningResult.version,
-        ),
-      )
-      .catch();
+    await updateDevelopmentScreeningResult.mutateAsync(
+      mapToRequest(procedureId, formValues, developmentScreeningResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
index 8d63acd82..3e9c5ed40 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/ear/page.tsx
@@ -46,11 +46,9 @@ export default function SchoolEntryHearingTestPage(
   const updateHearingTestResult = useUpdateHearingTestResult();
 
   async function handleSubmit(formValues: HearingTestFormValues) {
-    await updateHearingTestResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, hearingTestResult.version),
-      )
-      .catch();
+    await updateHearingTestResult.mutateAsync(
+      mapToRequest(procedureId, formValues, hearingTestResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
index 746607503..3079497e7 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/eye/page.tsx
@@ -45,11 +45,9 @@ export default function SchoolEntryEyeExaminationPage(
   const updateEyeExaminationResult = useUpdateEyeExaminationResult();
 
   async function handleSubmit(formValues: EyeExaminationFormValues) {
-    await updateEyeExaminationResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, eyeExaminationResult.version),
-      )
-      .catch();
+    await updateEyeExaminationResult.mutateAsync(
+      mapToRequest(procedureId, formValues, eyeExaminationResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
index a5c227a98..26d9bc3ec 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/examinations/sopess/page.tsx
@@ -51,11 +51,9 @@ export default function SchoolEntrySopessExaminationPage(
   const updateSopessExaminationResult = useUpdateSopessExaminationResult();
 
   async function handleSubmit(formValues: SopessExaminationFormValues) {
-    await updateSopessExaminationResult
-      .mutateAsync(
-        mapToRequest(procedureId, formValues, sopessExaminationResult.version),
-      )
-      .catch();
+    await updateSopessExaminationResult.mutateAsync(
+      mapToRequest(procedureId, formValues, sopessExaminationResult.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
index 3e82dec58..22f298980 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
@@ -23,18 +23,16 @@ export default function SyncPersonPage({
   const syncPerson = useSyncPerson(params.id);
 
   async function handleSync() {
-    await syncPerson
-      .mutateAsync(
-        {
-          referenceVersion: data.referenceVersion,
-          personVersion: params.personVersion,
-          fileStateId: params.fileStateId,
-        },
-        {
-          onSuccess: () => router.back(),
-        },
-      )
-      .catch();
+    await syncPerson.mutateAsync(
+      {
+        referenceVersion: data.referenceVersion,
+        personVersion: params.personVersion,
+        fileStateId: params.fileStateId,
+      },
+      {
+        onSuccess: () => router.back(),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
index 9bbfbb893..30538769a 100644
--- a/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
+++ b/employee-portal/src/app/(businessModules)/school-entry/procedures/[id]/vaccination/page.tsx
@@ -54,9 +54,9 @@ export default function SchoolEntryVaccinationStatusPage(
   const updateVaccinationStatus = useUpdateVaccinationStatus();
 
   async function handleSubmit(values: VaccinationFormValues) {
-    await updateVaccinationStatus
-      .mutateAsync(mapToRequest(procedureId, values, vaccinationStatus.version))
-      .catch();
+    await updateVaccinationStatus.mutateAsync(
+      mapToRequest(procedureId, values, vaccinationStatus.version),
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx b/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx
new file mode 100644
index 000000000..2face0d0a
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/statistics/statistics/evaluation-templates/page.tsx
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { EvaluationTemplatesOverview } from "@/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview";
+import { routes } from "@/lib/businessModules/statistics/shared/routes";
+import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
+import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
+import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
+
+export default function EvaluationTemplatesOverviewPage() {
+  return (
+    <StickyToolbarLayout
+      toolbar={
+        <Toolbar
+          title="Auswertungsvorlagen"
+          backHref={routes.statistics.index}
+        />
+      }
+    >
+      <MainContentLayout fullViewportHeight>
+        <EvaluationTemplatesOverview />
+      </MainContentLayout>
+    </StickyToolbarLayout>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx b/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
index d2752bc84..7f9856331 100644
--- a/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
+++ b/employee-portal/src/app/(businessModules)/statistics/statistics/page.tsx
@@ -25,13 +25,15 @@ import {
 function parseSearchParams(searchParams: SearchParams): GetStatisticsRequest {
   const { pageSize, pageNumber } = parsePageParams(searchParams);
   return {
-    sortKey: parseOptionalEnum(ApiStatisticSortKey, searchParams.sortKey),
-    sortDirection: parseOptionalEnum(
-      ApiSortDirection,
-      searchParams.sortDirection,
-    ),
-    page: pageNumber,
-    pageSize,
+    apiGetStatisticsRequest: {
+      sortKey: parseOptionalEnum(ApiStatisticSortKey, searchParams.sortKey),
+      sortDirection: parseOptionalEnum(
+        ApiSortDirection,
+        searchParams.sortDirection,
+      ),
+      page: pageNumber,
+      pageSize,
+    },
   };
 }
 
@@ -40,8 +42,8 @@ export default function StatisticsOverviewPage(props: {
 }) {
   const params = parseSearchParams(props.searchParams);
   const {
-    statistics,
-    statisticsIsFetching,
+    statisticsOverview,
+    statisticsOverviewIsFetching,
     availableDataSources,
     evaluationTemplates,
   } = useGetStatisticsOverviewPage(params);
@@ -50,8 +52,8 @@ export default function StatisticsOverviewPage(props: {
     <StickyToolbarLayout toolbar={<Toolbar title="Auswertungen" />}>
       <MainContentLayout fullViewportHeight>
         <StatisticsOverview
-          statisticsResponse={statistics}
-          isFetchingStatistics={statisticsIsFetching}
+          statisticsOverview={statisticsOverview}
+          isFetchingStatisticsOverview={statisticsOverviewIsFetching}
           dataSources={availableDataSources}
           templates={evaluationTemplates}
         />
diff --git a/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx b/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
index 1fe9f4350..899215eed 100644
--- a/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
+++ b/employee-portal/src/app/(businessModules)/sti-protection/procedures/[id]/@tabs/anamnesis/page.tsx
@@ -5,13 +5,25 @@
 
 "use client";
 
+import { DisabledFormProvider } from "@eshg/lib-portal/components/form/DisabledFormContext";
+
 import { StiProtectionProcedurePageParams } from "@/app/(businessModules)/sti-protection/procedures/[id]/layout";
+import { useMedicalHistoryQuery } from "@/lib/businessModules/stiProtection/api/queries/medicalHistory";
 import { MedicalHistoryForm } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
 
 export default function StiProtectionProcedureAnamnesisPage({
-  params,
+  params: { id: procedureId },
 }: Readonly<{
   params: StiProtectionProcedurePageParams;
 }>) {
-  return <MedicalHistoryForm procedureId={params.id} />;
+  const { data: medicalHistory } = useMedicalHistoryQuery(procedureId);
+
+  return (
+    <DisabledFormProvider disabled={!!medicalHistory}>
+      <MedicalHistoryForm
+        procedureId={procedureId}
+        medicalHistory={medicalHistory}
+      />
+    </DisabledFormProvider>
+  );
 }
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx
new file mode 100644
index 000000000..2f3928f82
--- /dev/null
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/information-statements/page.tsx
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiTravelMedicineFeature } from "@eshg/employee-portal-api/travelMedicine";
+
+import { InformationStatementsTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable";
+import { ToggledPage } from "@/lib/businessModules/travelMedicine/shared/ToggledPage";
+
+export default function InformationStatementsPage({
+  params,
+}: Readonly<{ params: { id: string } }>) {
+  return (
+    <ToggledPage
+      feature={ApiTravelMedicineFeature.CitizenPortalInformationStatement}
+    >
+      <InformationStatementsTable procedureId={params.id} />
+    </ToggledPage>
+  );
+}
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
index afc7db6fa..c7ea3b26f 100644
--- a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/[id]/sync-person/[fileStateId]/[personVersion]/page.tsx
@@ -23,18 +23,16 @@ export default function SyncPersonPage({
   const syncPerson = useSyncPerson(params.id);
 
   async function handleSync() {
-    await syncPerson
-      .mutateAsync(
-        {
-          referenceVersion: data.referenceVersion,
-          personVersion: params.personVersion,
-          fileStateId: params.fileStateId,
-        },
-        {
-          onSuccess: () => router.back(),
-        },
-      )
-      .catch();
+    await syncPerson.mutateAsync(
+      {
+        referenceVersion: data.referenceVersion,
+        personVersion: params.personVersion,
+        fileStateId: params.fileStateId,
+      },
+      {
+        onSuccess: () => router.back(),
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
index 9efe906ff..bdc445e81 100644
--- a/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
+++ b/employee-portal/src/app/(businessModules)/travel-medicine/procedure/page.tsx
@@ -8,11 +8,19 @@ import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLay
 import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout";
 import { Toolbar } from "@/lib/shared/components/layout/Toolbar";
 
-export default function VaccinationConsultationsOverviewPage() {
+export default function VaccinationConsultationsOverviewPage(
+  props: Readonly<{
+    searchParams?: {
+      date: string;
+    };
+  }>,
+) {
   return (
     <StickyToolbarLayout toolbar={<Toolbar title="Impfberatung" />}>
       <MainContentLayout>
-        <VaccinationConsultationsOverviewTable />
+        <VaccinationConsultationsOverviewTable
+          date={props.searchParams?.date}
+        />
       </MainContentLayout>
     </StickyToolbarLayout>
   );
diff --git a/employee-portal/src/app/layout.tsx b/employee-portal/src/app/layout.tsx
index c26d830d7..6806ebbc1 100644
--- a/employee-portal/src/app/layout.tsx
+++ b/employee-portal/src/app/layout.tsx
@@ -59,6 +59,9 @@ export default function RootLayout({
       <body
         style={{ backgroundColor: "var(--joy-palette-neutral-100, #F0F4F8)" }}
       >
+        <noscript>
+          Bitte aktivieren Sie JavaScript, um diese Anwendung zu nutzen.
+        </noscript>
         <NonceProvider initialNonce={nonce}>
           <ThemeProvider>
             <SnackbarProvider snackbar={EmployeeSnackbar}>
diff --git a/employee-portal/src/app/playground/appointment-picker/page.tsx b/employee-portal/src/app/playground/appointment-picker/page.tsx
index 01e6250c2..f547dff02 100644
--- a/employee-portal/src/app/playground/appointment-picker/page.tsx
+++ b/employee-portal/src/app/playground/appointment-picker/page.tsx
@@ -147,7 +147,6 @@ function AltAppointmentList<T extends Appointment>({
         wrap
         size="sm"
         sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
-        // eslint-disable-next-line jsx-a11y/aria-props
         aria-description={description}
       >
         {appointments.map((apt) => {
diff --git a/employee-portal/src/app/playground/charts/page.tsx b/employee-portal/src/app/playground/charts/page.tsx
index fcf94fa5c..428d1b9e5 100644
--- a/employee-portal/src/app/playground/charts/page.tsx
+++ b/employee-portal/src/app/playground/charts/page.tsx
@@ -5,42 +5,705 @@
 
 "use client";
 
-import { Stack } from "@mui/joy";
+import { Option, Select, Sheet, Stack, Switch, Typography } from "@mui/joy";
+import { ReactNode, useState } from "react";
 
 import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute";
-import { DiagramType } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
+import {
+  DiagramAxisRange,
+  DiagramCharacteristicParameter,
+  DiagramColorScheme,
+  DiagramGrouping,
+  DiagramOrientation,
+  DiagramScaling,
+  DiagramType,
+  EvaluationLineDiagramConfiguration,
+  EvaluationScatterDiagramConfiguration,
+} from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import { EvaluationDiagramBox } from "@/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
+import { ChoroplethMap } from "@/lib/businessModules/statistics/components/shared/charts/ChoroplethMap";
+import { Histogram } from "@/lib/businessModules/statistics/components/shared/charts/Histogram";
+import { LineChart } from "@/lib/businessModules/statistics/components/shared/charts/LineChart";
+import { PieChart } from "@/lib/businessModules/statistics/components/shared/charts/PieChart";
 import { ScatterChart } from "@/lib/businessModules/statistics/components/shared/charts/ScatterChart";
 import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout";
 
+function PlaygroundChartBox({
+  title,
+  chart,
+  switches,
+}: {
+  title: string;
+  chart: ReactNode;
+  switches?: ReactNode[];
+}) {
+  return (
+    <Sheet>
+      <Stack gap={2}>
+        <Typography level="h4">{title}</Typography>
+        <EvaluationDiagramBox
+          evaluatedDataAmountTotal={100}
+          description="Hier könnte Ihre Werbung stehen"
+          filterLabels={["Label 1", "Label 2"]}
+          evaluatedDataAmount={42}
+          chart={chart}
+        />
+        <Stack direction="row" gap={3}>
+          {switches}
+        </Stack>
+      </Stack>
+    </Sheet>
+  );
+}
+
 export default function PlaygroundChartsPage() {
+  const [orientation, setOrientation] =
+    useState<DiagramOrientation>("VERTICAL");
+  const [grouping, setGrouping] = useState<DiagramGrouping>("GROUPED");
+  const [scaling, setScaling] = useState<DiagramScaling>("ABSOLUTE");
+  const [axisRange, setAxisRange] = useState<DiagramAxisRange>("ADAPTED");
+  const [trendLine, setTrendLine] = useState(false);
+  const [colorScheme, setColorScheme] = useState<DiagramColorScheme>("UNIFORM");
+  const [characteristicParameter, setCharacteristicParameter] = useState<
+    DiagramCharacteristicParameter | undefined
+  >();
+
+  const orientationSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={orientation === "HORIZONTAL"}
+          onChange={(event) =>
+            setOrientation(event.target.checked ? "HORIZONTAL" : "VERTICAL")
+          }
+        />
+      }
+    >
+      Ausrichtung
+    </Typography>
+  );
+
+  const groupingSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={grouping === "STACKED"}
+          onChange={(event) =>
+            setGrouping(event.target.checked ? "STACKED" : "GROUPED")
+          }
+        />
+      }
+    >
+      Anordnung
+    </Typography>
+  );
+
+  const scalingSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={scaling === "RELATIVE"}
+          onChange={(event) =>
+            setScaling(event.target.checked ? "RELATIVE" : "ABSOLUTE")
+          }
+        />
+      }
+    >
+      Verhältnisse
+    </Typography>
+  );
+
+  const axisRangeSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={axisRange === "ORIGIN"}
+          onChange={(event) =>
+            setAxisRange(event.target.checked ? "ORIGIN" : "ADAPTED")
+          }
+        />
+      }
+    >
+      Achsenskalierung
+    </Typography>
+  );
+
+  const trendLineSwitch = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Switch
+          checked={trendLine}
+          onChange={(event) => setTrendLine(event.target.checked)}
+        />
+      }
+    >
+      Trendlinie
+    </Typography>
+  );
+
+  const colorSchemeSelect = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Select
+          value={colorScheme}
+          onChange={(_, value) => setColorScheme(value!)}
+        >
+          <Option value="UNIFORM">Uniform</Option>
+          <Option value="GREEN2BLUE">Grün zu blau</Option>
+          <Option value="BLUE2GREEN">Blau zu grün</Option>
+        </Select>
+      }
+    >
+      Farbschema
+    </Typography>
+  );
+
+  const characteristicParameterSelect = (
+    <Typography
+      component="label"
+      endDecorator={
+        <Select
+          value={characteristicParameter}
+          onChange={(_, value) =>
+            value === null
+              ? setCharacteristicParameter(undefined)
+              : setCharacteristicParameter(value)
+          }
+        >
+          <Option value="MEAN"> Mittelwert</Option>
+          <Option value="SUM"> Summe</Option>
+          <Option value={null}> Häufigkeit</Option>
+        </Select>
+      }
+    >
+      Darstellung
+    </Typography>
+  );
+
+  const barChartSimple = [
+    {
+      label: "Hund",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+      ],
+    },
+    {
+      label: "Katze",
+      attributes: [
+        {
+          label: "Katze",
+          value: 8,
+        },
+      ],
+    },
+    {
+      label: "Schwein",
+      attributes: [
+        {
+          label: "Schwein",
+          value: 3,
+        },
+      ],
+    },
+    {
+      label: "Schildkröte",
+      attributes: [
+        {
+          label: "Schildkröte",
+          value: 6,
+        },
+      ],
+    },
+    {
+      label: "Leguan",
+      attributes: [
+        {
+          label: "Leguan",
+          value: 1,
+        },
+      ],
+    },
+  ];
+
+  const barChartSimpleWithNegativeValues = [
+    {
+      label: "Hund",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+      ],
+    },
+    {
+      label: "Katze",
+      attributes: [
+        {
+          label: "Katze",
+          value: 0,
+        },
+      ],
+    },
+    {
+      label: "Schwein",
+      attributes: [
+        {
+          label: "Schwein",
+          value: 3,
+        },
+      ],
+    },
+    {
+      label: "Schildkröte",
+      attributes: [
+        {
+          label: "Schildkröte",
+          value: -6,
+        },
+      ],
+    },
+    {
+      label: "Leguan",
+      attributes: [
+        {
+          label: "Leguan",
+          value: 1,
+        },
+      ],
+    },
+  ];
+
+  const barChartGrouped = [
+    {
+      label: "Wild",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15,
+        },
+      ],
+    },
+    {
+      label: "Domestiziert",
+      attributes: [
+        {
+          label: "Hund",
+          value: 20,
+        },
+        {
+          label: "Katze",
+          value: 10,
+        },
+        {
+          label: "Schwein",
+          value: 2,
+        },
+        {
+          label: "Schildkröte",
+          value: 7,
+        },
+      ],
+    },
+  ];
+
+  const barChartGroupedWithNegativeValues = [
+    {
+      label: "Wild",
+      attributes: [
+        {
+          label: "Hund",
+          value: 5,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: -15,
+        },
+      ],
+    },
+    {
+      label: "Domestiziert",
+      attributes: [
+        {
+          label: "Hund",
+          value: 20,
+        },
+        {
+          label: "Katze",
+          value: 0,
+        },
+        {
+          label: "Schwein",
+          value: 2,
+        },
+        {
+          label: "Schildkröte",
+          value: 7,
+        },
+      ],
+    },
+  ];
+
   const barChartMuchData = [];
   for (let i = 0; i < 50; i++) {
     barChartMuchData.push({
       label: i.toString(),
       attributes: [
         {
-          label: "blau",
+          label: "Hund",
+          value: 5 + 0.1 * i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 6,
+        },
+      ],
+    });
+  }
+
+  const barChartLongLabels = [
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
+      attributes: [
+        {
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
           value: 5,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
+      attributes: [
         {
-          label: "grün",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
           value: 8,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : C",
+      attributes: [
         {
-          label: "rot",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : C",
           value: 3,
         },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : D",
+      attributes: [
         {
-          label: "gelb",
+          label:
+            "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : D",
           value: 6,
         },
       ],
+    },
+  ];
+
+  const barChartManyLongSecondaryAttributes = [];
+  const attributes = [];
+  for (let i = 0; i < 20; i++) {
+    attributes.push({
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : " +
+        i,
+      value: i,
+    });
+  }
+  for (let i = 0; i < 4; i++) {
+    barChartManyLongSecondaryAttributes.push({
+      label: i.toString(),
+      attributes,
     });
   }
 
-  const scatterChartData = [
+  const pieChartSimple = [
+    {
+      label: "Hund",
+      value: 5,
+    },
+    {
+      label: "Katze",
+      value: 8,
+    },
+    {
+      label: "Schwein",
+      value: 3,
+    },
+    {
+      label: "Schildkröte",
+      value: 6,
+    },
+    {
+      label: "Leguan",
+      value: 1,
+    },
+  ];
+
+  const pieChartManyLongValues = [];
+  for (let i = 0; i < 100; i++) {
+    pieChartManyLongValues.push({
+      label:
+        "Sehr lange Bezeichnung für den Bezirk mit der wundervollen Nummer:  " +
+        i,
+      value: 1,
+    });
+  }
+
+  const pieChartLargeAndSmallValues = [
+    {
+      label: "Mehrheit",
+      value: 10000,
+    },
+  ];
+  for (let i = 0; i < 10; i++) {
+    pieChartLargeAndSmallValues.push({
+      label: "Minderheit Nr " + i,
+      value: 1,
+    });
+  }
+
+  const pieChartWithNegativeValue = [
+    {
+      label: "Hund",
+      value: 5,
+    },
+    {
+      label: "Katze",
+      value: 0,
+    },
+    {
+      label: "Schwein",
+      value: 3,
+    },
+    {
+      label: "Schildkröte",
+      value: -6,
+    },
+    {
+      label: "Leguan",
+      value: 1,
+    },
+  ];
+
+  const histogramSimple = [];
+  for (let i = 0; i < 10; i++) {
+    histogramSimple.push({
+      min: 0.1 * i - 0.5,
+      max: 0.1 * i - 0.4,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + i,
+        },
+      ],
+    });
+  }
+
+  const histogramGrouped = [];
+  for (let i = 0; i < 10; i++) {
+    histogramGrouped.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15 - 0.5 * i,
+        },
+      ],
+    });
+  }
+
+  const histogramWithNegativeValues = [];
+  for (let i = 0; i < 10; i++) {
+    histogramWithNegativeValues.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 - i,
+        },
+        {
+          label: "Katze",
+          value: 8,
+        },
+        {
+          label: "Schwein",
+          value: 3,
+        },
+        {
+          label: "Schildkröte",
+          value: 15 - 0.5 * i,
+        },
+      ],
+    });
+  }
+
+  const histogramWithManyValues = [];
+  for (let i = 0; i < 50; i++) {
+    histogramWithManyValues.push({
+      min: i,
+      max: i + 1,
+      attributes: [
+        {
+          label: "Hund",
+          value: 5 + 0.1 * i,
+        },
+      ],
+    });
+  }
+
+  const lineChartSimple = [
+    {
+      label: "Gruppe 1",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: -2 },
+        { x: 8, y: 2 },
+      ],
+    },
+  ];
+
+  const lineChartSimpleConfiguration = {
+    axisRange: axisRange,
+    type: DiagramType.LINE_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Größe",
+      unit: "m",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Gewicht",
+      unit: "kg",
+    } as FlatAttribute,
+  } as EvaluationLineDiagramConfiguration;
+
+  const lineChartGroupedWithLongValues = [
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : A",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: -2 },
+        { x: 8, y: 2 },
+      ],
+    },
+    {
+      label:
+        "Sehr lange Bezeichnung für einen einfachen Attributen der den meisten Leuten bekannt ist unter : B",
+      dataPoints: [
+        { x: -5, y: 1 },
+        { x: 10, y: 12 },
+        { x: 0, y: 6 },
+        { x: 20, y: 8 },
+      ],
+    },
+  ];
+
+  const lineChartConfigurationWithLongAxisNames = {
+    axisRange: axisRange,
+    type: DiagramType.LINE_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Die ganz besondere Einheit der gemessen Substanz gerundet nach Gefühl und Flexibilität",
+      unit: "Einheitskürzel",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Die ganz besondere Einheit der gemessen Substanz gerundet nach Gefühl und Flexibilität",
+      unit: "Einheitskürzel",
+    } as FlatAttribute,
+  } as EvaluationLineDiagramConfiguration;
+
+  const scatterChartSimple = [
+    {
+      label: "Gruppe 1",
+      dataPoints: [
+        { x: 1, y: 10 },
+        { x: 1, y: 1 },
+        { x: 2, y: 7 },
+        { x: 4, y: 4 },
+        { x: 4, y: 3 },
+        { x: 6, y: 8 },
+        { x: 8, y: 2 },
+      ],
+      trendline: {
+        offset: 3.8071065989847717,
+        slope: 0.08629441624365483,
+      },
+    },
+  ];
+
+  const scatterChartLargeAndSmallValues = [
     {
       label: "Gruppe 1",
       dataPoints: [
@@ -73,47 +736,264 @@ export default function PlaygroundChartsPage() {
     },
   ];
 
+  const scatterChartConfig = {
+    trendline: trendLine,
+    axisRange: axisRange,
+    type: DiagramType.SCATTER_CHART,
+    secondaryAttribute: undefined,
+    xAttribute: {
+      type: "IntegerAttribute",
+      name: "Größe",
+      unit: "m",
+    } as FlatAttribute,
+    yAttribute: {
+      type: "IntegerAttribute",
+      name: "Gewicht",
+      unit: "kg",
+    } as FlatAttribute,
+  } as EvaluationScatterDiagramConfiguration;
+
+  const choroplethData = [
+    {
+      name: "Altstadt",
+      value: 10,
+    },
+    {
+      name: "Neustadt",
+      value: 23,
+    },
+  ];
+
+  const geoJson = JSON.stringify({
+    type: "FeatureCollection",
+    features: [
+      {
+        type: "Feature",
+        geometry: {
+          type: "MultiPolygon",
+          coordinates: [
+            [
+              [
+                [8, 50],
+                [9, 50],
+                [9, 52],
+                [8, 52],
+                [8, 50],
+              ],
+            ],
+          ],
+        },
+        properties: {
+          name: "Altstadt",
+          cartodb_id: 1,
+          created_at: "2015-02-27T08:56:16Z",
+          updated_at: "2015-02-22T00:00:00Z",
+        },
+      },
+      {
+        type: "Feature",
+        geometry: {
+          type: "MultiPolygon",
+          coordinates: [
+            [
+              [
+                [9, 50],
+                [9, 52],
+                [10, 51],
+                [9, 50],
+              ],
+            ],
+          ],
+        },
+        properties: {
+          name: "Neustadt",
+          cartodb_id: 2,
+          created_at: "2015-02-27T08:56:16Z",
+          updated_at: "2015-02-22T00:00:00Z",
+        },
+      },
+    ],
+  });
+
   return (
     <MainContentLayout>
-      <Stack gap={2}>
-        <EvaluationDiagramBox
-          diagramId={"123"}
-          evaluatedDataAmountTotal={100}
+      <Stack gap={3}>
+        <PlaygroundChartBox
+          title="Balkendiagramm simpel"
+          chart={
+            <BarChart
+              filterSetData={barChartSimple}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm simpel mit negativen Werten"
+          chart={
+            <BarChart
+              filterSetData={barChartSimpleWithNegativeValues}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm gruppiert"
+          chart={
+            <BarChart
+              filterSetData={barChartGrouped}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm gruppiert mit negativen Werten"
+          chart={
+            <BarChart
+              filterSetData={barChartGroupedWithNegativeValues}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
           title="Balkendiagramm mit vielen Werten"
-          description="Hier könnte Ihre Werbung stehen"
-          filterLabels={["Label 1", "Label 2"]}
-          evaluatedDataAmount={42}
-          isReport={false}
-          openChartInFullScreenDialog={() => undefined}
-        >
-          <BarChart filterSetData={barChartMuchData} orientation={"VERTICAL"} />
-        </EvaluationDiagramBox>
-        <EvaluationDiagramBox
-          diagramId={"123"}
-          evaluatedDataAmountTotal={100}
-          title="ScatterDiagramm mit Werten die weit auseinander sind"
-          description="Hier könnte Ihre Werbung stehen"
-          filterLabels={["Label 1", "Label 2"]}
-          evaluatedDataAmount={42}
-          isReport={false}
-          openChartInFullScreenDialog={() => undefined}
-        >
-          <ScatterChart
-            filterSet={scatterChartData}
-            configuration={{
-              trendline: true,
-              axisRange: "ADAPTED",
-              type: DiagramType.SCATTER_CHART,
-              secondaryAttribute: undefined,
-              xAttribute: {
-                type: "IntegerAttribute",
-              } as FlatAttribute,
-              yAttribute: {
-                type: "IntegerAttribute",
-              } as FlatAttribute,
-            }}
-          />
-        </EvaluationDiagramBox>
+          chart={
+            <BarChart
+              filterSetData={barChartMuchData}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm mit langen Bezeichnern"
+          chart={
+            <BarChart
+              filterSetData={barChartLongLabels}
+              orientation={orientation}
+            />
+          }
+          switches={[orientationSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Balkendiagramm mit vielen langen sekundären Attributen"
+          chart={
+            <BarChart
+              filterSetData={barChartManyLongSecondaryAttributes}
+              orientation={orientation}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[orientationSwitch, groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram simpel"
+          chart={<PieChart filterSetData={pieChartSimple} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit vielen Werten und langen Bezeichnern"
+          chart={<PieChart filterSetData={pieChartManyLongValues} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit sehr großen und kleinen Werten"
+          chart={<PieChart filterSetData={pieChartLargeAndSmallValues} />}
+        />
+        <PlaygroundChartBox
+          title="Kreisdiagram mit negativem Wert"
+          chart={<PieChart filterSetData={pieChartWithNegativeValue} />}
+        />
+        <PlaygroundChartBox
+          title="Histogramm simpel"
+          chart={<Histogram diagramData={histogramSimple} />}
+        />
+        <PlaygroundChartBox
+          title="Histogramm gruppiert"
+          chart={
+            <Histogram
+              diagramData={histogramGrouped}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Histogramm mit negativen Werten"
+          chart={
+            <Histogram
+              diagramData={histogramWithNegativeValues}
+              grouping={grouping}
+              scaling={scaling}
+            />
+          }
+          switches={[groupingSwitch, scalingSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Histogramm mit vielen Werten"
+          chart={<Histogram diagramData={histogramWithManyValues} />}
+        />
+        <PlaygroundChartBox
+          title="Liniendiagramm simpel"
+          chart={
+            <LineChart
+              diagram={lineChartSimple}
+              configuration={lineChartSimpleConfiguration}
+            />
+          }
+          switches={[axisRangeSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Liniendiagramm gruppiert mit langen Attributnamen und Achsentiteln"
+          chart={
+            <LineChart
+              diagram={lineChartGroupedWithLongValues}
+              configuration={lineChartConfigurationWithLongAxisNames}
+            />
+          }
+          switches={[axisRangeSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Streudiagramm simpel"
+          chart={
+            <ScatterChart
+              filterSet={scatterChartSimple}
+              configuration={scatterChartConfig}
+            />
+          }
+          switches={[axisRangeSwitch, trendLineSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Streudiagramm mit Werten die weit auseinander sind"
+          chart={
+            <ScatterChart
+              filterSet={scatterChartLargeAndSmallValues}
+              configuration={scatterChartConfig}
+            />
+          }
+          switches={[axisRangeSwitch, trendLineSwitch]}
+        />
+        <PlaygroundChartBox
+          title="Choroplethenkarte"
+          chart={
+            <ChoroplethMap
+              diagramData={choroplethData}
+              colorScheme={colorScheme}
+              characteristicParameter={characteristicParameter}
+              geoJson={geoJson}
+            />
+          }
+          switches={[colorSchemeSelect, characteristicParameterSelect]}
+        />
       </Stack>
     </MainContentLayout>
   );
diff --git a/employee-portal/src/app/playground/formPlus/page.tsx b/employee-portal/src/app/playground/formPlus/page.tsx
index 96b26eb7d..2435ff367 100644
--- a/employee-portal/src/app/playground/formPlus/page.tsx
+++ b/employee-portal/src/app/playground/formPlus/page.tsx
@@ -49,7 +49,7 @@ export default function PlaygroundFormPlusPage() {
             telephoneNumberExt: "",
           }}
           onSubmit={async () => {
-            await new Promise((res) => setTimeout(res, 1000));
+            await new Promise((resolve) => setTimeout(resolve, 1000));
             snackbar.confirmation("Toll!");
           }}
         >
diff --git a/employee-portal/src/env/server.js b/employee-portal/src/env/server.js
index a1cea7259..7e3f805a5 100644
--- a/employee-portal/src/env/server.js
+++ b/employee-portal/src/env/server.js
@@ -30,6 +30,7 @@ const schema = object({
   PUBLIC_AUDITLOG_BACKEND_URL: pipe(string(), url()),
   PUBLIC_OPENDATA_BACKEND_URL: pipe(string(), url()),
   PUBLIC_STI_PROTECTION_BACKEND_URL: pipe(string(), url()),
+  PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: pipe(string(), url()),
 
   MARKDOWN_PAGE_DIRECTORY: string(),
 
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx b/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
index 70a9592d5..5694fddde 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogAccessibleTableView.tsx
@@ -49,12 +49,14 @@ export function AuditlogAccessibleTableView({
           <DataTable
             data={response.accessibleAuditLogs}
             columns={auditLogAccessibleColumns}
-            rowNavRoute={(row) =>
-              routes.auditlog.access(
-                row.original.auditLog.source,
-                format(row.original.auditLog.date, "yyyy-MM-dd"),
-              )
-            }
+            rowNavigation={{
+              route: (row) =>
+                routes.auditlog.access(
+                  row.original.auditLog.source,
+                  format(row.original.auditLog.date, "yyyy-MM-dd"),
+                ),
+              focusColumnAccessorKey: "auditLog.source",
+            }}
           />
         </TableSheet>
       </TablePage>
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx b/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
index 44707b477..a3ac9a5bb 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogCreatePasswordSidebar.tsx
@@ -64,11 +64,12 @@ export function AuditlogCreatePasswordSidebar({
         }}
         initialValues={{ validForm: "", password: "", repeatedPassword: "" }}
         onSubmit={async ({ password }) => {
-          await addEmployeeSelfUserKeys
-            .mutateAsync(await generateKeyPairs(password), {
+          await addEmployeeSelfUserKeys.mutateAsync(
+            await generateKeyPairs(password),
+            {
               onSuccess: () => router.push(routes.auditlog.index),
-            })
-            .catch();
+            },
+          );
         }}
       >
         {({ isSubmitting, values, errors }) => (
diff --git a/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx b/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
index 5e9f91872..b2f3f9ea6 100644
--- a/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
+++ b/employee-portal/src/lib/auditlog/components/AuditlogDeletePasswordButton.tsx
@@ -14,7 +14,7 @@ export function AuditlogDeletePasswordButton() {
   const deleteEmployeeUserKeys = useDeleteEmployeeUserKeys();
 
   async function handleConfirm() {
-    await deleteEmployeeUserKeys.mutateAsync().catch();
+    await deleteEmployeeUserKeys.mutateAsync();
   }
 
   return (
diff --git a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
index 14cfc93a8..c1817752c 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizePage.tsx
@@ -66,14 +66,16 @@ export function AuditLogAuthorizePage(
         <DataTable
           data={response.logs}
           columns={auditLogAuthorizeColumns}
-          rowNavRoute={(row) =>
-            buildRoutePreservingSearchParams(
-              routes.auditlog.authorize.grantAccess(
-                row.original.auditLogSource,
-                format(row.original.createdAt, "yyyy-MM-dd"),
+          rowNavigation={{
+            route: (row) =>
+              buildRoutePreservingSearchParams(
+                routes.auditlog.authorize.grantAccess(
+                  row.original.auditLogSource,
+                  format(row.original.createdAt, "yyyy-MM-dd"),
+                ),
               ),
-            )
-          }
+            focusColumnAccessorKey: "auditLogSource",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
index 766d836d3..08c79d5b3 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx
@@ -71,23 +71,21 @@ export function AuditLogAuthorizeSidebar({
   const userSelection = useRef([] as ApiUser[]);
 
   async function handleAuthorizeConfirmationDialog() {
-    await grantAuditLogAccess
-      .mutateAsync(
-        {
-          source: source,
-          date: date,
-          idsOfGrantedUser: new Set(
-            userSelection.current.map((user) => user.userId),
+    await grantAuditLogAccess.mutateAsync(
+      {
+        source: source,
+        date: date,
+        idsOfGrantedUser: new Set(
+          userSelection.current.map((user) => user.userId),
+        ),
+      },
+      {
+        onSuccess: () =>
+          router.push(
+            buildRoutePreservingSearchParams(routes.auditlog.authorize.index),
           ),
-        },
-        {
-          onSuccess: () =>
-            router.push(
-              buildRoutePreservingSearchParams(routes.auditlog.authorize.index),
-            ),
-        },
-      )
-      .catch();
+      },
+    );
   }
 
   const formRef = useRef<SidebarFormHandle>(null);
diff --git a/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx b/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
index 97a85bb19..99723d9b6 100644
--- a/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
+++ b/employee-portal/src/lib/auditlog/components/authorize/UserAutoCompleteField.tsx
@@ -75,9 +75,6 @@ function UserOption({ user }: { user: ApiUser }) {
       </ListItemDecorator>
       <ListItemContent>
         <Typography level="title-md">{fullName(user)}</Typography>
-        <Typography level={"body-sm"} textColor="text.secondary">
-          Gesundheitsamt Frankfurt
-        </Typography>
       </ListItemContent>
     </>
   );
diff --git a/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx b/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
index d01dacc59..f4daca7bb 100644
--- a/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/calendar/sidebar/AddAbsenceSidebar.tsx
@@ -34,20 +34,18 @@ export function AddAbsenceSidebar({
   const snackbar = useSnackbar();
 
   async function saveEvent(values: EventFormValues) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          request: mapFormToRequestValues(values, "VACATION", userCalendarId),
+    await submitCalendarEvent.mutateAsync(
+      {
+        request: mapFormToRequestValues(values, "VACATION", userCalendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Abwesenheit wurde erfolgreich gespeichert");
+          closeSidebar();
+          refetchEvents();
         },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Abwesenheit wurde erfolgreich gespeichert");
-            closeSidebar();
-            refetchEvents();
-          },
-        },
-      )
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
index 85b2b6572..2404884ee 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactDetails.tsx
@@ -6,11 +6,10 @@
 import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
 import { Divider, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 import { isDefined, isNonNullish } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { UpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
+import { useUpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
 import {
   Contact,
   isInstitutionContact,
@@ -40,7 +39,7 @@ import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 export function ContactDetails({ contact }: { contact: Contact }) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
   const hasWritePerms = useHasUserRoleCheck(ApiUserRole.BaseContactsWrite);
-  const [editSidebar, setEditSidebar] = useState(false);
+  const updateSidebar = useUpdateContactSidebar();
 
   const showEmailPhoneSection =
     (contact.emailAddresses?.length ?? 0) +
@@ -48,148 +47,129 @@ export function ContactDetails({ contact }: { contact: Contact }) {
     0;
 
   return (
-    <>
-      <ContentPanel dense={false} testId={"contact-details-panel"}>
-        {hasWritePerms && (
-          <EditButton
-            onClick={() => setEditSidebar(true)}
-            sx={{ maxWidth: "fit-content", flex: 0, alignSelf: "flex-end" }}
-          />
-        )}
-        <Stack
-          gap={3}
-          direction={{
-            xxs: "column",
-            md: "row",
-          }}
-          divider={<ResponsiveDivider />}
-        >
-          <DetailsColumn>
-            {isPersonContact(contact) && (
-              <>
-                <DetailsRow>
-                  {isDefined(contact.salutation) && (
-                    <DetailsCell
-                      name={"salutation"}
-                      label={"Anrede"}
-                      value={
-                        contact.salutation !== "NOT_SPECIFIED"
-                          ? SALUTATION_VALUES[contact.salutation]
-                          : undefined
-                      }
-                    />
-                  )}
-                  <DetailsCell
-                    name={"title"}
-                    label={"Titel"}
-                    value={getOptionalTitle(contact.title)}
-                  />
-                </DetailsRow>
-                <DetailsCell
-                  name={"firstName"}
-                  label={"Vorname"}
-                  value={contact.firstName}
-                />
-                <DetailsCell
-                  name={"name"}
-                  label={"Name"}
-                  value={contact.name}
-                />
-
-                {isDefined(contact.gender) && (
-                  <DetailsCell
-                    name={"gender"}
-                    label={"Geschlecht"}
-                    value={GENDER_VALUES[contact.gender]}
-                  />
-                )}
-
-                {showChatUsername && (
+    <ContentPanel dense={false} testId={"contact-details-panel"}>
+      {hasWritePerms && (
+        <EditButton
+          onClick={() => updateSidebar.open({ contact })}
+          sx={{ maxWidth: "fit-content", flex: 0, alignSelf: "flex-end" }}
+        />
+      )}
+      <Stack
+        gap={3}
+        direction={{
+          xxs: "column",
+          md: "row",
+        }}
+        divider={<ResponsiveDivider />}
+      >
+        <DetailsColumn>
+          {isPersonContact(contact) && (
+            <>
+              <DetailsRow>
+                {isDefined(contact.salutation) && (
                   <DetailsCell
-                    name={"externalChatUsername"}
-                    label={"Chat Nutzername"}
+                    name={"salutation"}
+                    label={"Anrede"}
                     value={
-                      isNonNullish(contact.externalChatUsername) ? (
-                        <InternalLink
-                          href={routes.userRoom(contact.externalChatUsername)}
-                        >
-                          {contact.externalChatUsername}
-                        </InternalLink>
-                      ) : undefined
+                      contact.salutation !== "NOT_SPECIFIED"
+                        ? SALUTATION_VALUES[contact.salutation]
+                        : undefined
                     }
                   />
                 )}
-              </>
-            )}
-            {isInstitutionContact(contact) && (
-              <>
                 <DetailsCell
-                  name={"name"}
-                  label={"Name"}
-                  value={contact.name}
+                  name={"title"}
+                  label={"Titel"}
+                  value={getOptionalTitle(contact.title)}
+                />
+              </DetailsRow>
+              <DetailsCell
+                name={"firstName"}
+                label={"Vorname"}
+                value={contact.firstName}
+              />
+              <DetailsCell name={"name"} label={"Name"} value={contact.name} />
+
+              {isDefined(contact.gender) && (
+                <DetailsCell
+                  name={"gender"}
+                  label={"Geschlecht"}
+                  value={GENDER_VALUES[contact.gender]}
                 />
+              )}
+
+              {showChatUsername && (
                 <DetailsCell
-                  name={"category"}
-                  label={"Objekttyp"}
+                  name={"externalChatUsername"}
+                  label={"Chat Nutzername"}
                   value={
-                    isNonNullish(contact.category)
-                      ? contactCategoryNames[contact.category]
-                      : undefined
+                    isNonNullish(contact.externalChatUsername) ? (
+                      <InternalLink
+                        href={routes.userRoom(contact.externalChatUsername)}
+                      >
+                        {contact.externalChatUsername}
+                      </InternalLink>
+                    ) : undefined
                   }
                 />
+              )}
+            </>
+          )}
+          {isInstitutionContact(contact) && (
+            <>
+              <DetailsCell name={"name"} label={"Name"} value={contact.name} />
+              <DetailsCell
+                name={"category"}
+                label={"Objekttyp"}
+                value={
+                  isNonNullish(contact.category)
+                    ? contactCategoryNames[contact.category]
+                    : undefined
+                }
+              />
+            </>
+          )}
+        </DetailsColumn>
+
+        {isDefined(contact.contactAddress) && (
+          <DetailsColumn>
+            <BaseAddressDetails
+              address={contact.contactAddress}
+              sx={{ flex: 1 }}
+            />
+            {isDefined(contact.differentBillingAddress) && (
+              <>
+                <Divider />
+                <Typography level={"title-md"}>Rechnungsadresse</Typography>
+                <BaseAddressDetails address={contact.differentBillingAddress} />
               </>
             )}
           </DetailsColumn>
+        )}
 
-          {isDefined(contact.contactAddress) && (
-            <DetailsColumn>
-              <BaseAddressDetails
-                address={contact.contactAddress}
-                sx={{ flex: 1 }}
+        {showEmailPhoneSection && (
+          <DetailsColumn>
+            {contact.emailAddresses?.map((emailAddress, index) => (
+              <ExternalLinkDetailsCell
+                key={[emailAddress, index].join("-")}
+                name={`emailAddresses.${index}`}
+                label={"E-Mail-Adresse"}
+                value={emailAddress}
+                href={emailHref}
               />
-              {isDefined(contact.differentBillingAddress) && (
-                <>
-                  <Divider />
-                  <Typography level={"title-md"}>Rechnungsadresse</Typography>
-                  <BaseAddressDetails
-                    address={contact.differentBillingAddress}
-                  />
-                </>
-              )}
-            </DetailsColumn>
-          )}
-
-          {showEmailPhoneSection && (
-            <DetailsColumn>
-              {contact.emailAddresses?.map((emailAddress, index) => (
-                <ExternalLinkDetailsCell
-                  key={[emailAddress, index].join("-")}
-                  name={`emailAddresses.${index}`}
-                  label={"E-Mail-Adresse"}
-                  value={emailAddress}
-                  href={emailHref}
-                />
-              ))}
-              {contact.phoneNumbers?.map((phoneNumber, index) => (
-                <ExternalLinkDetailsCell
-                  key={[phoneNumber, index].join("-")}
-                  name={`phoneNumbers.${index}`}
-                  label={"Telefonnummer"}
-                  value={phoneNumber}
-                  href={phoneHref}
-                />
-              ))}
-            </DetailsColumn>
-          )}
-        </Stack>
-      </ContentPanel>
-      {hasWritePerms && (
-        <UpdateContactSidebar
-          contact={contact}
-          open={editSidebar}
-          onClose={() => setEditSidebar(false)}
-        />
-      )}
-    </>
+            ))}
+            {contact.phoneNumbers?.map((phoneNumber, index) => (
+              <ExternalLinkDetailsCell
+                key={[phoneNumber, index].join("-")}
+                name={`phoneNumbers.${index}`}
+                label={"Telefonnummer"}
+                value={phoneNumber}
+                href={phoneHref}
+              />
+            ))}
+          </DetailsColumn>
+        )}
+      </Stack>
+    </ContentPanel>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
index 1cc45012f..49a8c78ae 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsOverview.tsx
@@ -10,25 +10,15 @@ import {
   ApiContactSortKey,
   ApiContactType,
 } from "@eshg/employee-portal-api/base";
-import { useState } from "react";
 
 import { useGetContactsOverviewPageQuery } from "@/lib/baseModule/api/queries/contacts";
 import { ContactsTable } from "@/lib/baseModule/components/contacts/ContactsTable";
-import { AddInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar";
-import { AddPersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddPersonContactSidebar";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
+import { useAddInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar";
+import { useAddPersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/AddPersonContactSidebar";
 import {
   PaginatedSearchParams,
   SortableSearchParams,
 } from "@/lib/shared/helpers/searchParams";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
-
-interface SidebarState {
-  open: boolean;
-  contactType: "AddInstitutionContactRequest" | "AddPersonContactRequest";
-  flowStep: "IMPORT" | "SEARCH";
-}
 
 export interface ContactOverviewSearchParams
   extends PaginatedSearchParams,
@@ -43,61 +33,35 @@ export function ContactsOverview({
 }: {
   params: ContactOverviewSearchParams;
 }) {
-  const [sidebarState, setSidebarState] = useState<SidebarState>({
-    open: false,
-    contactType: "AddPersonContactRequest",
-    flowStep: "IMPORT",
-  });
-
   const query = useGetContactsOverviewPageQuery(params);
   const response = query.isSuccess ? query.data : undefined;
 
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setSidebarState((state) => ({ ...state, open: false })),
-  });
+  const addInstitutionContactSidebar = useAddInstitutionContactSidebar();
+  const addPersonContactSidebar = useAddPersonContactSidebar();
 
   return (
-    <>
-      <ContactsTable
-        loading={query.isFetching}
-        elements={response?.elements ?? []}
-        totalNumberOfElements={response?.totalNumberOfElements ?? 0}
-        onCreate={(type) =>
-          setSidebarState({
-            contactType: type,
+    <ContactsTable
+      loading={query.isFetching}
+      elements={response?.elements ?? []}
+      totalNumberOfElements={response?.totalNumberOfElements ?? 0}
+      onCreate={(type) => {
+        if (type === "AddInstitutionContactRequest") {
+          addInstitutionContactSidebar.open({
             flowStep: "SEARCH",
-            open: true,
-          })
+          });
+        } else {
+          addPersonContactSidebar.open({ flowStep: "SEARCH" });
         }
-        onImport={(type) =>
-          setSidebarState({
-            contactType: type,
+      }}
+      onImport={(type) => {
+        if (type === "AddInstitutionContactRequest") {
+          addInstitutionContactSidebar.open({
             flowStep: "IMPORT",
-            open: true,
-          })
+          });
+        } else {
+          addPersonContactSidebar.open({ flowStep: "IMPORT" });
         }
-      />
-
-      <OverlayBoundary>
-        <Sidebar open={sidebarState.open} onClose={handleClose}>
-          {sidebarState.open &&
-            (sidebarState.contactType === "AddPersonContactRequest" ? (
-              <AddPersonContactSidebar
-                onClose={handleClose}
-                onSuccess={closeSidebar}
-                flowStep={sidebarState.flowStep}
-                sidebarFormRef={sidebarFormRef}
-              />
-            ) : (
-              <AddInstitutionContactSidebar
-                onClose={handleClose}
-                onSuccess={closeSidebar}
-                flowStep={sidebarState.flowStep}
-                sidebarFormRef={sidebarFormRef}
-              />
-            ))}
-        </Sidebar>
-      </OverlayBoundary>
-    </>
+      }}
+    />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
index 66d71351e..a431a7719 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/ContactsTable.tsx
@@ -32,7 +32,7 @@ import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import { ContactsTableTitle } from "@/lib/baseModule/components/contacts/ContactsTableTitle";
 import { useMergeInstitutionContactSidebar } from "@/lib/baseModule/components/contacts/modals/MergeInstitutionContactSidebar";
 import { useMergePersonContactSidebar } from "@/lib/baseModule/components/contacts/modals/MergePersonContactSidebar";
-import { UpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
+import { useUpdateContactSidebar } from "@/lib/baseModule/components/contacts/modals/UpdateContactSidebar";
 import { Contact } from "@/lib/baseModule/components/contacts/types";
 import { routes } from "@/lib/baseModule/shared/routes";
 import { contactCategoryNames } from "@/lib/baseModule/shared/translations";
@@ -116,11 +116,7 @@ export function ContactsTable({
 
   const institutionMergeSidebar = useMergeInstitutionContactSidebar();
   const personMergeSidebar = useMergePersonContactSidebar();
-
-  const [editSidebar, setEditSidebar] = useState<{
-    open: boolean;
-    contact?: Contact;
-  }>({ open: false });
+  const updateSidebar = useUpdateContactSidebar();
 
   const { selectedContacts, rowSelection, rowSelectionProps } =
     usePersistentSelectionCache({
@@ -223,25 +219,19 @@ export function ContactsTable({
             data={elements}
             columns={contactTableColumns({
               hasWritePerms,
-              onEdit: (contact) => setEditSidebar({ open: true, contact }),
+              onEdit: (contact) => updateSidebar.open({ contact }),
             })}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.contacts.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.contacts.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             rowSelectionProps={
               isContactMergeEnabled ? rowSelectionProps : undefined
             }
           />
         </TableSheet>
       </TablePage>
-
-      {hasWritePerms && (
-        <UpdateContactSidebar
-          open={editSidebar.open}
-          contact={editSidebar.contact}
-          onClose={() => setEditSidebar({ open: false })}
-        />
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
index 6a302f704..4644d321b 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/columns.tsx
@@ -94,7 +94,6 @@ export function contactTableColumns({
       id: "navigationControl",
       cell: (props) => (
         <ActionsMenu
-          disablePortal
           actionItems={[
             {
               label: "Anzeigen",
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
index 823f24165..673b001c0 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/ContactEntityForm.tsx
@@ -71,17 +71,13 @@ export function ContactEntityForm({
 
   async function handleSubmit(values: ContactFormValues) {
     if (isDefined(contactId)) {
-      await updateContact
-        .mutateAsync(mapUpdateContactRequest(values), {
-          onSuccess: () => onUpdated?.(),
-        })
-        .catch();
+      await updateContact.mutateAsync(mapUpdateContactRequest(values), {
+        onSuccess: () => onUpdated?.(),
+      });
     } else {
-      await createContact
-        .mutateAsync(mapAddContactRequest(values), {
-          onSuccess: ({ id }) => router.push(routes.contacts.details(id)),
-        })
-        .catch();
+      await createContact.mutateAsync(mapAddContactRequest(values), {
+        onSuccess: ({ id }) => router.push(routes.contacts.details(id)),
+      });
     }
   }
 
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
index efc28afe6..1464b960a 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm.tsx
@@ -51,16 +51,14 @@ export function InstitutionContactImportForm(props: ContactImportFormProps) {
     useState<ApiImportInstitutionContactResponse>();
 
   async function handleSubmit(file: File) {
-    await importInstitutionContact
-      .mutateAsync(file, {
-        onSuccess: (response) => {
-          setSearchResults(response);
-          if (response.totalNumberOfMatches === 0) {
-            props.onImported(mapImportToCreate(response.vCard));
-          }
-        },
-      })
-      .catch();
+    await importInstitutionContact.mutateAsync(file, {
+      onSuccess: (response) => {
+        setSearchResults(response);
+        if (response.totalNumberOfMatches === 0) {
+          props.onImported(mapImportToCreate(response.vCard));
+        }
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
index d377f794b..150f87937 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/import/PersonContactImportForm.tsx
@@ -52,16 +52,14 @@ export function PersonContactImportForm(props: ContactImportFormProps) {
     useState<ApiImportPersonContactResponse>();
 
   async function handleSubmit(file: File) {
-    await importPersonContact
-      .mutateAsync(file, {
-        onSuccess: (response) => {
-          setSearchResults(response);
-          if (response.totalNumberOfMatches === 0) {
-            props.onImported(mapImportToCreate(response.vCard));
-          }
-        },
-      })
-      .catch();
+    await importPersonContact.mutateAsync(file, {
+      onSuccess: (response) => {
+        setSearchResults(response);
+        if (response.totalNumberOfMatches === 0) {
+          props.onImported(mapImportToCreate(response.vCard));
+        }
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
index 843462fe7..d53c64000 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeInstitutionContactForm.tsx
@@ -107,17 +107,15 @@ export function MergeInstitutionContactForm({
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergeInstitutionContactFormValues) {
-    await updateContact
-      .mutateAsync(
-        mapImportMergeContactRequest(
-          values,
-          from.type === "Entity" ? from.data.id : undefined,
-        ),
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateContact.mutateAsync(
+      mapImportMergeContactRequest(
+        values,
+        from.type === "Entity" ? from.data.id : undefined,
+      ),
+      {
+        onSuccess,
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
index 34d377712..627f8765d 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergePersonContactForm.tsx
@@ -120,17 +120,15 @@ export function MergePersonContactForm({
   const updateContact = useUpdateContactMutation(into.id);
 
   async function handleSubmit(values: MergePersonContactFormValues) {
-    await updateContact
-      .mutateAsync(
-        mapImportMergeContactRequest(
-          values,
-          from.type === "Entity" ? from.data.id : undefined,
-        ),
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateContact.mutateAsync(
+      mapImportMergeContactRequest(
+        values,
+        from.type === "Entity" ? from.data.id : undefined,
+      ),
+      {
+        onSuccess,
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
index bab05d4f5..a320b490b 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddInstitutionContactSidebar.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiInstitutionContact } from "@eshg/employee-portal-api/base";
-import { Ref, useState } from "react";
+import { useState } from "react";
 
 import { ContactEntityForm } from "@/lib/baseModule/components/contacts/forms/ContactEntityForm";
 import { InstitutionContactImportForm } from "@/lib/baseModule/components/contacts/forms/import/InstitutionContactImportForm";
@@ -14,19 +14,19 @@ import {
   AddContactSidebarState,
   InstitutionContactFormValues,
 } from "@/lib/baseModule/components/contacts/types";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
 import { createEmptyAddress } from "@/lib/shared/components/form/address/helpers";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 type AddInstitutionContactSidebarState = AddContactSidebarState<
   InstitutionContactFormValues,
   ApiInstitutionContact
 >;
 
-type CreateContactSidebarProps = AddInstitutionContactSidebarState & {
-  onClose: () => void;
-  onSuccess: () => void;
-  sidebarFormRef: Ref<SidebarFormHandle>;
-};
+type CreateContactSidebarProps = SidebarWithFormRefProps &
+  AddInstitutionContactSidebarState;
 
 const initialCreateContactFormValues = {
   type: "AddInstitutionContactRequest",
@@ -38,10 +38,15 @@ const initialCreateContactFormValues = {
   differentBillingAddress: undefined,
 } as const satisfies InstitutionContactFormValues;
 
-export function AddInstitutionContactSidebar({
+export function useAddInstitutionContactSidebar() {
+  return useSidebarWithFormRef({
+    component: AddInstitutionContactSidebar,
+  });
+}
+
+function AddInstitutionContactSidebar({
   onClose,
-  onSuccess,
-  sidebarFormRef,
+  formRef,
   ...initialState
 }: CreateContactSidebarProps) {
   const [formState, setFormState] =
@@ -64,16 +69,16 @@ export function AddInstitutionContactSidebar({
               into: into,
             })
           }
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "CREATE" && (
         <ContactEntityForm
           type={"INSTITUTION"}
           initialValues={formState.initialValues}
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "SEARCH" && (
@@ -99,9 +104,9 @@ export function AddInstitutionContactSidebar({
           from={formState.from}
           intoLabel={"Aktuell"}
           fromLabel={"Importiert"}
-          onCancel={onClose}
-          onSuccess={onSuccess}
-          sidebarFormRef={sidebarFormRef}
+          onCancel={() => onClose(false)}
+          onSuccess={() => onClose(true)}
+          sidebarFormRef={formRef}
         />
       )}
     </>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
index 371e06d8c..fdaeb813f 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/AddPersonContactSidebar.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiPersonContact } from "@eshg/employee-portal-api/base";
-import { Ref, useState } from "react";
+import { useState } from "react";
 
 import { ContactEntityForm } from "@/lib/baseModule/components/contacts/forms/ContactEntityForm";
 import { PersonContactImportForm } from "@/lib/baseModule/components/contacts/forms/import/PersonContactImportForm";
@@ -14,19 +14,19 @@ import {
   AddContactSidebarState,
   PersonContactFormValues,
 } from "@/lib/baseModule/components/contacts/types";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
 import { createEmptyAddress } from "@/lib/shared/components/form/address/helpers";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 type AddPersonContactSidebarState = AddContactSidebarState<
   PersonContactFormValues,
   ApiPersonContact
 >;
 
-type CreateContactSidebarProps = AddPersonContactSidebarState & {
-  onClose: () => void;
-  onSuccess: () => void;
-  sidebarFormRef: Ref<SidebarFormHandle>;
-};
+type CreateContactSidebarProps = AddPersonContactSidebarState &
+  SidebarWithFormRefProps;
 
 const initialCreateContactFormValues = {
   type: "AddPersonContactRequest",
@@ -42,10 +42,15 @@ const initialCreateContactFormValues = {
   differentBillingAddress: undefined,
 } as const satisfies PersonContactFormValues;
 
-export function AddPersonContactSidebar({
+export function useAddPersonContactSidebar() {
+  return useSidebarWithFormRef({
+    component: AddPersonContactSidebar,
+  });
+}
+
+function AddPersonContactSidebar({
   onClose,
-  onSuccess,
-  sidebarFormRef,
+  formRef,
   ...initialState
 }: CreateContactSidebarProps) {
   const [formState, setFormState] =
@@ -68,16 +73,16 @@ export function AddPersonContactSidebar({
               into: into,
             })
           }
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "CREATE" && (
         <ContactEntityForm
           type={"PERSON"}
           initialValues={formState.initialValues}
-          onClose={onClose}
-          sidebarFormRef={sidebarFormRef}
+          onClose={() => onClose(false)}
+          sidebarFormRef={formRef}
         />
       )}
       {formState.flowStep === "SEARCH" && (
@@ -100,9 +105,9 @@ export function AddPersonContactSidebar({
           from={formState.from}
           intoLabel={"Aktuell"}
           fromLabel={"Importiert"}
-          onCancel={onClose}
-          onSuccess={onSuccess}
-          sidebarFormRef={sidebarFormRef}
+          onCancel={() => onClose(false)}
+          onSuccess={() => onClose(true)}
+          sidebarFormRef={formRef}
         />
       )}
     </>
diff --git a/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx b/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
index d0b600245..880c253a7 100644
--- a/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/contacts/modals/UpdateContactSidebar.tsx
@@ -17,50 +17,39 @@ import {
   PersonContactFormValues,
   isPersonContact,
 } from "@/lib/baseModule/components/contacts/types";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import {
   createEmptyAddress,
   mapApiAddressToForm,
 } from "@/lib/shared/components/form/address/helpers";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
-interface UpdateContactSidebarProps {
-  contact: Contact | undefined;
-  open: boolean;
-  onClose: () => void;
+interface UpdateContactSidebarProps extends SidebarWithFormRefProps {
+  contact: Contact;
 }
 
-export function UpdateContactSidebar(props: UpdateContactSidebarProps) {
-  return (
-    <OverlayBoundary>
-      <UpdateContactSidebarWithinBoundary {...props} />
-    </OverlayBoundary>
-  );
+export function useUpdateContactSidebar() {
+  return useSidebarWithFormRef({
+    component: UpdateContactSidebar,
+  });
 }
 
-function UpdateContactSidebarWithinBoundary({
+function UpdateContactSidebar({
   contact,
-  open,
   onClose,
+  formRef,
 }: UpdateContactSidebarProps) {
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose,
-  });
-
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      {open && isDefined(contact) && (
-        <ContactEntityForm
-          contactId={contact.id}
-          initialValues={mapContactToForm(contact)}
-          onClose={handleClose}
-          onUpdated={closeSidebar}
-          type={contactDiscriminatorToEnum[contact.type]}
-          sidebarFormRef={sidebarFormRef}
-        />
-      )}
-    </Sidebar>
+    <ContactEntityForm
+      contactId={contact.id}
+      initialValues={mapContactToForm(contact)}
+      onClose={() => onClose(false)}
+      onUpdated={() => onClose(true)}
+      type={contactDiscriminatorToEnum[contact.type]}
+      sidebarFormRef={formRef}
+    />
   );
 }
 
diff --git a/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx b/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
index 2711ef9b1..015d4de61 100644
--- a/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/dashboard/DashboardProceduresTable.tsx
@@ -37,13 +37,15 @@ export function DashboardProceduresTable() {
       <DataTable
         data={procedures}
         columns={proceduresColumns}
-        rowNavRoute={(row) =>
-          resolveProcedureDetailsRoute({
-            businessModule: row.original.businessModule,
-            procedureId: row.original.procedureId,
-            status: row.original.procedureStatus,
-          })
-        }
+        rowNavigation={{
+          route: (row) =>
+            resolveProcedureDetailsRoute({
+              businessModule: row.original.businessModule,
+              procedureId: row.original.procedureId,
+              status: row.original.procedureStatus,
+            }),
+          focusColumnAccessorKey: "procedureType",
+        }}
         sorting={{
           manualSorting: false,
           initialSorting,
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
index 9c3a4cbec..110dd5ccb 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx
@@ -16,18 +16,13 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 import { Divider, Grid } from "@mui/joy";
 import { Formik } from "formik";
 import { useRouter } from "next/navigation";
-import { useRef } from "react";
 
 import { mapAddGdprProcedureRequest } from "@/lib/baseModule/api/mapper/gdpr";
 import { useAddGdprProcedure } from "@/lib/baseModule/api/mutations/gdpr";
 import { TYPE_OPTIONS } from "@/lib/baseModule/components/gdpr/i18n";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { ContactAddressForm } from "@/lib/shared/components/form/address/BaseAddressForm";
 import {
   BaseAddressFormInputs,
@@ -35,9 +30,12 @@ import {
 } from "@/lib/shared/components/form/address/helpers";
 import { EmailField } from "@/lib/shared/components/formFields/EmailField";
 import { SALUTATION_OPTIONS } from "@/lib/shared/components/personSidebar/constants";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 export interface GDPRProcedureFormInputs {
   type: OptionalFieldValue<ApiGdprProcedureType>;
@@ -65,130 +63,110 @@ function initialValues(): GDPRProcedureFormInputs {
   };
 }
 
-interface CreateGDPRProcedureSidebarProps {
-  open: boolean;
-  onClose: () => void;
+export function useCreateGDPRProcedureSidebar() {
+  return useSidebarWithFormRef({
+    component: CreateGDPRProcedureSidebar,
+  });
 }
 
-export function CreateGDPRProcedureSidebar({
-  open,
+function CreateGDPRProcedureSidebar({
   onClose,
-}: CreateGDPRProcedureSidebarProps) {
+  formRef,
+}: SidebarWithFormRefProps) {
   const router = useRouter();
   const fieldName = createFieldNameMapper<GDPRProcedureFormInputs>();
-  const formRef = useRef<SidebarFormHandle>(null);
-  const { openCancelDialog } = useConfirmationDialog();
-
-  function closeAndReset() {
-    onClose();
-    formRef.current?.resetForm();
-  }
 
   const addGdprProcedure = useAddGdprProcedure();
 
-  function handleClose() {
-    if (formRef.current?.dirty) {
-      openCancelDialog({
-        onConfirm: closeAndReset,
-      });
-    } else {
-      closeAndReset();
-    }
-  }
-
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      <Formik
-        initialValues={initialValues()}
-        onSubmit={async (values) => {
-          await addGdprProcedure
-            .mutateAsync(mapAddGdprProcedureRequest(values), {
-              onSuccess: ({ id }) => router.push(routes.gdpr.details(id)),
-            })
-            .catch();
-        }}
-      >
-        {({ isSubmitting, values }) => (
-          <SidebarForm ref={formRef}>
-            <SidebarContent title={"Vorgang anlegen"}>
-              <Grid container spacing={3}>
-                <Grid xxs={12}>
-                  <SelectField
-                    options={TYPE_OPTIONS}
-                    name={fieldName("type")}
-                    label={"Vorgangsart"}
-                    required={"Bitte die Art des Vorgangs angeben"}
-                  />
-                </Grid>
-                <Grid xxs={12} xs={6}>
-                  <SelectField
-                    options={SALUTATION_OPTIONS}
-                    name={fieldName("salutation")}
-                    label={"Anrede"}
-                  />
-                </Grid>
-                <Grid xxs={12} xs={6}>
-                  <InputField name={fieldName("title")} label={"Titel"} />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("firstName")}
-                    label={"Vorname"}
-                    required={"Bitte einen Vornamen angeben"}
-                    validate={validateLength(1, 80)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("lastName")}
-                    label={"Nachname"}
-                    required={"Bitte einen Nachname angeben"}
-                    validate={validateLength(1, 120)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <DateField
-                    name={fieldName("dateOfBirth")}
-                    label={"Geburtsdatum"}
-                    required={"Bitte ein Geburtsdatum eingeben"}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <Divider />
-                </Grid>
-                <ContactAddressForm
-                  type={values.address.type}
-                  name={fieldName("address")}
+    <Formik
+      initialValues={initialValues()}
+      onSubmit={async (values) => {
+        await addGdprProcedure.mutateAsync(mapAddGdprProcedureRequest(values), {
+          onSuccess: ({ id }) => router.push(routes.gdpr.details(id)),
+        });
+      }}
+    >
+      {({ isSubmitting, values }) => (
+        <SidebarForm ref={formRef}>
+          <SidebarContent title={"Vorgang anlegen"}>
+            <Grid container spacing={3}>
+              <Grid xxs={12}>
+                <SelectField
+                  options={TYPE_OPTIONS}
+                  name={fieldName("type")}
+                  label={"Vorgangsart"}
+                  required={"Bitte die Art des Vorgangs angeben"}
+                />
+              </Grid>
+              <Grid xxs={12} xs={6}>
+                <SelectField
+                  options={SALUTATION_OPTIONS}
+                  name={fieldName("salutation")}
+                  label={"Anrede"}
                 />
-                <Grid xxs={12}>
-                  <Divider />
-                </Grid>
-                <Grid xxs={12}>
-                  <EmailField
-                    name={fieldName("emailAddress")}
-                    label={"E-Mail-Adresse"}
-                    validate={validateLength(6, 254)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={fieldName("phoneNumber")}
-                    label={"Telefonnummer"}
-                    validate={validateLength(1, 23)}
-                  />
-                </Grid>
               </Grid>
-            </SidebarContent>
-            <SidebarActions>
-              <FormButtonBar
-                submitLabel={"Anlegen"}
-                submitting={isSubmitting}
-                onCancel={handleClose}
+              <Grid xxs={12} xs={6}>
+                <InputField name={fieldName("title")} label={"Titel"} />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("firstName")}
+                  label={"Vorname"}
+                  required={"Bitte einen Vornamen angeben"}
+                  validate={validateLength(1, 80)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("lastName")}
+                  label={"Nachname"}
+                  required={"Bitte einen Nachname angeben"}
+                  validate={validateLength(1, 120)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <DateField
+                  name={fieldName("dateOfBirth")}
+                  label={"Geburtsdatum"}
+                  required={"Bitte ein Geburtsdatum eingeben"}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <Divider />
+              </Grid>
+              <ContactAddressForm
+                type={values.address.type}
+                name={fieldName("address")}
               />
-            </SidebarActions>
-          </SidebarForm>
-        )}
-      </Formik>
-    </Sidebar>
+              <Grid xxs={12}>
+                <Divider />
+              </Grid>
+              <Grid xxs={12}>
+                <EmailField
+                  name={fieldName("emailAddress")}
+                  label={"E-Mail-Adresse"}
+                  validate={validateLength(6, 254)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={fieldName("phoneNumber")}
+                  label={"Telefonnummer"}
+                  validate={validateLength(1, 23)}
+                />
+              </Grid>
+            </Grid>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitLabel={"Anlegen"}
+              submitting={isSubmitting}
+              onCancel={() => onClose(false)}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx b/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
index f031d0bd7..5a180346b 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/overview/GDPRTable.tsx
@@ -11,13 +11,11 @@ import {
 } from "@eshg/employee-portal-api/base";
 import AddIcon from "@mui/icons-material/Add";
 import { Button } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetGdprProcedureOverviewQuery } from "@/lib/baseModule/api/queries/gdpr";
 import { TYPE_OPTIONS } from "@/lib/baseModule/components/gdpr/i18n";
-import { CreateGDPRProcedureSidebar } from "@/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar";
+import { useCreateGDPRProcedureSidebar } from "@/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar";
 import { columns } from "@/lib/baseModule/components/gdpr/overview/columns";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { Pagination } from "@/lib/shared/components/pagination/Pagination";
 import { DataTable } from "@/lib/shared/components/table/DataTable";
@@ -33,63 +31,53 @@ export function GDPRTable({ params }: { params: GetGdprProceduresRequest }) {
     serverSideSorting: true,
     sortFieldName: "sortKey",
   });
-  const [open, setOpen] = useState(false);
   const {
     data: { elements, totalNumberOfElements },
     isFetching,
   } = useGetGdprProcedureOverviewQuery(params);
+  const sidebar = useCreateGDPRProcedureSidebar();
 
   return (
-    <>
-      <TablePage
-        data-testid="gdpr-procedures-table"
-        controls={
-          <ButtonBar
-            left={
-              <SingleSelectFilter
-                tableControl={tableControl}
-                placeholder={"Typ"}
-                searchParamName={"type"}
-                options={TYPE_OPTIONS}
-              />
-            }
-            right={
-              hasWritePerms && (
-                <Button
-                  onClick={() => setOpen(true)}
-                  startDecorator={<AddIcon />}
-                >
-                  Vorgang anlegen
-                </Button>
-              )
-            }
-          />
-        }
-      >
-        <TableSheet
-          loading={isFetching}
-          footer={
-            <Pagination
-              totalCount={totalNumberOfElements}
-              {...tableControl.paginationProps}
+    <TablePage
+      data-testid="gdpr-procedures-table"
+      controls={
+        <ButtonBar
+          left={
+            <SingleSelectFilter
+              tableControl={tableControl}
+              placeholder={"Typ"}
+              searchParamName={"type"}
+              options={TYPE_OPTIONS}
             />
           }
-        >
-          <DataTable
-            data={elements}
-            columns={columns}
-            sorting={tableControl.tableSorting}
-          />
-        </TableSheet>
-      </TablePage>
-      {hasWritePerms && (
-        <OverlayBoundary>
-          <CreateGDPRProcedureSidebar
-            open={open}
-            onClose={() => setOpen(false)}
+          right={
+            hasWritePerms && (
+              <Button
+                onClick={() => sidebar.open()}
+                startDecorator={<AddIcon />}
+              >
+                Vorgang anlegen
+              </Button>
+            )
+          }
+        />
+      }
+    >
+      <TableSheet
+        loading={isFetching}
+        footer={
+          <Pagination
+            totalCount={totalNumberOfElements}
+            {...tableControl.paginationProps}
           />
-        </OverlayBoundary>
-      )}
-    </>
+        }
+      >
+        <DataTable
+          data={elements}
+          columns={columns}
+          sorting={tableControl.tableSorting}
+        />
+      </TableSheet>
+    </TablePage>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
index 4e85aab93..336315a87 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/linkCentralFileSidebar/LinkCentralFileSidebar.tsx
@@ -119,15 +119,13 @@ function LinkCentralFileSidebar<TMatch extends CentralFileData>({
 
   async function doSubmit() {
     if (isNonNullish(selected)) {
-      await addCentralFileIdToGdprProcedure
-        .mutateAsync(
-          mapAddCentralFileIdToGdprProcedureRequest(
-            selected.id,
-            procedureVersion,
-          ),
-          { onSuccess: onClose },
-        )
-        .catch();
+      await addCentralFileIdToGdprProcedure.mutateAsync(
+        mapAddCentralFileIdToGdprProcedureRequest(
+          selected.id,
+          procedureVersion,
+        ),
+        { onSuccess: onClose },
+      );
     } else {
       onClose();
     }
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
index fabbc82b8..0cb9dccf8 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar.tsx
@@ -57,11 +57,9 @@ function EditMatterOfConcernSidebar({
         date: formatDate(procedure.createdAt),
       }}
       onSubmit={async (values: EditMatterOfConcernFormValues) => {
-        await setMatterOfConcern
-          .mutateAsync(values.matterOfConcern, {
-            onSuccess: () => onClose(true),
-          })
-          .catch();
+        await setMatterOfConcern.mutateAsync(values.matterOfConcern, {
+          onSuccess: () => onClose(true),
+        });
       }}
     >
       {({ isSubmitting }) => (
diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
index 42d19fd47..00ad07ee4 100644
--- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
+++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/ProcedureDetailsTile.tsx
@@ -55,9 +55,9 @@ export function ProcedureDetailsTile({
       });
     } else {
       alert.close();
-      await changeProcedureStatus
-        .mutateAsync(ApiGdprProcedureStatus.InProgress)
-        .catch();
+      await changeProcedureStatus.mutateAsync(
+        ApiGdprProcedureStatus.InProgress,
+      );
     }
   }
 
diff --git a/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx b/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
index 9829411f1..641c2f503 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/InventoryTable.tsx
@@ -120,8 +120,10 @@ export function InventoryTable({ params }: InventoryTableProps) {
             data={elements}
             minWidth="60rem"
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.inventory.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.inventory.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             columns={inventoryColumns({
               isAdmin,
               onCorrection: (item) =>
diff --git a/employee-portal/src/lib/baseModule/components/inventory/columns.tsx b/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
index bde83afd6..63627942d 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/columns.tsx
@@ -86,7 +86,6 @@ export function inventoryColumns({
       enableSorting: false,
       cell: (props) => (
         <ActionsMenu
-          disablePortal
           actionItems={[
             {
               label: "Anzeigen",
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
index 4ce384963..30be37571 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/AddInventorySidebar.tsx
@@ -43,14 +43,12 @@ function AddInventorySidebar(props: AddInventorySidebarProps) {
   const createInventory = useAddInventoryItem();
 
   async function handleSubmit(values: InventoryFormValues) {
-    await createInventory
-      .mutateAsync(mapAddInventoryItemRequest(values), {
-        onSuccess: (item) => {
-          props.onClose(true);
-          router.push(routes.inventory.details(item.id));
-        },
-      })
-      .catch();
+    await createInventory.mutateAsync(mapAddInventoryItemRequest(values), {
+      onSuccess: (item) => {
+        props.onClose(true);
+        router.push(routes.inventory.details(item.id));
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
index f1c07e40b..994f808ce 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryRestockSidebar.tsx
@@ -38,11 +38,9 @@ function InventoryRestockSidebar({
       formRef={formRef}
       onClose={onClose}
       onSubmit={async (values) => {
-        await restockInventory
-          .mutateAsync(mapRequiredValue(values.count), {
-            onSuccess: () => onClose(true),
-          })
-          .catch();
+        await restockInventory.mutateAsync(mapRequiredValue(values.count), {
+          onSuccess: () => onClose(true),
+        });
       }}
     />
   );
diff --git a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
index d0fe17e0a..cf87165e2 100644
--- a/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/inventory/modals/InventoryUpdateSidebar.tsx
@@ -40,11 +40,9 @@ function InventoryUpdateSidebar({
   const updateInventory = useUpdateInventoryItem(inventory.id);
 
   async function handleSubmit(values: InventoryFormValues) {
-    await updateInventory
-      .mutateAsync(mapUpdateInventoryItemRequest(values), {
-        onSuccess: () => onClose(true),
-      })
-      .catch();
+    await updateInventory.mutateAsync(mapUpdateInventoryItemRequest(values), {
+      onSuccess: () => onClose(true),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx b/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
index 605276839..8db0953b9 100644
--- a/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/header/HeaderButtons.tsx
@@ -13,6 +13,11 @@ import { useSelfUserSidebar } from "@/lib/baseModule/components/layout/SelfUserS
 import { HeaderIconButton } from "@/lib/baseModule/components/layout/header/HeaderIconButton";
 import { useNotificationsSidebar } from "@/lib/baseModule/components/layout/notificationsSidebar/NotificationsSidebar";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { useGetSelfUserPresence } from "@/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence";
+import {
+  getPresenseLabel,
+  getStatusColor,
+} from "@/lib/businessModules/chat/shared/utils";
 
 import { HeaderMessagesButton } from "./HeaderMessagesButton";
 
@@ -23,6 +28,8 @@ export function HeaderButtons() {
   const { data: notificationResponse } = useGetUnreadNotifications();
   const notificationsSidebar = useNotificationsSidebar();
 
+  const { userPresence, sharePresence } = useGetSelfUserPresence();
+
   const notificationsCount = notificationResponse
     ? notificationResponse.notifications.length
     : 0;
@@ -65,14 +72,28 @@ export function HeaderButtons() {
         </Badge>
       </HeaderIconButton>
       {canAccessChat && <HeaderMessagesButton />}
+
       <HeaderIconButton
-        aria-label="Benutzer"
+        aria-label={`Benutzer (${getPresenseLabel(userPresence)})`}
         sx={{
           backgroundColor: "transparent",
         }}
         onClick={toggleUserSidebar}
       >
-        <UserIcon sx={{ color: "background.body" }} />
+        <Badge
+          invisible={!canAccessChat || !sharePresence}
+          size="sm"
+          badgeInset="18%"
+          anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
+          sx={{
+            "& .MuiBadge-badge": {
+              backgroundColor: getStatusColor(userPresence),
+              boxShadow: "0 0 0 1px",
+            },
+          }}
+        >
+          <UserIcon sx={{ color: "background.body" }} />
+        </Badge>
       </HeaderIconButton>
     </Box>
   );
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
index 5079ee1e8..aa2cda16e 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListExpanded.tsx
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { LoadingOverlay } from "@eshg/lib-portal/components/LoadingOverlay";
 import { ExpandNavigation } from "@eshg/lib-portal/components/icons/ExpandNavigation";
 import { Button, Stack, Typography } from "@mui/joy";
 import { Dispatch, SetStateAction } from "react";
@@ -18,10 +19,12 @@ export function NavigationListExpanded({
   setCollapsed,
   showCollapseButton,
   items,
+  isLoading,
 }: {
   setCollapsed?: Dispatch<SetStateAction<boolean>>;
   showCollapseButton: boolean;
   items: SideNavigationItem[];
+  isLoading: boolean;
 }) {
   return (
     <Stack
@@ -64,6 +67,7 @@ export function NavigationListExpanded({
             <NavigationItem key={item.name} item={item} />
           ))}
         </StyledList>
+        {isLoading && <LoadingOverlay />}
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
index 5216c4c40..9abeeb196 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/SideNavigation.tsx
@@ -25,7 +25,7 @@ export function SideNavigation({
   setCollapsed: Dispatch<SetStateAction<boolean>>;
 }) {
   const sidenav = useSidenav();
-  const items = useNavigationItems();
+  const { isLoading, items } = useNavigationItems();
 
   return (
     <>
@@ -45,6 +45,7 @@ export function SideNavigation({
             showCollapseButton
             setCollapsed={setCollapsed}
             items={items}
+            isLoading={isLoading}
           />
         ) : (
           <NavigationListCollapsed setCollapsed={setCollapsed} items={items} />
@@ -76,7 +77,11 @@ export function SideNavigation({
             display: "flex",
           }}
         >
-          <NavigationListExpanded showCollapseButton={false} items={items} />
+          <NavigationListExpanded
+            showCollapseButton={false}
+            items={items}
+            isLoading={isLoading}
+          />
         </Box>
       </Drawer>
     </>
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
index 4116c696d..8a69a041c 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/types.ts
@@ -36,3 +36,8 @@ export interface SideNavigationSubItem {
 export type SideNavigationItem =
   | SideNavigationItemWithoutSubItems
   | SideNavigationItemWithSubItems;
+
+export interface UseSideNavigationItemsResult {
+  isLoading: boolean;
+  items: SideNavigationItem[];
+}
diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
index 83f0a7206..7f4e881da 100644
--- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
+++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/useNavigationItems.ts
@@ -7,7 +7,7 @@ import { useResolveSideNavigationItems } from "@/lib/baseModule/moduleRegister/s
 import { AccessCheck } from "@/lib/shared/helpers/accessControl";
 import { useAccessControl } from "@/lib/shared/hooks/useAccessControl";
 
-import { SideNavigationItem } from "./types";
+import { SideNavigationItem, UseSideNavigationItemsResult } from "./types";
 
 export function filterNavigationItemsWithAccess(
   items: SideNavigationItem[],
@@ -44,9 +44,12 @@ export function filterNavigationItemsWithAccess(
   );
 }
 
-export function useNavigationItems() {
+export function useNavigationItems(): UseSideNavigationItemsResult {
   const checkAccess = useAccessControl();
-  const navItems = useResolveSideNavigationItems();
+  const { isLoading, items } = useResolveSideNavigationItems();
 
-  return filterNavigationItemsWithAccess(navItems, checkAccess);
+  return {
+    isLoading,
+    items: filterNavigationItemsWithAccess(items, checkAccess),
+  };
 }
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
index 80535ddb3..c2b98c5ef 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx
@@ -125,15 +125,16 @@ export function ProcedureMetricsDisplay() {
                   manualSorting: false,
                   initialSorting,
                 }}
-                rowNavRoute={(row) =>
-                  taskMetricsEnabled
-                    ? routes.metrics.details(
-                        row.original.businessModule,
-                        row.original.procedureType,
-                      )
-                    : undefined
-                }
-                focusColumnHeader="Typ"
+                rowNavigation={{
+                  route: (row) =>
+                    taskMetricsEnabled
+                      ? routes.metrics.details(
+                          row.original.businessModule,
+                          row.original.procedureType,
+                        )
+                      : undefined,
+                  focusColumnAccessorKey: "procedureType",
+                }}
               />
             </TableSheet>
           </Stack>
diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
index 5fa2a3a3b..987c7bfc5 100644
--- a/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
+++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/taskMetrics/TaskMetricsDisplay.tsx
@@ -142,14 +142,15 @@ function SlowestAndFastestTable({
         sorting={{
           manualSorting: false,
         }}
-        rowNavRoute={(row) =>
-          resolveProcedureDetailsRoute({
-            businessModule: businessModuleName as ApiBusinessModule,
-            procedureId: row.original.id,
-            status: ApiProcedureStatus.Closed,
-          })
-        }
-        focusColumnHeader="Erstellt am"
+        rowNavigation={{
+          route: (row) =>
+            resolveProcedureDetailsRoute({
+              businessModule: businessModuleName as ApiBusinessModule,
+              procedureId: row.original.id,
+              status: ApiProcedureStatus.Closed,
+            }),
+          focusColumnAccessorKey: "createdAt",
+        }}
       />
     </TableSheet>
   );
diff --git a/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx b/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
index a298703cf..a2e1b9da5 100644
--- a/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/ResourceDetail.tsx
@@ -13,30 +13,23 @@ import {
 } from "@eshg/employee-portal-api/base";
 import { Add } from "@mui/icons-material";
 import { Button, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 
 import { LabelList } from "@/lib/baseModule/components/labels/LabelList";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
-import { formatDateToFullReadableStringWithShortenedWeekday } from "@/lib/shared/helpers/dateTime";
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
 
 import { ResourceCalendar, TimeRangeProps } from "./ResourceCalendar";
 import { resourceTypeNames } from "./constants";
 import {
-  AddServiceSidebar,
-  EditServiceSidebar,
+  useAddServiceSidebar,
+  useEditServiceSidebar,
 } from "./sidebar/AddServiceSidebar";
-import { EventsViewSidebar } from "./sidebar/EventsViewSidebar";
-import { UpdateResourceSidebar } from "./sidebar/UpdateResourceSidebar";
+import { useEventsViewSidebar } from "./sidebar/EventsViewSidebar";
+import { useUpdateResourceSidebar } from "./sidebar/UpdateResourceSidebar";
 
 export type UserActivityState =
-  | { type: "view-resource" }
   | { type: "add-service"; start?: string }
   | { type: "edit-data" }
   | {
@@ -46,153 +39,122 @@ export type UserActivityState =
     }
   | { type: "edit-service"; event: ApiDetailedEventWithoutCalendarId };
 
-const initialUserActivity: UserActivityState = { type: "view-resource" };
-
 export function ResourceDetail(props: {
   resource: ApiResource;
   labels: ApiLabel[];
   resourceCalendarEvents: ApiDetailedEventWithoutCalendarId[];
   calendarId: string;
   timeRangeProps: TimeRangeProps;
-  isTodayAvaliable: boolean;
+  isTodayAvailable: boolean;
 }) {
   const hasWritePerms = useHasUserRoleCheck(ApiUserRole.BaseResourcesWrite);
-  const [userActivity, setUserActivity] =
-    useState<UserActivityState>(initialUserActivity);
 
   const labels = props.labels.sort((a, b) => a.name.localeCompare(b.name));
 
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setUserActivity(initialUserActivity),
-  });
+  const updateSidebar = useUpdateResourceSidebar();
+  const addServiceSidebar = useAddServiceSidebar();
+  const editServiceSidebar = useEditServiceSidebar();
+  const eventsViewSidebar = useEventsViewSidebar();
 
-  return (
-    <>
-      <Stack gap={2}>
-        <Stack spacing={1} alignItems={"flex-end"}>
-          <Button
-            variant="solid"
-            startDecorator={<Add />}
-            onClick={() => {
-              setUserActivity({ type: "add-service" });
-            }}
-          >
-            Service eintragen
-          </Button>
-        </Stack>
+  function startActivity(activity: UserActivityState) {
+    switch (activity.type) {
+      case "add-service":
+        addServiceSidebar.open({
+          start: activity.start,
+          resourceId: props.resource.id,
+          calendarId: props.calendarId,
+        });
+        break;
+      case "edit-service":
+        editServiceSidebar.open({
+          event: activity.event,
+          resourceId: props.resource.id,
+          calendarId: props.calendarId,
+        });
+        break;
+      case "edit-data":
+        updateSidebar.open({
+          resource: props.resource,
+          labels: labels,
+        });
+        break;
+      case "view-events":
+        eventsViewSidebar.open({
+          date: activity.date,
+          events: activity.events,
+          setUserActivity: startActivity,
+        });
+        break;
+    }
+  }
 
-        <Stack direction={{ xxs: "column-reverse", md: "row" }} gap={2}>
-          <InformationSheet data-testid="resource-details" sx={{ flex: 2 }}>
-            <Stack
-              direction={"row"}
-              alignItems={"center"}
-              justifyContent={"space-between"}
-            >
-              <Typography component={"h2"} level="h3">
-                Details
-              </Typography>
-              {hasWritePerms && (
-                <EditButton
-                  onClick={() => setUserActivity({ type: "edit-data" })}
-                />
-              )}
-            </Stack>
-            <Stack gap={1}>
-              <DetailsCell
-                name={"name"}
-                label="Name"
-                value={props.resource.name}
-              />
-              <DetailsCell
-                name={"type"}
-                label="Typ"
-                value={resourceTypeNames[props.resource.type]}
-              />
-              <DetailsCell
-                name={"articleNumber"}
-                label={"Artikelnummer"}
-                value={props.resource.articleNumber}
-              />
-              {props.resource.labels.length > 0 && (
-                <DetailsCell
-                  name={"labels"}
-                  label={"Labels"}
-                  value={
-                    <LabelList labels={props.resource.labels} maxVisible={3} />
-                  }
-                />
-              )}
-              <DetailsCell
-                name={"description"}
-                label={"Beschreibung"}
-                value={props.resource.description}
-              />
-            </Stack>
-          </InformationSheet>
-          <ResourceCalendar
-            resourceCalendarEvents={props.resourceCalendarEvents}
-            setUserActivity={setUserActivity}
-            timeRangeProps={props.timeRangeProps}
-            isTodayAvaliable={props.isTodayAvaliable}
-          />
-        </Stack>
+  return (
+    <Stack gap={2}>
+      <Stack spacing={1} alignItems={"flex-end"}>
+        <Button
+          variant="solid"
+          startDecorator={<Add />}
+          onClick={() => startActivity({ type: "add-service" })}
+        >
+          Service eintragen
+        </Button>
       </Stack>
 
-      <OverlayBoundary>
-        <AddServiceSidebar
-          open={userActivity.type === "add-service"}
-          onClose={closeSidebar}
-          start={
-            userActivity.type === "add-service" ? userActivity.start : undefined
-          }
-          resourceId={props.resource.id}
-          calendarId={props.calendarId}
-        />
-
-        {hasWritePerms && (
-          <Sidebar
-            open={userActivity.type === "edit-data"}
-            onClose={handleClose}
+      <Stack direction={{ xxs: "column-reverse", md: "row" }} gap={2}>
+        <InformationSheet data-testid="resource-details" sx={{ flex: 2 }}>
+          <Stack
+            direction={"row"}
+            alignItems={"center"}
+            justifyContent={"space-between"}
           >
-            <UpdateResourceSidebar
-              onClose={handleClose}
-              onSave={closeSidebar}
-              labels={labels}
-              resource={props.resource}
-              sidebarFormRef={sidebarFormRef}
+            <Typography component={"h2"} level="h3">
+              Details
+            </Typography>
+            {hasWritePerms && (
+              <EditButton
+                onClick={() => startActivity({ type: "edit-data" })}
+              />
+            )}
+          </Stack>
+          <Stack gap={1}>
+            <DetailsCell
+              name={"name"}
+              label="Name"
+              value={props.resource.name}
             />
-          </Sidebar>
-        )}
-
-        <Sidebar
-          open={userActivity.type === "view-events"}
-          onClose={closeSidebar}
-        >
-          {userActivity.type === "view-events" && (
-            <SidebarContent
-              title={formatDateToFullReadableStringWithShortenedWeekday(
-                userActivity.date,
-              )}
-            >
-              <EventsViewSidebar
-                events={userActivity.events}
-                setUserActivity={setUserActivity}
+            <DetailsCell
+              name={"type"}
+              label="Typ"
+              value={resourceTypeNames[props.resource.type]}
+            />
+            <DetailsCell
+              name={"articleNumber"}
+              label={"Artikelnummer"}
+              value={props.resource.articleNumber}
+            />
+            {props.resource.labels.length > 0 && (
+              <DetailsCell
+                name={"labels"}
+                label={"Labels"}
+                value={
+                  <LabelList labels={props.resource.labels} maxVisible={3} />
+                }
               />
-            </SidebarContent>
-          )}
-        </Sidebar>
-
-        {userActivity.type === "edit-service" && (
-          <EditServiceSidebar
-            open
-            onClose={closeSidebar}
-            onCloseWithConfirmation={handleClose}
-            resourceId={props.resource.id}
-            calendarId={props.calendarId}
-            event={userActivity.event}
-          />
-        )}
-      </OverlayBoundary>
-    </>
+            )}
+            <DetailsCell
+              name={"description"}
+              label={"Beschreibung"}
+              value={props.resource.description}
+            />
+          </Stack>
+        </InformationSheet>
+        <ResourceCalendar
+          resourceCalendarEvents={props.resourceCalendarEvents}
+          setUserActivity={startActivity}
+          timeRangeProps={props.timeRangeProps}
+          isTodayAvaliable={props.isTodayAvailable}
+        />
+      </Stack>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx b/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
index fceb166d9..3b340640b 100644
--- a/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/ResourcesTable.tsx
@@ -11,13 +11,11 @@ import {
 } from "@eshg/employee-portal-api/base";
 import AddIcon from "@mui/icons-material/Add";
 import { Button, Stack } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetResourcesOverviewQuery } from "@/lib/baseModule/api/queries/resources";
 import { useResourcesFilterSettings } from "@/lib/baseModule/components/resources/hooks/useResourcesFilterSettings";
-import { AddResourceSidebar } from "@/lib/baseModule/components/resources/sidebar/AddResourceSidebar";
+import { useAddResourceSidebar } from "@/lib/baseModule/components/resources/sidebar/AddResourceSidebar";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
 import { FilterSettingsContent } from "@/lib/shared/components/filterSettings/FilterSettingsContent";
 import { FilterSettingsSheet } from "@/lib/shared/components/filterSettings/FilterSettingsSheet";
@@ -47,7 +45,7 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
     isFetching,
   } = useGetResourcesOverviewQuery(params);
 
-  const [sidebarState, setSidebarState] = useState(false);
+  const addResourceSidebar = useAddResourceSidebar();
 
   const filterSettings = useResourcesFilterSettings({
     tableControl: tableControl,
@@ -87,7 +85,11 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
             </Stack>
             {hasWritePerms && (
               <Button
-                onClick={() => setSidebarState(true)}
+                onClick={() =>
+                  addResourceSidebar.open({
+                    labels: labels.elements,
+                  })
+                }
                 startDecorator={<AddIcon />}
               >
                 Ressource hinzufügen
@@ -109,22 +111,14 @@ export function ResourcesTable({ params }: ResourcesTableProps) {
             data={resources.elements}
             columns={resourceTableColumns}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) => routes.resources.details(row.original.id)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.resources.details(row.original.id),
+              focusColumnAccessorKey: "name",
+            }}
             minWidth="60rem"
           />
         </TableSheet>
       </TablePage>
-
-      {hasWritePerms && (
-        <OverlayBoundary>
-          <AddResourceSidebar
-            open={sidebarState}
-            onClose={() => setSidebarState(false)}
-            labels={labels.elements}
-          />
-        </OverlayBoundary>
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
index b454d3c5c..08c9ee4bd 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddResourceSidebar.tsx
@@ -13,8 +13,10 @@ import {
   ResourceFormValues,
 } from "@/lib/baseModule/components/resources/forms/ResourceForm";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 const emptyValues: ResourceFormValues = {
   type: "",
@@ -24,40 +26,36 @@ const emptyValues: ResourceFormValues = {
   labelNames: [],
 };
 
-interface AddResourceSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface AddResourceSidebarProps extends SidebarWithFormRefProps {
   labels: ApiLabel[];
 }
 
-export function AddResourceSidebar(props: AddResourceSidebarProps) {
+export function useAddResourceSidebar() {
+  return useSidebarWithFormRef({
+    component: AddResourceSidebar,
+  });
+}
+
+function AddResourceSidebar(props: AddResourceSidebarProps) {
   const router = useRouter();
   const createResource = useAddResource();
 
-  const { sidebarFormRef, handleClose } = useSidebarForm({
-    onClose: props.onClose,
-  });
-
   async function handleSubmit(values: ResourceFormValues) {
-    await createResource
-      .mutateAsync(mapAddResourceRequest(values), {
-        onSuccess: ({ id }) => router.push(routes.resources.details(id)),
-      })
-      .catch();
+    await createResource.mutateAsync(mapAddResourceRequest(values), {
+      onSuccess: ({ id }) => router.push(routes.resources.details(id)),
+    });
   }
 
   return (
-    <Sidebar open={props.open} onClose={handleClose}>
-      <ResourceForm
-        initialValues={emptyValues}
-        labels={props.labels}
-        formRef={sidebarFormRef}
-        onCancel={handleClose}
-        onSubmit={handleSubmit}
-        title={"Ressource hinzufügen"}
-        submitLabel={"Hinzufügen"}
-        canChooseType
-      />
-    </Sidebar>
+    <ResourceForm
+      initialValues={emptyValues}
+      labels={props.labels}
+      formRef={props.formRef}
+      onCancel={() => props.onClose(false)}
+      onSubmit={handleSubmit}
+      title={"Ressource hinzufügen"}
+      submitLabel={"Hinzufügen"}
+      canChooseType
+    />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
index eef57c59d..106c3e872 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/AddServiceSidebar.tsx
@@ -29,9 +29,12 @@ import {
   handleWholeDayChange,
   validateEndAfterStart,
 } from "@/lib/shared/components/formFields/dateOrDateTimeFieldHelper";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 export interface AddServiceFormValues {
   reason: string;
@@ -109,15 +112,19 @@ export function AddServiceForm({
   );
 }
 
-interface AddServiceSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface AddServiceSidebarProps extends SidebarWithFormRefProps {
   resourceId: string;
   calendarId: string;
   start: string | undefined;
 }
 
-export function AddServiceSidebar(props: AddServiceSidebarProps) {
+export function useAddServiceSidebar() {
+  return useSidebarWithFormRef({
+    component: AddServiceSidebar,
+  });
+}
+
+function AddServiceSidebar(props: AddServiceSidebarProps) {
   const snackbar = useSnackbar();
   const submitCalendarEvent = useSubmitCalendarEvent();
 
@@ -125,56 +132,53 @@ export function AddServiceSidebar(props: AddServiceSidebarProps) {
     values: AddServiceFormValues,
     event: ApiDetailedEventWithoutCalendarId | undefined,
   ) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          eventId: event?.id,
-          request: mapFormToRequestValues(values, props.calendarId),
+    await submitCalendarEvent.mutateAsync(
+      {
+        eventId: event?.id,
+        request: mapFormToRequestValues(values, props.calendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Service erfolgreich eingetragen");
+          props.onClose(true);
         },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Service erfolgreich eingetragen");
-          },
-        },
-      )
-      .then(() => props.onClose())
-      .catch();
+      },
+    );
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onClose}>
-      <AddServiceForm
-        initialValues={
-          isDefined(props.start)
-            ? {
-                start: props.start,
-              }
-            : undefined
-        }
-        onSubmit={async (values) => {
-          await saveService(values, undefined);
-        }}
-      >
-        <SidebarContent title={"Service eintragen"}>
-          <AddServiceFormInputs />
-        </SidebarContent>
-        <SidebarActions>
-          <AddServiceFormActions onCancel={props.onClose} />
-        </SidebarActions>
-      </AddServiceForm>
-    </Sidebar>
+    <AddServiceForm
+      initialValues={
+        isDefined(props.start)
+          ? {
+              start: props.start,
+            }
+          : undefined
+      }
+      onSubmit={async (values) => {
+        await saveService(values, undefined);
+      }}
+    >
+      <SidebarContent title={"Service eintragen"}>
+        <AddServiceFormInputs />
+      </SidebarContent>
+      <SidebarActions>
+        <AddServiceFormActions onCancel={() => props.onClose(false)} />
+      </SidebarActions>
+    </AddServiceForm>
   );
 }
 
-interface EditServiceSidebarProps {
-  open: boolean;
-  onClose: () => void;
-  onCloseWithConfirmation: () => void;
+interface EditServiceSidebarProps extends SidebarWithFormRefProps {
   resourceId: string;
   calendarId: string;
   event: ApiDetailedEventWithoutCalendarId;
 }
 
+export function useEditServiceSidebar() {
+  return useSidebarWithFormRef({ component: EditServiceSidebar });
+}
+
 export function EditServiceSidebar(props: EditServiceSidebarProps) {
   const { openConfirmationDialog } = useConfirmationDialog();
   const snackbar = useSnackbar();
@@ -185,20 +189,18 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
     values: AddServiceFormValues,
     event: ApiDetailedEventWithoutCalendarId,
   ) {
-    await submitCalendarEvent
-      .mutateAsync(
-        {
-          eventId: event?.id,
-          request: mapFormToRequestValues(values, props.calendarId),
-        },
-        {
-          onSuccess: () => {
-            snackbar.confirmation("Service erfolgreich eingetragen");
-          },
+    await submitCalendarEvent.mutateAsync(
+      {
+        eventId: event?.id,
+        request: mapFormToRequestValues(values, props.calendarId),
+      },
+      {
+        onSuccess: () => {
+          snackbar.confirmation("Service erfolgreich eingetragen");
+          props.onClose(true);
         },
-      )
-      .then(() => props.onClose())
-      .catch();
+      },
+    );
   }
 
   function saveServiceWithConfirmation(
@@ -223,8 +225,7 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
           },
         },
       )
-      .then(() => props.onClose())
-      .catch();
+      .then(() => props.onClose());
   }
 
   function deleteServiceWithConfirmation(
@@ -240,28 +241,26 @@ export function EditServiceSidebar(props: EditServiceSidebarProps) {
   }
 
   return (
-    <Sidebar open={props.open} onClose={props.onCloseWithConfirmation}>
-      <AddServiceForm
-        initialValues={mapEventToFormValues(props.event)}
-        onSubmit={(values) => saveServiceWithConfirmation(values, props.event)}
-      >
-        <SidebarContent title={"Service Eintrag bearbeiten"}>
-          <AddServiceFormInputs />
-        </SidebarContent>
-        <SidebarActions>
-          <Stack justifyContent={"space-between"} direction={"row"}>
-            <Button
-              variant="plain"
-              color="danger"
-              startDecorator={<DeleteForever />}
-              onClick={() => deleteServiceWithConfirmation(props.event)}
-            >
-              Löschen
-            </Button>
-            <AddServiceFormActions onCancel={props.onCloseWithConfirmation} />
-          </Stack>
-        </SidebarActions>
-      </AddServiceForm>
-    </Sidebar>
+    <AddServiceForm
+      initialValues={mapEventToFormValues(props.event)}
+      onSubmit={(values) => saveServiceWithConfirmation(values, props.event)}
+    >
+      <SidebarContent title={"Service Eintrag bearbeiten"}>
+        <AddServiceFormInputs />
+      </SidebarContent>
+      <SidebarActions>
+        <Stack justifyContent={"space-between"} direction={"row"}>
+          <Button
+            variant="plain"
+            color="danger"
+            startDecorator={<DeleteForever />}
+            onClick={() => deleteServiceWithConfirmation(props.event)}
+          >
+            Löschen
+          </Button>
+          <AddServiceFormActions onCancel={() => props.onClose(false)} />
+        </Stack>
+      </SidebarActions>
+    </AddServiceForm>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
index a6d796c41..5f03c4894 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/EventsViewSidebar.tsx
@@ -13,63 +13,78 @@ import {
   mapResourceCalendarEventColor,
   mapResourceEventDateInfo,
 } from "@/lib/baseModule/components/resources/resourceCalendarMapper";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import { formatDateToFullReadableStringWithShortenedWeekday } from "@/lib/shared/helpers/dateTime";
 
-export function EventsViewSidebar(props: {
+export function useEventsViewSidebar() {
+  return useSidebar({
+    component: EventsViewSidebar,
+  });
+}
+
+function EventsViewSidebar(props: {
+  date: Date;
   events: ApiDetailedEventWithoutCalendarId[];
   setUserActivity: (activity: UserActivityState) => void;
+  onClose: () => void;
 }) {
   return (
-    <Stack gap={2}>
-      <Divider sx={{ marginBottom: 1 }} />
-      {props.events.map((event) => (
-        <Stack gap={2} key={event.id}>
-          <Stack direction="row" gap={1} alignItems="baseline">
-            <Box
-              width="0.75rem"
-              height="0.75rem"
-              bgcolor={mapResourceCalendarEventColor(event.type)}
-              borderRadius="lg"
-            ></Box>
-            <Stack flex={1}>
-              <Stack
-                direction="row"
-                gap={1}
-                alignItems="center"
-                justifyContent="space-between"
-              >
-                <Typography level="title-md">
-                  {isDefined(event.metaData.subject)
-                    ? event.metaData.subject
-                    : mapEventTypeToFallbackTitle(event.type)}
-                </Typography>
-                {event.type !== "BUSINESS_CASE" && (
-                  <Button
-                    onClick={() =>
-                      props.setUserActivity({
-                        type: "edit-service",
-                        event,
-                      })
-                    }
-                    variant="plain"
-                    sx={{
-                      paddingY: 0,
-                      minHeight: "24px",
-                    }}
-                  >
-                    Bearbeiten
-                  </Button>
-                )}
+    <SidebarContent
+      title={formatDateToFullReadableStringWithShortenedWeekday(props.date)}
+    >
+      <Stack gap={2}>
+        <Divider sx={{ marginBottom: 1 }} />
+        {props.events.map((event) => (
+          <Stack gap={2} key={event.id}>
+            <Stack direction="row" gap={1} alignItems="baseline">
+              <Box
+                width="0.75rem"
+                height="0.75rem"
+                bgcolor={mapResourceCalendarEventColor(event.type)}
+                borderRadius="lg"
+              ></Box>
+              <Stack flex={1}>
+                <Stack
+                  direction="row"
+                  gap={1}
+                  alignItems="center"
+                  justifyContent="space-between"
+                >
+                  <Typography level="title-md">
+                    {isDefined(event.metaData.subject)
+                      ? event.metaData.subject
+                      : mapEventTypeToFallbackTitle(event.type)}
+                  </Typography>
+                  {event.type !== "BUSINESS_CASE" && (
+                    <Button
+                      onClick={() =>
+                        props.setUserActivity({
+                          type: "edit-service",
+                          event,
+                        })
+                      }
+                      variant="plain"
+                      sx={{
+                        paddingY: 0,
+                        minHeight: "24px",
+                      }}
+                    >
+                      Bearbeiten
+                    </Button>
+                  )}
+                </Stack>
+                {mapResourceEventDateInfo(event).map((info) => (
+                  <Typography level="body-md" color="neutral" key={info}>
+                    {info}
+                  </Typography>
+                ))}
               </Stack>
-              {mapResourceEventDateInfo(event).map((info) => (
-                <Typography level="body-md" color="neutral" key={info}>
-                  {info}
-                </Typography>
-              ))}
             </Stack>
+            <Divider sx={{ marginTop: 1 }} />
           </Stack>
-          <Divider sx={{ marginTop: 1 }} />
-        </Stack>
-      ))}
-    </Stack>
+        ))}
+      </Stack>
+    </SidebarContent>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx b/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
index ede57cf97..c2c724d30 100644
--- a/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/resources/sidebar/UpdateResourceSidebar.tsx
@@ -4,7 +4,6 @@
  */
 
 import { ApiLabel, ApiResource } from "@eshg/employee-portal-api/base";
-import { Ref } from "react";
 
 import { mapUpdateResourceRequest } from "@/lib/baseModule/api/mapper/resources";
 import { useUpdateResource } from "@/lib/baseModule/api/mutations/resources";
@@ -13,17 +12,23 @@ import {
   ResourceFormValues,
 } from "@/lib/baseModule/components/resources/forms/ResourceForm";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
-interface UpdateResourceSidebarProps {
-  onClose: () => void;
-  onSave: () => void;
+interface UpdateResourceSidebarProps extends SidebarWithFormRefProps {
   labels: ApiLabel[];
   resource: ApiResource;
-  sidebarFormRef: Ref<SidebarFormHandle>;
 }
 
-export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
+export function useUpdateResourceSidebar() {
+  return useSidebarWithFormRef({
+    component: UpdateResourceSidebar,
+  });
+}
+
+function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
   const saveDataOverview = useUpdateResource(props.resource.id);
   const { openConfirmationDialog } = useConfirmationDialog();
 
@@ -31,7 +36,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
     openConfirmationDialog({
       onConfirm: () => {
         saveDataOverview.mutate(mapUpdateResourceRequest(values), {
-          onSuccess: props.onSave,
+          onSuccess: () => props.onClose(true),
         });
       },
     });
@@ -40,7 +45,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
 
   return (
     <ResourceForm
-      formRef={props.sidebarFormRef}
+      formRef={props.formRef}
       title={"Ressource bearbeiten"}
       submitLabel={"Speichern"}
       labels={props.labels}
@@ -52,7 +57,7 @@ export function UpdateResourceSidebar(props: UpdateResourceSidebarProps) {
         type: props.resource.type,
       }}
       onSubmit={handleSubmit}
-      onCancel={props.onClose}
+      onCancel={() => props.onClose(false)}
     />
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx b/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
index 8026aba41..6d2529e86 100644
--- a/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/task/TasksTable.tsx
@@ -108,12 +108,14 @@ export function TasksTable(
         <DataTable
           data={tasksResponse.tasks}
           columns={tasksColumns}
-          rowNavRoute={(row) =>
-            resolveProcedureDetailsRoute({
-              businessModule: row.original.businessModule,
-              procedureId: row.original.procedureId,
-            })
-          }
+          rowNavigation={{
+            route: (row) =>
+              resolveProcedureDetailsRoute({
+                businessModule: row.original.businessModule,
+                procedureId: row.original.procedureId,
+              }),
+            focusColumnAccessorKey: "taskType",
+          }}
           sorting={tableControl.tableSorting}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
index 00c2feb08..b5ca4abc0 100644
--- a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
+++ b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx
@@ -7,11 +7,10 @@
 
 import {
   ApiBusinessModule,
-  ApiGetTaskByUserResponse,
   ApiTask,
   ApiUser,
 } from "@eshg/employee-portal-api/businessProcedures";
-import { UseQueryOptions, useSuspenseQueries } from "@tanstack/react-query";
+import { useSuspenseQueries } from "@tanstack/react-query";
 import { differenceInDays } from "date-fns";
 import { useState } from "react";
 
@@ -19,6 +18,7 @@ import { useGetUsersByGroupQueryOptions } from "@/lib/baseModule/api/queries/use
 import { teamviewColumns } from "@/lib/baseModule/components/task/teamviewColumns";
 import { useTeamviewFilterSettings } from "@/lib/baseModule/components/task/useTeamviewFilterSettings";
 import { resolveProcedureDetailsRoute } from "@/lib/baseModule/moduleRegister/routeResolver";
+import { useFetchTasksForTeamViewOptions } from "@/lib/businessModules/inspection/api/queries/useFetchTasksForTeamViewOptions";
 import { TeamviewFilters } from "@/lib/shared/api/queries/tasks";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { FilterButton } from "@/lib/shared/components/buttons/FilterButton";
@@ -53,14 +53,6 @@ function memberFilter({ assigneeId }: TeamviewFilters) {
 interface TeamviewPageProps {
   groupName: string;
   businessModule: ApiBusinessModule;
-  useFetchTasksForTeamViewOptions: (
-    teamviewFilters: TeamviewFilters,
-  ) => UseQueryOptions<
-    ApiGetTaskByUserResponse,
-    Error,
-    ApiGetTaskByUserResponse,
-    readonly (string | string[] | Record<string, string>)[]
-  >;
 }
 
 export function Teamview(props: Readonly<TeamviewPageProps>) {
@@ -70,7 +62,7 @@ export function Teamview(props: Readonly<TeamviewPageProps>) {
     useSuspenseQueries({
       queries: [
         useGetUsersByGroupQueryOptions(props.groupName),
-        props.useFetchTasksForTeamViewOptions(filters),
+        useFetchTasksForTeamViewOptions(filters),
       ],
     });
 
@@ -148,14 +140,16 @@ export function Teamview(props: Readonly<TeamviewPageProps>) {
       <TableSheet>
         <DataTable
           data={rows}
-          rowNavRoute={(row) =>
-            row.depth === 0
-              ? undefined
-              : resolveProcedureDetailsRoute({
-                  businessModule: props.businessModule,
-                  procedureId: row.original.procedureId!,
-                })
-          }
+          rowNavigation={{
+            route: (row) =>
+              row.depth === 0
+                ? undefined
+                : resolveProcedureDetailsRoute({
+                    businessModule: props.businessModule,
+                    procedureId: row.original.procedureId!,
+                  }),
+            focusColumnAccessorKey: "dueAtInDays",
+          }}
           columns={teamviewColumns}
           getSubRows={(row) => row.subRows}
         />
diff --git a/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx b/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
index 605527705..0d04209dc 100644
--- a/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/SuggestNewUserSidebar.tsx
@@ -12,25 +12,23 @@ import {
 } from "@eshg/lib-portal/helpers/validators";
 import { Chip, Grid } from "@mui/joy";
 import { Formik } from "formik";
-import { useRef } from "react";
 
 import { useSuggestUser } from "@/lib/baseModule/api/mutations/users";
 import {
   chatUsernameValidator,
   phoneNumberValidator,
 } from "@/lib/baseModule/components/users/validation";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { EmailField } from "@/lib/shared/components/formFields/EmailField";
 import { PhoneNumberField } from "@/lib/shared/components/formFields/PhoneNumberField";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 import { translateUserGroup } from "@/lib/shared/helpers/users";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 function initialInputs() {
   return {
@@ -65,149 +63,129 @@ interface UserAddFormInputs {
   groups: string[];
 }
 
-interface SuggestNewUserFormSidebarProps {
-  open: boolean;
-  onClose: () => void;
+interface SuggestNewUserFormSidebarProps extends SidebarWithFormRefProps {
   availableGroups: string[];
 }
 
-export function SuggestNewUserFormSidebar({
-  open,
+export function useSuggestNewUserSidebar() {
+  return useSidebarWithFormRef({
+    component: SuggestNewUserFormSidebar,
+  });
+}
+
+function SuggestNewUserFormSidebar({
   onClose,
+  formRef,
   availableGroups,
 }: SuggestNewUserFormSidebarProps) {
-  const { openCancelDialog } = useConfirmationDialog();
-  const formRef = useRef<SidebarFormHandle>(null);
-
-  function closeAndReset() {
-    onClose();
-    formRef.current?.resetForm();
-  }
-
   const suggestUser = useSuggestUser();
 
-  function handleClose() {
-    if (formRef.current?.dirty) {
-      openCancelDialog({
-        onConfirm: () => {
-          closeAndReset();
-        },
-      });
-    } else {
-      closeAndReset();
-    }
-  }
-
   async function handleSubmit(values: UserAddFormInputs) {
-    await suggestUser
-      .mutateAsync(
-        {
-          username: values.username,
-          firstName: values.firstName,
-          lastName: values.lastName,
-          email: values.email,
-          phoneNumber: mapOptionalValue(values.phoneNumber),
-          externalChatUsername: mapOptionalValue(values.externalChatUsername),
-          groups: values.groups,
-        },
-        {
-          onSuccess: closeAndReset,
-        },
-      )
-      .catch();
+    await suggestUser.mutateAsync(
+      {
+        username: values.username,
+        firstName: values.firstName,
+        lastName: values.lastName,
+        email: values.email,
+        phoneNumber: mapOptionalValue(values.phoneNumber),
+        externalChatUsername: mapOptionalValue(values.externalChatUsername),
+        groups: values.groups,
+      },
+      {
+        onSuccess: () => onClose(true),
+      },
+    );
   }
 
   return (
-    <Sidebar open={open} onClose={handleClose}>
-      <Formik
-        initialValues={initialInputs()}
-        onSubmit={async (values) => {
-          await handleSubmit(values);
-        }}
-      >
-        {({ isSubmitting }) => (
-          <SidebarForm ref={formRef}>
-            <SidebarContent title={"Benutzer vorschlagen"}>
-              <Grid container spacing={2}>
-                <Grid xxs={12}>
-                  <InputField
-                    name={"username"}
-                    label={"Benutzername"}
-                    placeholder={"Beispielsweise erika.mustermann"}
-                    required={"Bitte einen Benutzernamen angeben"}
-                    validate={validatePipe(
-                      validateLength(3, 200),
-                      validateUsernameCharacters,
-                    )}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <EmailField
-                    name={"email"}
-                    label={"E-Mail"}
-                    required={"Bitte eine E-Mail angeben"}
-                  />
-                </Grid>
-                <Grid xxs={12} sm={6}>
-                  <InputField
-                    name={"firstName"}
-                    label={"Vorname"}
-                    required={"Bitte einen Vornamen angeben"}
-                    validate={validateLength(2, 200)}
-                  />
-                </Grid>
-                <Grid xxs={12} sm={6}>
-                  <InputField
-                    name={"lastName"}
-                    label={"Nachname"}
-                    required={"Bitte einen Nachnamen angeben"}
-                    validate={validateLength(2, 200)}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <PhoneNumberField
-                    name={"phoneNumber"}
-                    label={"Telefonnummer"}
-                    validate={phoneNumberValidator}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <InputField
-                    name={"externalChatUsername"}
-                    label={"Chat Benutzername"}
-                    validate={chatUsernameValidator}
-                  />
-                </Grid>
-                <Grid xxs={12}>
-                  <SelectField
-                    multiple
-                    name={`groups`}
-                    label={`Gruppen`}
-                    options={availableGroups.map((name) => ({
-                      value: name,
-                      label: translateUserGroup(name),
-                    }))}
-                    renderValue={(groups) =>
-                      groups.map((group) => (
-                        <Chip key={group.value} color={"primary"}>
-                          {group.label}
-                        </Chip>
-                      ))
-                    }
-                  />
-                </Grid>
+    <Formik
+      initialValues={initialInputs()}
+      onSubmit={async (values) => {
+        await handleSubmit(values);
+      }}
+    >
+      {({ isSubmitting }) => (
+        <SidebarForm ref={formRef}>
+          <SidebarContent title={"Benutzer vorschlagen"}>
+            <Grid container spacing={2}>
+              <Grid xxs={12}>
+                <InputField
+                  name={"username"}
+                  label={"Benutzername"}
+                  placeholder={"Beispielsweise erika.mustermann"}
+                  required={"Bitte einen Benutzernamen angeben"}
+                  validate={validatePipe(
+                    validateLength(3, 200),
+                    validateUsernameCharacters,
+                  )}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <EmailField
+                  name={"email"}
+                  label={"E-Mail"}
+                  required={"Bitte eine E-Mail angeben"}
+                />
+              </Grid>
+              <Grid xxs={12} sm={6}>
+                <InputField
+                  name={"firstName"}
+                  label={"Vorname"}
+                  required={"Bitte einen Vornamen angeben"}
+                  validate={validateLength(2, 200)}
+                />
+              </Grid>
+              <Grid xxs={12} sm={6}>
+                <InputField
+                  name={"lastName"}
+                  label={"Nachname"}
+                  required={"Bitte einen Nachnamen angeben"}
+                  validate={validateLength(2, 200)}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <PhoneNumberField
+                  name={"phoneNumber"}
+                  label={"Telefonnummer"}
+                  validate={phoneNumberValidator}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <InputField
+                  name={"externalChatUsername"}
+                  label={"Chat Benutzername"}
+                  validate={chatUsernameValidator}
+                />
+              </Grid>
+              <Grid xxs={12}>
+                <SelectField
+                  multiple
+                  name={`groups`}
+                  label={`Gruppen`}
+                  options={availableGroups.map((name) => ({
+                    value: name,
+                    label: translateUserGroup(name),
+                  }))}
+                  renderValue={(groups) =>
+                    groups.map((group) => (
+                      <Chip key={group.value} color={"primary"}>
+                        {group.label}
+                      </Chip>
+                    ))
+                  }
+                />
               </Grid>
-            </SidebarContent>
-            <SidebarActions>
-              <FormButtonBar
-                submitLabel={"Vorschlagen"}
-                submitting={isSubmitting}
-                onCancel={handleClose}
-              />
-            </SidebarActions>
-          </SidebarForm>
-        )}
-      </Formik>
-    </Sidebar>
+            </Grid>
+          </SidebarContent>
+          <SidebarActions>
+            <FormButtonBar
+              submitLabel={"Vorschlagen"}
+              submitting={isSubmitting}
+              onCancel={() => onClose(false)}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx b/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
index bd61d6fd8..b8f802341 100644
--- a/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/UserProfileDetails.tsx
@@ -12,15 +12,13 @@ import {
 } from "@eshg/employee-portal-api/base";
 import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
 import { Sheet, Stack, Typography } from "@mui/joy";
-import { useState } from "react";
 import { isNullish } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import { GroupList } from "@/lib/baseModule/components/users/GroupList";
-import { UserProfileEditSidebar } from "@/lib/baseModule/components/users/UserProfileEditSidebar";
+import { useUserProfileEditSidebar } from "@/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar";
 import { routes } from "@/lib/businessModules/chat/shared/routes";
 import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
@@ -28,7 +26,6 @@ import {
   ExternalLinkDetailsCell,
   emailHref,
 } from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell";
-import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm";
 
 import { UserAvatar } from "./UserAvatar";
 
@@ -42,10 +39,7 @@ export function UserProfileDetails({
   isSelf: boolean;
 }) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
-  const [editSidebar, setEditSidebar] = useState(false);
-  const { sidebarFormRef, closeSidebar, handleClose } = useSidebarForm({
-    onClose: () => setEditSidebar(false),
-  });
+  const updateSidebar = useUserProfileEditSidebar();
 
   return (
     <Sheet
@@ -57,17 +51,6 @@ export function UserProfileDetails({
         flexBasis: "500px",
       }}
     >
-      <OverlayBoundary>
-        <UserProfileEditSidebar
-          open={editSidebar}
-          selfUser={user}
-          sidebarFormRef={sidebarFormRef}
-          selfGroups={groups}
-          onClose={handleClose}
-          onSuccess={closeSidebar}
-        />
-      </OverlayBoundary>
-
       <Stack gap={2} flex={1}>
         <Stack direction={"row"} gap={1} justifyContent={"space-between"}>
           <Typography level={"h3"} component={"h2"} id={"user-profiler-header"}>
@@ -76,7 +59,12 @@ export function UserProfileDetails({
           {isSelf && (
             <EditButton
               aria-label={"Profil bearbeiten"}
-              onClick={() => setEditSidebar(true)}
+              onClick={() =>
+                updateSidebar.open({
+                  selfUser: user,
+                  selfGroups: groups,
+                })
+              }
             />
           )}
         </Stack>
diff --git a/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx b/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx
deleted file mode 100644
index 66284371c..000000000
--- a/employee-portal/src/lib/baseModule/components/users/UserProfileEditSidebar.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { ApiUser, ApiUserGroup } from "@eshg/employee-portal-api/base";
-import { Ref } from "react";
-
-import { UserProfileEditForm } from "@/lib/baseModule/components/users/userSidebar/UserProfileEditForm";
-import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
-import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
-
-interface UserProfileEditSidebarProps {
-  selfUser: ApiUser;
-  selfGroups: ApiUserGroup[];
-  sidebarFormRef: Ref<SidebarFormHandle>;
-  open: boolean;
-  onClose: () => void;
-  onSuccess: () => void;
-}
-
-export function UserProfileEditSidebar({
-  selfUser,
-  selfGroups,
-  sidebarFormRef,
-  open,
-  onClose,
-  onSuccess,
-}: UserProfileEditSidebarProps) {
-  return (
-    <Sidebar open={open} onClose={onClose}>
-      <UserProfileEditForm
-        onCancel={onClose}
-        onSuccess={onSuccess}
-        selfGroups={selfGroups}
-        selfUser={selfUser}
-        sidebarFormRef={sidebarFormRef}
-      />
-    </Sidebar>
-  );
-}
diff --git a/employee-portal/src/lib/baseModule/components/users/UserTable.tsx b/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
index 00fe62d58..1a8363500 100644
--- a/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/UserTable.tsx
@@ -8,14 +8,12 @@
 import AddIcon from "@mui/icons-material/Add";
 import LockIcon from "@mui/icons-material/LockOutlined";
 import { Button, Chip, Sheet, Stack } from "@mui/joy";
-import { useState } from "react";
 
 import { useGetUserOverviewPageQuery } from "@/lib/baseModule/api/queries/users";
-import { SuggestNewUserFormSidebar } from "@/lib/baseModule/components/users/SuggestNewUserSidebar";
+import { useSuggestNewUserSidebar } from "@/lib/baseModule/components/users/SuggestNewUserSidebar";
 import { useUserTableColumns } from "@/lib/baseModule/components/users/columns";
 import { businessModuleLeaderRoles } from "@/lib/baseModule/moduleRegister/moduleUserGroupResolver";
 import { routes } from "@/lib/baseModule/shared/routes";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
 import { DataTable } from "@/lib/shared/components/table/DataTable";
 import { TablePage } from "@/lib/shared/components/table/TablePage";
 import { TableSheet } from "@/lib/shared/components/table/TableSheet";
@@ -27,12 +25,12 @@ export function UserTable() {
     isFetching,
   } = useGetUserOverviewPageQuery();
 
-  const [open, setOpen] = useState(false);
   const isLeader = useHasUserRolesCheck(businessModuleLeaderRoles).some(
     (b) => b,
   );
 
   const userColumns = useUserTableColumns();
+  const suggestUserSidebar = useSuggestNewUserSidebar();
 
   return (
     <>
@@ -68,7 +66,11 @@ export function UserTable() {
             {isLeader && (
               <Button
                 startDecorator={<AddIcon />}
-                onClick={() => setOpen(true)}
+                onClick={() =>
+                  suggestUserSidebar.open({
+                    availableGroups: selfGroups,
+                  })
+                }
                 sx={{
                   minWidth: "fit-content",
                 }}
@@ -84,21 +86,13 @@ export function UserTable() {
             minWidth="75rem"
             columns={userColumns}
             data={users}
-            rowNavRoute={(row) => routes.users.details(row.original.userId)}
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) => routes.users.details(row.original.userId),
+              focusColumnAccessorKey: "lastName",
+            }}
           />
         </TableSheet>
       </TablePage>
-
-      {isLeader && (
-        <OverlayBoundary>
-          <SuggestNewUserFormSidebar
-            open={open}
-            onClose={() => setOpen(false)}
-            availableGroups={selfGroups}
-          />
-        </OverlayBoundary>
-      )}
     </>
   );
 }
diff --git a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
similarity index 81%
rename from employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx
rename to employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
index 3b673254d..329da2cd4 100644
--- a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditForm.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserProfileEditSidebar.tsx
@@ -15,7 +15,6 @@ import {
 } from "@eshg/lib-portal/helpers/form";
 import { Divider, Stack } from "@mui/joy";
 import { Formik } from "formik";
-import { Ref } from "react";
 import { isDefined } from "remeda";
 
 import { useUpdateSelfUser } from "@/lib/baseModule/api/mutations/users";
@@ -28,13 +27,14 @@ import {
 } from "@/lib/baseModule/components/users/validation";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
-import {
-  SidebarForm,
-  SidebarFormHandle,
-} from "@/lib/shared/components/form/SidebarForm";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { PhoneNumberField } from "@/lib/shared/components/formFields/PhoneNumberField";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import {
+  SidebarWithFormRefProps,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
 
 interface UserEditFormInputs {
   email: string;
@@ -42,34 +42,36 @@ interface UserEditFormInputs {
   externalChatUsername: string;
 }
 
-export function UserProfileEditForm({
-  selfUser,
-  selfGroups,
-  onCancel,
-  onSuccess,
-  sidebarFormRef,
-}: {
+interface UserProfileEditSidebarProps extends SidebarWithFormRefProps {
   selfUser: ApiUser;
   selfGroups: ApiUserGroup[];
-  onCancel: () => void;
-  onSuccess: () => void;
-  sidebarFormRef?: Ref<SidebarFormHandle>;
-}) {
+}
+
+export function useUserProfileEditSidebar() {
+  return useSidebarWithFormRef({
+    component: UserProfileEditSidebar,
+  });
+}
+
+function UserProfileEditSidebar({
+  selfUser,
+  selfGroups,
+  formRef,
+  onClose,
+}: UserProfileEditSidebarProps) {
   const showChatUsername = useIsNewFeatureEnabled(ApiBaseFeature.ChatUsername);
   const updateSelfUser = useUpdateSelfUser();
 
   async function handleSubmit(values: UserEditFormInputs) {
-    await updateSelfUser
-      .mutateAsync(
-        {
-          externalChatUsername: mapOptionalValue(values.externalChatUsername),
-          phoneNumber: mapOptionalValue(values.phoneNumber),
-        },
-        {
-          onSuccess,
-        },
-      )
-      .catch();
+    await updateSelfUser.mutateAsync(
+      {
+        externalChatUsername: mapOptionalValue(values.externalChatUsername),
+        phoneNumber: mapOptionalValue(values.phoneNumber),
+      },
+      {
+        onSuccess: () => onClose(true),
+      },
+    );
   }
 
   const initialValues: UserEditFormInputs = {
@@ -87,7 +89,7 @@ export function UserProfileEditForm({
       enableReinitialize
     >
       {({ isSubmitting }) => (
-        <SidebarForm ref={sidebarFormRef}>
+        <SidebarForm ref={formRef}>
           <SidebarContent header={<UserSidebarHeader selfUser={selfUser} />}>
             <Stack gap={2}>
               <Divider />
@@ -132,7 +134,7 @@ export function UserProfileEditForm({
             <MultiFormButtonBar
               submitting={isSubmitting}
               submitLabel={"Speichern"}
-              onCancel={onCancel}
+              onCancel={() => onClose(false)}
             />
           </SidebarActions>
         </SidebarForm>
diff --git a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
index 075792ba0..6d28b6647 100644
--- a/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
+++ b/employee-portal/src/lib/baseModule/components/users/userSidebar/UserSidebarHeader.tsx
@@ -4,13 +4,19 @@
  */
 
 import { ApiUser } from "@eshg/employee-portal-api/base";
-import { DialogTitle, Stack, Typography } from "@mui/joy";
+import { Badge, DialogTitle, Stack, Typography } from "@mui/joy";
 
 import { UserAvatar } from "@/lib/baseModule/components/users/UserAvatar";
+import { useGetSelfUserPresence } from "@/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence";
+import {
+  getPresenseLabel,
+  getStatusColor,
+} from "@/lib/businessModules/chat/shared/utils";
 import { sidebarPadding } from "@/lib/shared/components/sidebar/Sidebar";
 import { fullName } from "@/lib/shared/components/users/userFormatter";
 
 export function UserSidebarHeader({ selfUser }: { selfUser: ApiUser }) {
+  const { userPresence, sharePresence } = useGetSelfUserPresence();
   return (
     <Stack
       direction={"row"}
@@ -20,7 +26,22 @@ export function UserSidebarHeader({ selfUser }: { selfUser: ApiUser }) {
         paddingRight: sidebarPadding,
       }}
     >
-      <UserAvatar user={selfUser} size={"lg"} />
+      <Badge
+        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
+        badgeInset="14%"
+        invisible={!sharePresence}
+        variant="solid"
+        size="md"
+        aria-label={`Benutzer (${getPresenseLabel(userPresence)})`}
+        sx={{
+          "& .MuiBadge-badge": {
+            backgroundColor: getStatusColor(userPresence),
+            boxShadow: "0 0 0 1px",
+          },
+        }}
+      >
+        <UserAvatar user={selfUser} size={"lg"} />
+      </Badge>
       <Stack
         sx={{
           minWidth: 0,
diff --git a/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx b/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
index 51ca9f949..1d4fe98b8 100644
--- a/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
+++ b/employee-portal/src/lib/baseModule/moduleRegister/sideNavigationItemsResolver.tsx
@@ -3,39 +3,57 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useSideNavigationItems as useBaseSideNavigationItems } from "@/lib/baseModule/sideNavigationItems";
 import { useSideNavigationItems as useChatSideNavigationItems } from "@/lib/businessModules/chat/shared/sideNavigationItem";
 import { useSideNavigationItems as useInspectionSideNavigationItems } from "@/lib/businessModules/inspection/shared/sideNavigationItem";
 import { useSideNavigationItems as useMeaslesProtectionSideNavigationItems } from "@/lib/businessModules/measlesProtection/shared/sideNavigationItem";
+import { useSideNavigationItems as useMedicalRegistrySideNavigationItems } from "@/lib/businessModules/medicalRegistry/shared/sideNavigationItem";
 import { useSideNavigationItems as useSchoolEntrySideNavigationItems } from "@/lib/businessModules/schoolEntry/shared/sideNavigationItem";
 import { useSideNavigationItems as useStatisticsSideNavigationItems } from "@/lib/businessModules/statistics/shared/sideNavigationItem";
 import { useSideNavigationItems as useStiProtectionSideNavigationItems } from "@/lib/businessModules/stiProtection/shared/sideNavigationItem";
 import { useSideNavigationItems as useTravelMedicineSideNavigationItems } from "@/lib/businessModules/travelMedicine/shared/sideNavigationItem";
 import { sideNavigationItems as archivingSideNavigationItems } from "@/lib/shared/components/archiving/shared/sideNavigationItem";
 
-export function useResolveSideNavigationItems(): SideNavigationItem[] {
-  const inspectionSideNavigationItems = useInspectionSideNavigationItems();
-  const schoolEntrySideNavigationItems = useSchoolEntrySideNavigationItems();
-  const travelMedicineSideNavigationItems =
-    useTravelMedicineSideNavigationItems();
-  const measlesProtectionSideNavigationItems =
+export function useResolveSideNavigationItems(): UseSideNavigationItemsResult {
+  const inspectionSideNavigation = useInspectionSideNavigationItems();
+  const schoolEntrySideNavigation = useSchoolEntrySideNavigationItems();
+  const travelMedicineSideNavigation = useTravelMedicineSideNavigationItems();
+  const measlesProtectionSideNavigation =
     useMeaslesProtectionSideNavigationItems();
-  const stiProtectionSideNavigationItems =
-    useStiProtectionSideNavigationItems();
-  const statisticsSideNavigationItems = useStatisticsSideNavigationItems();
-  const chatSideNavigationItems = useChatSideNavigationItems();
-  const baseSideNavigationItems = useBaseSideNavigationItems();
+  const stiProtectionSideNavigation = useStiProtectionSideNavigationItems();
+  const medicalRegistrySideNavigationItems =
+    useMedicalRegistrySideNavigationItems();
+  const statisticsSideNavigation = useStatisticsSideNavigationItems();
+  const chatSideNavigation = useChatSideNavigationItems();
+  const baseSideNavigation = useBaseSideNavigationItems();
 
-  const businessModuleSideNavigationItems: SideNavigationItem[] = [
-    ...inspectionSideNavigationItems,
-    ...schoolEntrySideNavigationItems,
-    ...travelMedicineSideNavigationItems,
-    ...measlesProtectionSideNavigationItems,
-    ...stiProtectionSideNavigationItems,
-    ...statisticsSideNavigationItems,
-    ...archivingSideNavigationItems,
-    ...chatSideNavigationItems,
+  const orderedSideNavigationItems: UseSideNavigationItemsResult[] = [
+    baseSideNavigation,
+    inspectionSideNavigation,
+    schoolEntrySideNavigation,
+    travelMedicineSideNavigation,
+    measlesProtectionSideNavigation,
+    stiProtectionSideNavigation,
+    medicalRegistrySideNavigationItems,
+    statisticsSideNavigation,
+    { isLoading: false, items: archivingSideNavigationItems },
+    chatSideNavigation,
   ];
-  return [...baseSideNavigationItems, ...businessModuleSideNavigationItems];
+
+  return {
+    isLoading: orderedSideNavigationItems.some(isLoading),
+    items: orderedSideNavigationItems.map(getItems).flat(),
+  };
+}
+
+function isLoading(result: UseSideNavigationItemsResult): boolean {
+  return result.isLoading;
+}
+
+function getItems(result: UseSideNavigationItemsResult): SideNavigationItem[] {
+  return result.items;
 }
diff --git a/employee-portal/src/lib/baseModule/sideNavigationItems.tsx b/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
index c9ba89056..a43f48932 100644
--- a/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
+++ b/employee-portal/src/lib/baseModule/sideNavigationItems.tsx
@@ -19,7 +19,10 @@ import {
 } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { hasUserRole, noCheck } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./shared/routes";
@@ -106,7 +109,7 @@ const inboxNavigationItem: SideNavigationItem[] = [
   },
 ];
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
   const isGdprEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Gdpr);
   const isOpenDataEnabled = useIsNewFeatureEnabled(ApiBaseFeature.OpenData);
@@ -122,5 +125,5 @@ export function useSideNavigationItems(): SideNavigationItem[] {
     items = items.filter((item) => item.name !== "Open Data");
   }
 
-  return items;
+  return { isLoading: false, items };
 }
diff --git a/employee-portal/src/lib/baseModule/theme/theme.ts b/employee-portal/src/lib/baseModule/theme/theme.ts
index b56068d6b..7c2504538 100644
--- a/employee-portal/src/lib/baseModule/theme/theme.ts
+++ b/employee-portal/src/lib/baseModule/theme/theme.ts
@@ -433,5 +433,19 @@ export const theme = extendTheme({
         }),
       },
     },
+    JoyToggleButtonGroup: {
+      styleOverrides: {
+        root: ({ ownerState }) => {
+          if (ownerState.color === "primary") {
+            return {
+              ".MuiButton-variantOutlined": {
+                color: "var(--joy-palette-primary-600)",
+              },
+            };
+          }
+          return {};
+        },
+      },
+    },
   },
 });
diff --git a/employee-portal/src/lib/businessModules/chat/components/Chat.tsx b/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
index 5a0852134..ee1d16b25 100644
--- a/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/Chat.tsx
@@ -16,13 +16,14 @@ import { RoomsPanel } from "@/lib/businessModules/chat/components/roomsPanel/Roo
 import { BackupSetupView } from "@/lib/businessModules/chat/components/secureBackup/BackupSetupView";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import {
   ChatPanelView,
   ClientState,
 } from "@/lib/businessModules/chat/shared/enums";
 import { useCreateNewChat } from "@/lib/businessModules/chat/shared/hooks/useCreateNewChat";
 import {
-  clearUserIdParam,
+  clearSearchParams,
   getChatUser,
 } from "@/lib/businessModules/chat/shared/utils";
 
@@ -55,7 +56,7 @@ export function Chat() {
       const isUserExist = user.results.length;
 
       if (!isUserExist) {
-        clearUserIdParam();
+        clearSearchParams(chatSearchParamNames.userId);
         return;
       }
       void createNewDirectMessage({ invite: [userIdForChatStart] });
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
index e9a8e01a4..9c0df6f95 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx
@@ -54,7 +54,7 @@ export function ChatAvatar({
   ) : (
     <BadgeAvatar
       size={size}
-      presence={sharePresence ? usersPresence[userId ?? ""] : undefined}
+      presence={sharePresence && userId ? usersPresence[userId] : undefined}
       {...props}
     />
   );
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx
deleted file mode 100644
index 8a7c0e488..000000000
--- a/employee-portal/src/lib/businessModules/chat/components/ChatBubble.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
-import { User } from "matrix-js-sdk/lib/matrix";
-
-import { ReadConfirmations } from "@/lib/businessModules/chat/components/ReadConfirmations";
-import { Message } from "@/lib/businessModules/chat/shared/types";
-
-interface ChatBubbleProps {
-  message: Message;
-  variant: "sent" | "received";
-  loggedInUserId: string;
-  receiptUsers: (User | null)[];
-  getImageUrl: (url?: string) => string | null;
-}
-
-export function ChatBubble({
-  variant,
-  message,
-  loggedInUserId,
-  receiptUsers,
-  getImageUrl,
-}: Readonly<ChatBubbleProps>) {
-  const isSent = variant === "sent";
-  const backgroundColor = message.mentions?.find(
-    (userId) => userId == loggedInUserId,
-  )
-    ? "warning.100"
-    : "background.body";
-
-  return (
-    <Stack direction="column" alignItems="flex-end">
-      <Box sx={{ maxWidth: "100%", minWidth: "auto" }}>
-        <Stack
-          direction="row"
-          justifyContent="space-between"
-          spacing={2}
-          sx={{ mb: 0.25 }}
-        >
-          <Typography level="body-xs">
-            {message.sender?.userId === loggedInUserId
-              ? "You"
-              : message.sender?.displayName}
-          </Typography>
-          <Typography level="body-xs">
-            {formatDateTime(message.timestamp)}
-          </Typography>
-        </Stack>
-        <Box sx={{ position: "relative" }}>
-          <Sheet
-            color={isSent ? "primary" : "neutral"}
-            variant={isSent ? "solid" : "soft"}
-            sx={{
-              p: 1.25,
-              borderRadius: "lg",
-              borderTopRightRadius: isSent ? 0 : "lg",
-              borderTopLeftRadius: isSent ? "lg" : 0,
-              backgroundColor: isSent ? "primary.500" : backgroundColor,
-              wordBreak: "break-word",
-            }}
-          >
-            <Typography
-              level="body-sm"
-              sx={{
-                color: isSent ? "background.body" : "neutral.700",
-                overflowWrap: "break-word",
-              }}
-            >
-              {message.content}
-            </Typography>
-          </Sheet>
-        </Box>
-      </Box>
-      <ReadConfirmations
-        receiptUsers={receiptUsers}
-        getImageUrl={getImageUrl}
-      />
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx
deleted file mode 100644
index 217c978f0..000000000
--- a/employee-portal/src/lib/businessModules/chat/components/ChatHeader.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import NotificationsOffOutlinedIcon from "@mui/icons-material/NotificationsOffOutlined";
-import { Stack, Typography, useTheme } from "@mui/joy";
-
-import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
-import { OnlineStatus } from "@/lib/businessModules/chat/components/OnlineStatus";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
-import { RoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import {
-  getDepartmentNameFromUserId,
-  getMemberAvatarUrl,
-  getRoomAvatarUrl,
-  isDMRoom,
-} from "@/lib/businessModules/chat/shared/utils";
-
-export interface ChatHeaderProps extends Partial<RoomInfo> {
-  userId?: string;
-  username?: string;
-  variant?: "default" | "settings";
-}
-
-export function ChatHeader({
-  communicationType,
-  dmRoomMember,
-  userId,
-  username,
-  room,
-  roomMembers,
-  variant = "default",
-}: Readonly<ChatHeaderProps>) {
-  const theme = useTheme();
-  const { matrixClient } = useChatClientContext();
-  const isSettings = variant === "settings";
-  const name = room?.name ?? username;
-  const avatarUrl = isDMRoom(communicationType)
-    ? getMemberAvatarUrl(matrixClient, dmRoomMember?.member)
-    : getRoomAvatarUrl(matrixClient, room ?? null);
-
-  // TO DO - finish mute feature
-  const muteIndicator = false;
-
-  return (
-    <Stack
-      direction="row"
-      spacing={2}
-      sx={{
-        alignItems: "center",
-        width: "100%",
-        minWidth: 0,
-      }}
-    >
-      <ChatAvatar
-        name={name}
-        communicationType={communicationType}
-        avatarUrl={avatarUrl ?? null}
-        size="lg"
-        userId={dmRoomMember?.member.userId ?? userId}
-        disablePresence={!isSettings}
-      />
-      <Stack sx={{ flex: 1, overflow: "hidden" }}>
-        <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
-          <Typography
-            noWrap
-            level={variant === "settings" ? "title-md" : "h3"}
-            sx={{ minWidth: "5ch" }}
-          >
-            {name}
-          </Typography>
-          {muteIndicator && (
-            <NotificationsOffOutlinedIcon
-              sx={{
-                width: "1.125rem",
-                height: "1.125rem",
-                color: theme.palette.neutral.outlinedDisabledColor,
-              }}
-            />
-          )}
-        </Stack>
-        {variant === "default" && (
-          <Stack direction="row" spacing={2}>
-            {roomMembers?.map((item) => (
-              <OnlineStatus
-                key={item.member.userId}
-                userId={item.member.userId}
-                name={roomMembers.length > 1 ? item.member.name : undefined}
-              />
-            ))}
-          </Stack>
-        )}
-        {variant === "settings" &&
-          (isDMRoom(communicationType) || username) && (
-            <Typography noWrap sx={{ minWidth: "5ch" }}>
-              {
-                getDepartmentNameFromUserId(
-                  dmRoomMember?.member.userId ?? userId,
-                )?.username
-              }
-            </Typography>
-          )}
-      </Stack>
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
index 31e208df8..1f90581f4 100644
--- a/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/ChatIllustrationBackground.tsx
@@ -12,7 +12,7 @@ export function ChatIllustrationBackground() {
     <Box
       sx={{
         width: "100%",
-        height: "100%",
+        height: "calc(100% - 24px)",
         display: "flex",
         justifyContent: "center",
         alignItems: "center",
diff --git a/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx b/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
index 53d143d53..17c24d624 100644
--- a/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/OnlineStatus.tsx
@@ -18,11 +18,7 @@ export function OnlineStatus({ userId, name }: OnlineStatusProps) {
   const presence = usersPresence[userId];
 
   return (
-    <Stack
-      direction="row"
-      spacing={0.5}
-      sx={{ alignItems: "center", overflow: "hidden" }}
-    >
+    <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
       {presence && (
         <Box
           sx={{
@@ -35,15 +31,7 @@ export function OnlineStatus({ userId, name }: OnlineStatusProps) {
           }}
         />
       )}
-      <Typography
-        noWrap
-        sx={{
-          overflow: "hidden",
-          textOverflow: "ellipsis",
-        }}
-      >
-        {name ?? presence}
-      </Typography>
+      <Typography noWrap>{name ?? presence}</Typography>
     </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx b/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
index ddf13db0b..60d8dc66c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/UsersAutocomplete.tsx
@@ -71,7 +71,7 @@ export function UsersAutocomplete({
           },
           backgroundColor: "background.surface",
           borderColor: "primary.300",
-          height: "3.25rem",
+          minHeight: "3.25rem",
         }}
         renderOption={(props, option) => {
           const apiUser = usersList?.find((user) => user.user_id === option);
@@ -137,16 +137,18 @@ export function UsersAutocomplete({
           })
         }
       />
-      {meta.error && (
-        <FormHelperText
-          sx={{
-            color: (theme) => theme.palette.danger[500],
-            marginLeft: 0,
-          }}
-        >
-          {meta.error}
-        </FormHelperText>
-      )}
+      <Box sx={{ minHeight: "1.25rem" }}>
+        {meta.error && (
+          <FormHelperText
+            sx={{
+              color: (theme) => theme.palette.danger[500],
+              marginLeft: 0,
+            }}
+          >
+            {meta.error}
+          </FormHelperText>
+        )}
+      </Box>
     </Box>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
index e0fb0de78..31e7dc001 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatBubble.tsx
@@ -4,10 +4,7 @@
  */
 
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
-import Box from "@mui/joy/Box";
-import Sheet from "@mui/joy/Sheet";
-import Stack from "@mui/joy/Stack";
-import Typography from "@mui/joy/Typography";
+import { Box, Sheet, Stack, Typography } from "@mui/joy";
 import { ReactNode } from "react";
 import { isEmpty } from "remeda";
 
@@ -32,6 +29,7 @@ interface ChatBubbleProps {
   lastReadMessageIndexes: number[];
   index: number;
   mentions: MentionedMember[];
+  removeMessage: (messageId: string) => Promise<void>;
 }
 
 export function ChatBubble({
@@ -156,10 +154,11 @@ function splitMessageWithNames(
   });
 
   memberIndexes.sort((a, b) => a.start - b.start);
-
   memberIndexes.forEach((member, index) => {
-    if (member.start > 0) {
-      contentParts.push(text.slice(0, member.start));
+    const prevMember = memberIndexes[index - 1];
+    const prevTextStart = prevMember?.end ?? 0;
+    if (member.start > 0 && !prevMember) {
+      contentParts.push(text.slice(prevTextStart, member.start));
     }
 
     contentParts.push(
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx
new file mode 100644
index 000000000..92672ef15
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatHeader.tsx
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Stack, Typography } from "@mui/joy";
+
+import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
+import { UserList } from "@/lib/businessModules/chat/components/chatPanel/UserList";
+import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
+import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
+import { isGroupRoom } from "@/lib/businessModules/chat/shared/utils";
+
+interface ChatHeaderProps {
+  avatarUrl: string | null;
+  communicationType?: CommunicationType;
+  roomId: string;
+  roomName?: string;
+  dmRoomMemberUserId?: string;
+  roomMembers: ChatRoomMember[];
+}
+
+export function ChatHeader({
+  avatarUrl,
+  communicationType,
+  roomId,
+  roomMembers,
+  dmRoomMemberUserId,
+  roomName,
+}: Readonly<ChatHeaderProps>) {
+  return (
+    <Stack
+      direction="row"
+      spacing={2}
+      sx={{
+        alignItems: "center",
+        width: "100%",
+        minWidth: 0,
+      }}
+    >
+      <ChatAvatar
+        name={roomName}
+        communicationType={communicationType}
+        avatarUrl={avatarUrl}
+        size="lg"
+        userId={dmRoomMemberUserId}
+        disablePresence={true}
+      />
+      <Stack sx={{ flex: 1, overflow: "hidden" }}>
+        <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
+          <Typography noWrap level="h3" sx={{ minWidth: "5ch" }}>
+            {roomName}
+          </Typography>
+        </Stack>
+        <UserList
+          key={roomId}
+          users={roomMembers}
+          isGroupRoom={isGroupRoom(communicationType)}
+        />
+      </Stack>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
index 421b43407..8b2278c19 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatMessages.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Box, Divider, List, ListItem, Typography, useTheme } from "@mui/joy";
+import { Box, Divider, List, ListItem, Typography } from "@mui/joy";
 import { isSameDay, startOfDay } from "date-fns";
 import { User } from "matrix-js-sdk/lib/matrix";
 import { Fragment, useMemo } from "react";
@@ -19,7 +19,6 @@ import {
   pipe,
 } from "remeda";
 
-import { ChatIllustrationBackground } from "@/lib/businessModules/chat/components/ChatIllustrationBackground";
 import { ChatBubble } from "@/lib/businessModules/chat/components/chatPanel/ChatBubble";
 import { ChatSystemMessage } from "@/lib/businessModules/chat/components/chatPanel/ChatSystemMessages";
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
@@ -65,7 +64,6 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
   } = useChat();
   const { typingUsersList } = useTyping(showTypingNotification);
   const typingUsers = typingUsersList[room.room.roomId];
-  const theme = useTheme();
   const { roomSystemMessages } = useChatSystemMessages();
   const chatAndSystemMessages = useMemo(() => {
     return [...messages, ...roomSystemMessages].sort((a, b) =>
@@ -87,23 +85,18 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
     rootMargin: "400px 0px 0px 0px",
   });
 
-  if (!loggedInUserId) {
-    return <ChatIllustrationBackground />;
+  async function removeMessage(messageId: string) {
+    await matrixClient.redactEvent(room.room.roomId, messageId);
   }
 
   return (
-    <Box
-      sx={{
-        overflowY: "hidden",
-        flex: 1,
-      }}
-    >
+    <Box sx={{ overflowY: "hidden", flex: 1 }}>
       <List
         ref={rootRef}
         sx={{
           display: "flex",
           flexDirection: "column-reverse",
-          height: "100%",
+          height: "calc(100% - 2rem)",
           overflowY: "auto",
         }}
       >
@@ -146,7 +139,7 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
                     message.sender?.userId === loggedInUserId
                       ? "row-reverse"
                       : "row",
-                  paddingX: theme.spacing(3),
+                  paddingX: 3,
                   paddingY: 0,
                   marginBottom: isChatMessage(message) ? 3 : 2,
                 }}
@@ -167,6 +160,7 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
                       [...initialReadIndexes, ...lastReadIndexes] as number[]
                     }
                     index={index}
+                    removeMessage={removeMessage}
                   />
                 )}
               </ListItem>
@@ -188,16 +182,13 @@ export function ChatMessages({ room }: Readonly<ChatMessagesProps>) {
             </Fragment>
           );
         })}
-        {(isLoading || hasNextPage) && (
-          <Box alignItems="center" ref={sentryRef} />
-        )}
+        {(isLoading || hasNextPage) && <Box ref={sentryRef} />}
       </List>
       {!!typingUsers?.length && (
         <Typography
           level="body-sm"
           sx={{
             mx: 2,
-            mb: 1,
             visibility: typingUsers?.length ? "visible" : "hidden",
           }}
         >
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
index 9a5c026e9..a433686fd 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanel.tsx
@@ -127,7 +127,7 @@ export function ChatPanel({
   if (roomId && roomWithCommunicationType) {
     return (
       <>
-        <ChatPanelHeader roomId={roomId} room={roomWithCommunicationType} />
+        <ChatPanelHeader roomId={roomId} />
         <Box
           sx={{
             height: `calc(100% - ${chatColumnHeaderHeight})`,
@@ -135,13 +135,15 @@ export function ChatPanel({
             flexDirection: "column",
           }}
         >
-          <ChatMessages room={roomWithCommunicationType} />
+          <ChatMessages
+            room={roomWithCommunicationType}
+            key={selectedRoom?.roomId}
+          />
           <MessageInput
             handleUserTyping={handleUserTyping}
             selectedRoomId={roomId}
             sendMessage={handleSendMessage}
             roomMembers={roomWithCommunicationType.room.getMembers()}
-            disabled={selectedRoom?.getMyMembership() === "leave"}
           />
         </Box>
       </>
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
index aec549b80..2e5a4a07c 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/ChatPanelHeader.tsx
@@ -20,14 +20,14 @@ import {
 import { useState } from "react";
 
 import { ChatColumnHeaderWrapper } from "@/lib/businessModules/chat/components/ChatColumnHeaderWrapper";
-import { ChatHeader } from "@/lib/businessModules/chat/components/ChatHeader";
 import { LeaveChatConfirmation } from "@/lib/businessModules/chat/components/LeaveChatConfirmation";
+import { ChatHeader } from "@/lib/businessModules/chat/components/chatPanel/ChatHeader";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
-import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { RoomWithCommunicationType } from "@/lib/businessModules/chat/shared/types";
 import {
+  clearSearchParams,
   isDMRoom,
   isGroupRoom,
   leaveRoom,
@@ -35,30 +35,34 @@ import {
 
 export interface ChatPanelHeaderProps {
   roomId: string;
-  room: RoomWithCommunicationType;
 }
 
-export function ChatPanelHeader({
-  roomId,
-  room,
-}: Readonly<ChatPanelHeaderProps>) {
+export function ChatPanelHeader({ roomId }: Readonly<ChatPanelHeaderProps>) {
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const roomInfo = useRoomInfo(roomId);
-  const { clearChatParams } = useChatSearchParams();
   const [isOpen, setIsOpen] = useState(false);
 
+  const {
+    getAvatarUrl,
+    getJoinedAndInvitedMembers,
+    exceptMe,
+    communicationType,
+    dmRoomMember,
+    room,
+  } = roomInfo;
+
   function handleRoomInfoClick() {
     setInfoPanelView(InfoPanelView.RoomInfo, roomId);
   }
 
   function handleLeaveRoomClick() {
     setIsOpen(false);
-    clearChatParams();
+    clearSearchParams(chatSearchParamNames.userId, chatSearchParamNames.roomId);
     closeInfoPanel();
     void leaveRoom(roomInfo.matrixClient, roomId);
   }
 
-  const roomSettingsItem = isDMRoom(room.communicationType) ? (
+  const roomSettingsItem = isDMRoom(roomInfo.communicationType) ? (
     <>
       <ListItemDecorator>
         <PersonOutlinedIcon />
@@ -87,8 +91,12 @@ export function ChatPanelHeader({
           }}
         >
           <ChatHeader
-            communicationType={room.communicationType}
-            room={room.room}
+            avatarUrl={getAvatarUrl()}
+            communicationType={communicationType}
+            roomId={roomId}
+            roomMembers={exceptMe(getJoinedAndInvitedMembers())}
+            dmRoomMemberUserId={dmRoomMember?.member.userId}
+            roomName={room?.name}
           />
           <Stack
             direction="row"
@@ -110,23 +118,24 @@ export function ChatPanelHeader({
                   {roomSettingsItem}
                 </MenuItem>
                 {/*Display settings button only for admin and only if it's group chat*/}
-                {roomInfo.isAdmin && isGroupRoom(room.communicationType) && (
-                  <MenuItem
-                    onClick={() =>
-                      setInfoPanelView(InfoPanelView.AdminSettings, roomId)
-                    }
-                  >
-                    <ListItemDecorator>
-                      <SettingsOutlinedIcon />
-                    </ListItemDecorator>
-                    Einstellungen
-                  </MenuItem>
-                )}
+                {roomInfo.checkIfAdmin() &&
+                  isGroupRoom(roomInfo.communicationType) && (
+                    <MenuItem
+                      onClick={() =>
+                        setInfoPanelView(InfoPanelView.AdminSettings, roomId)
+                      }
+                    >
+                      <ListItemDecorator>
+                        <SettingsOutlinedIcon />
+                      </ListItemDecorator>
+                      Einstellungen
+                    </MenuItem>
+                  )}
                 <MenuItem onClick={() => setIsOpen(true)}>
                   <ListItemDecorator>
                     <LogoutOutlinedIcon />
                   </ListItemDecorator>
-                  {isDMRoom(room.communicationType)
+                  {isDMRoom(roomInfo.communicationType)
                     ? "Verlassen"
                     : "Gruppe verlassen"}
                 </MenuItem>
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
index 04ade5d46..8cdcfb2e0 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/InputComponent.tsx
@@ -16,7 +16,7 @@ import {
 } from "@mui/joy";
 import { useField, useFormikContext } from "formik";
 import { RoomMember } from "matrix-js-sdk/lib/matrix";
-import { ChangeEvent, KeyboardEvent, useRef, useState } from "react";
+import { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from "react";
 import { useDebouncedCallback } from "use-debounce";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
@@ -58,15 +58,17 @@ export function InputComponent({
 
   const debouncedHandleUserTyping = useDebouncedCallback(
     (isTyping: boolean) => handleUserTyping?.(selectedRoomId ?? "", isTyping),
-    500,
-    { leading: true },
+    250,
   );
+  useEffect(() => {
+    resetForm();
+  }, [resetForm, selectedRoomId]);
 
   async function handleInputChange(event: ChangeEvent<HTMLInputElement>) {
     const { value } = event.target || {};
     await helpers.setValue(value);
 
-    // Check if we are currently mentioning someone
+    // // Check if we are currently mentioning someone
     const mentionIndex = value.lastIndexOf("@");
     if (
       mentionIndex !== -1 &&
@@ -85,7 +87,7 @@ export function InputComponent({
       setSelectedUserIndex(undefined);
     }
 
-    void debouncedHandleUserTyping(false);
+    void debouncedHandleUserTyping(true);
   }
   function handleUserModalClose() {
     setFilteredUsers([]);
@@ -110,7 +112,6 @@ export function InputComponent({
       await handleUserSelect(filteredUsers[selectedUserIndex ?? 0]);
       return;
     }
-
     if (event.key === "Enter" && !event.shiftKey) {
       try {
         event.preventDefault();
@@ -120,7 +121,6 @@ export function InputComponent({
         logger.warn("Sending message failed", error);
       }
     }
-
     if (event.key === "ArrowDown" && filteredUsers.length > 0) {
       event.preventDefault();
       setSelectedUserIndex(
@@ -155,9 +155,10 @@ export function InputComponent({
       inputNode.focus();
     }
   }
+  const isDisabled = disabled ?? !!_meta.error;
 
   return (
-    <Box sx={{ p: 2 }}>
+    <Box sx={{ p: 2, pt: 0 }}>
       <ClickAwayListener onClickAway={handleUserModalClose}>
         <Menu
           disablePortal
@@ -219,14 +220,13 @@ export function InputComponent({
         endDecorator={
           <IconButton
             size="sm"
-            color="primary"
+            color={isDisabled ? "neutral" : "primary"}
             type="submit"
-            disabled={disabled}
+            disabled={isDisabled}
           >
             <SendOutlinedIcon />
           </IconButton>
         }
-        disabled={disabled}
       />
     </Box>
   );
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
index f6c299bfd..e0ba17101 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewDirectChat.tsx
@@ -5,6 +5,7 @@
 
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import ChatOutlinedIcon from "@mui/icons-material/ChatOutlined";
 import { Box, Button, Stack, Typography, useTheme } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
 import { isObjectType } from "remeda";
@@ -97,6 +98,7 @@ export function NewDirectChat({
               <Box
                 sx={{
                   padding: 2,
+                  paddingBottom: 0,
                   borderBottom: "1px solid",
                   borderColor: theme.palette.neutral.outlinedBorder,
                 }}
@@ -125,11 +127,32 @@ export function NewDirectChat({
                   multiple={false}
                 />
               </Box>
-
               {isObjectType(existingChat) && existingChat?.room.roomId ? (
                 <ChatMessages room={existingChat} />
               ) : (
-                <ChatIllustrationBackground />
+                <>
+                  <ChatIllustrationBackground />
+                  <Box sx={{ minHeight: "2rem" }}>
+                    {values.invite && !isObjectType(existingChat) && (
+                      <Stack
+                        direction="row"
+                        spacing={1}
+                        alignItems="center"
+                        justifyContent="center"
+                        sx={{ width: "100%" }}
+                      >
+                        <ChatOutlinedIcon sx={{ color: "neutral.500" }} />
+                        <Typography
+                          level="title-sm"
+                          textColor="neutral.500"
+                          fontWeight="500"
+                        >
+                          Senden Sie eine Nachricht, um einen Chat zu starten!
+                        </Typography>
+                      </Stack>
+                    )}
+                  </Box>
+                </>
               )}
               <InputComponent
                 name="message"
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
index ad0c411fe..7647de1fb 100644
--- a/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/NewGroupChat.tsx
@@ -126,7 +126,7 @@ export function NewGroupChat({
               aria-label="Chat name"
               sx={{
                 "--FormLabel-margin": 0,
-                marginTop: 1,
+                marginTop: 0,
                 ".MuiInput-root": {
                   height: "3.25rem",
                 },
diff --git a/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx b/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx
new file mode 100644
index 000000000..ef45507a9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/chat/components/chatPanel/UserList.tsx
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Box, Stack, Typography } from "@mui/joy";
+import { useLayoutEffect, useRef, useState } from "react";
+
+import { OnlineStatus } from "@/lib/businessModules/chat/components/OnlineStatus";
+import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
+
+const SAFE_MARGIN_WIDTH = 90;
+
+export function UserList({
+  users,
+  isGroupRoom,
+}: {
+  users: ChatRoomMember[];
+  isGroupRoom: boolean;
+}) {
+  const containerRef = useRef<HTMLDivElement | null>(null);
+  const [visibleCount, setVisibleCount] = useState(users.length);
+  const [hiddenCount, setHiddenCount] = useState(0);
+
+  useLayoutEffect(() => {
+    const container = containerRef.current;
+    if (!container) return;
+
+    const resizeObserver = new ResizeObserver((entries) => {
+      for (const entry of entries) {
+        const containerWidth = entry.contentRect.width;
+
+        let accumulatedWidth = 0;
+        let newVisibleCount = users.length;
+
+        for (let i = 0; i < users.length; i++) {
+          accumulatedWidth += container.children[i]?.clientWidth ?? 0;
+          if (accumulatedWidth > containerWidth - SAFE_MARGIN_WIDTH) {
+            newVisibleCount = i;
+            break;
+          }
+        }
+        setVisibleCount(newVisibleCount);
+        setHiddenCount(users.length - newVisibleCount);
+      }
+    });
+
+    if (container && users.length > 1) {
+      resizeObserver.observe(container);
+    }
+
+    return () => {
+      if (container) {
+        resizeObserver.unobserve(container);
+      }
+    };
+  }, [users]);
+
+  return (
+    <Box sx={{ position: "relative" }}>
+      <Stack direction="row" spacing={2}>
+        {users.slice(0, visibleCount).map((item, index) => (
+          <OnlineStatus
+            key={item.member.userId + index.toString()}
+            userId={item.member.userId}
+            name={isGroupRoom ? item.member.name : undefined}
+          />
+        ))}
+        {!!hiddenCount && (
+          <Typography noWrap>+{hiddenCount} weitere</Typography>
+        )}
+      </Stack>
+
+      {/* Use a hidden list to calculate the total width
+      without changing the original user count */}
+
+      <Stack
+        ref={containerRef}
+        direction="row"
+        spacing={2}
+        sx={{ position: "absolute", visibility: "hidden", inset: 0 }}
+      >
+        {users.map((item, index) => (
+          <OnlineStatus
+            key={item.member.userId + index.toString()}
+            userId={item.member.userId}
+            name={isGroupRoom ? item.member.name : undefined}
+          />
+        ))}
+      </Stack>
+    </Box>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
index da227fd55..95be74753 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AddChatMember.tsx
@@ -7,19 +7,25 @@ import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
-import { useEffect, useState } from "react";
-import { isEmpty } from "remeda";
+import { useCallback, useEffect, useState } from "react";
+import {
+  filter,
+  isEmpty,
+  isNonNullish,
+  isStrictEqual,
+  map,
+  pipe,
+} from "remeda";
 
 import { UsersAutocomplete } from "@/lib/businessModules/chat/components/UsersAutocomplete";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
+import { logger } from "@/lib/businessModules/chat/shared/helpers";
+import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
+import { UserToInvite } from "@/lib/businessModules/chat/shared/types";
 import {
   getChatUserDirectory,
   getDepartmentNameFromUserId,
-} from "@/lib/businessModules/chat/shared//utils";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
-import { logger } from "@/lib/businessModules/chat/shared/helpers";
-import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { ApiUser } from "@/lib/businessModules/chat/shared/types";
+} from "@/lib/businessModules/chat/shared/utils";
 
 export interface AddChatMemberProps {
   roomId: string;
@@ -33,46 +39,53 @@ export function AddChatMember({
   onCancel,
 }: Readonly<AddChatMemberProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const [userList, setUserList] = useState<
-    (ApiUser & { department?: string })[]
-  >([]);
-  const { matrixClient } = useChatClientContext();
-  const loggedInUserId = matrixClient.getUserId();
+  const { matrixClient, getJoinedAndInvitedMembers } = roomInfo;
   const snackbar = useSnackbar();
 
+  const [userList, setUserList] = useState<UserToInvite[]>([]);
+
+  const getMembersToInvite = useCallback(async (): Promise<UserToInvite[]> => {
+    const data = await getChatUserDirectory(matrixClient);
+    const loggedInUserId = matrixClient.getUserId();
+
+    if (data.results.length) {
+      const roomMembers = getJoinedAndInvitedMembers();
+
+      const usersToInvite = pipe(
+        data.results,
+        filter((user) => {
+          const isLoggedInUser =
+            isStrictEqual(user.user_id, loggedInUserId) &&
+            isNonNullish(user.display_name);
+
+          const isDuplicated = roomMembers?.some((i) =>
+            isStrictEqual(i.member.userId, user.user_id),
+          );
+
+          return !isLoggedInUser && !isDuplicated;
+        }),
+        map((user) => ({
+          ...user,
+          department:
+            getDepartmentNameFromUserId(loggedInUserId)?.organisationName,
+        })),
+      );
+
+      return usersToInvite;
+    }
+
+    return [];
+  }, [getJoinedAndInvitedMembers, matrixClient]);
+
   useEffect(() => {
-    void (async () => {
-      const data = await getChatUserDirectory(matrixClient);
-      if (data.results.length) {
-        const users = data.results.filter(
-          (user) =>
-            !!user && user.user_id !== loggedInUserId && !!user.display_name,
-        );
-        // Filter chat room members
-        const room = matrixClient.getRoom(roomId);
-        const roomMembers = room?.getMembers();
-        const usersToInvite = users.filter(
-          (apiUser) =>
-            !roomMembers?.some(
-              (roomMember) => roomMember.userId === apiUser.user_id,
-            ),
-        );
-        const usersWithDepartment = await Promise.all(
-          usersToInvite.map(async (user) => {
-            const userInfo = await matrixClient.whoami();
-            const department =
-              getDepartmentNameFromUserId(userInfo.user_id)?.organisationName ??
-              "";
-            return {
-              ...user,
-              department,
-            };
-          }),
-        );
-        setUserList(usersWithDepartment);
-      }
-    })();
-  }, [loggedInUserId, matrixClient, roomId]);
+    getMembersToInvite()
+      .then((res) => {
+        setUserList(res);
+      })
+      .catch((error) => {
+        logger.error("Fetching users to invite to the chat failed", error);
+      });
+  }, [getMembersToInvite]);
 
   async function handleAddRoomMember(values: { users?: string[] }) {
     if (!values?.users || isEmpty(values.users)) return;
@@ -106,7 +119,7 @@ export function AddChatMember({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
@@ -128,7 +141,7 @@ export function AddChatMember({
               usersList={userList}
               multiple={true}
             />
-            <Stack direction="row" spacing={2} marginTop={2}>
+            <Stack direction="row" spacing={2} marginTop={1}>
               <Button type="button" fullWidth variant="soft" onClick={onCancel}>
                 Abbrechen
               </Button>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
index 2df9345e7..98886a4f1 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AdminSettings.tsx
@@ -12,10 +12,13 @@ import { useState } from "react";
 
 import { LeaveChatConfirmation } from "@/lib/businessModules/chat/components/LeaveChatConfirmation";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { leaveRoom } from "@/lib/businessModules/chat/shared//utils";
+import {
+  clearSearchParams,
+  leaveRoom,
+} from "@/lib/businessModules/chat/shared//utils";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
-import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
 
 export interface AdminSettingsProps {
@@ -30,18 +33,17 @@ export function AdminSettings({
   const roomInfo = useRoomInfo(roomId);
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const [leaveModalOpen, setLeaveModalOpen] = useState(false);
-  const { clearChatParams } = useChatSearchParams();
 
   function handleLeaveRoomClick() {
     setLeaveModalOpen(false);
-    clearChatParams();
+    clearSearchParams(chatSearchParamNames.userId, chatSearchParamNames.roomId);
     closeInfoPanel();
     void leaveRoom(roomInfo.matrixClient, roomId);
   }
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Stack gap={2} sx={{ overflowY: "auto", padding: 2, marginTop: 2 }}>
         <ButtonLink
           level="title-md"
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
index 5db21f5f0..1dc0201c5 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/AssignAdminView.tsx
@@ -7,16 +7,16 @@ import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { Box, Button, Stack, Switch, Typography } from "@mui/joy";
 import { Formik, FormikHelpers } from "formik";
-import { EventType } from "matrix-js-sdk/lib/matrix";
-import { useEffect, useState } from "react";
-import { filter } from "remeda";
+import { filter, mapToObj } from "remeda";
 
 import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import { reassignAdminRole } from "@/lib/businessModules/chat/shared/utils";
+import {
+  getRoomAdmins,
+  reassignAdminRole,
+} from "@/lib/businessModules/chat/shared/utils";
 import { SwitchField } from "@/lib/shared/components/formFields/SwitchField";
 
 type AdminFormValues = Record<string, boolean>;
@@ -32,80 +32,51 @@ export function AssignAdminView({
   onCancel,
 }: Readonly<AssignAdminProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { joinedMembers } = roomInfo;
-  const { matrixClient } = useChatClientContext();
-  const roomMembers = matrixClient
-    .getRoom(roomId)
-    ?.getMembers()
-    .filter((roomMember) => roomMember.membership === "join");
-  const roomMembersMap = roomMembers?.map((item) => ({ [item.userId]: false }));
-  const initialFormValues =
-    roomMembersMap?.reduce<AdminFormValues>((acc, value) => {
-      return { ...acc, ...value };
-    }, {}) ?? {};
-  const [initialValues, setInitialValues] =
-    useState<AdminFormValues>(initialFormValues);
   const snackbar = useSnackbar();
-  function isUserTheOnlyAdmin(
-    userId: string,
-    adminFormValues: Record<string, boolean>,
-  ): boolean {
-    const admins = Object.keys(adminFormValues).filter(
-      (id) => adminFormValues[id],
-    );
 
-    return admins.length === 1 && admins[0] === userId;
-  }
+  const { matrixClient, getJoinedMembers, checkIfAdmin, room } = roomInfo;
+
+  const loggedInUserId = matrixClient.getUserId();
+  const isAdmin = checkIfAdmin();
+  const roomMembers = getJoinedMembers();
+  const roomAdmins = getRoomAdmins(room);
+
+  const initialValues = mapToObj(roomMembers, (i) => [
+    i.member.userId,
+    roomAdmins.includes(i.member.userId),
+  ]);
 
   const sortedMembers = [
-    ...filter(joinedMembers, (x) => x.isRoomCreator),
-    ...filter(joinedMembers, (x) => !x.isRoomCreator),
+    ...filter(roomMembers, (x) => x.isRoomCreator),
+    ...filter(roomMembers, (x) => !x.isRoomCreator),
   ];
 
-  useEffect(() => {
-    void (async () => {
-      const room = matrixClient.getRoom(roomId);
-      if (!room) return;
-      const roomMembers = room
-        .getMembers()
-        .filter((roomMember) => roomMember.membership === "join");
-      const data = await matrixClient.getStateEvent(
-        roomId,
-        EventType.RoomPowerLevels,
-        "",
-      );
-      const powerLevels = ("users" in data ? data.users : data) as Record<
-        string,
-        number
-      >;
-      const values = roomMembers.reduce<AdminFormValues>((acc, user) => {
-        const userPowerLevel = Number(powerLevels?.[user.userId] ?? 0);
-        const isAdmin = userPowerLevel === 100;
-        return { ...acc, [user.userId]: isAdmin };
-      }, {});
-      setInitialValues(values);
-    })();
-  }, [matrixClient, roomId]);
-
   async function handleSubmit(
     values: AdminFormValues,
     formikHelpers: FormikHelpers<AdminFormValues>,
   ) {
     try {
-      const loggedInUserId = matrixClient.getUserId() ?? "";
       if (
-        initialValues[loggedInUserId] === true &&
-        isUserTheOnlyAdmin(loggedInUserId, initialValues) &&
+        isAdmin &&
+        roomAdmins.length === 1 &&
+        loggedInUserId &&
         values[loggedInUserId] === false
       ) {
         throw new Error(
           "You can't change settings, because you are the only admin in the chat room",
         );
       }
+
+      if (!room) {
+        throw new Error(
+          "Room is not available. Reassigning admin roles failed",
+        );
+      }
+
       const users = Object.fromEntries(
         Object.entries(values).map(([key, value]) => [key, value ? 100 : 0]),
       );
-      await reassignAdminRole({ matrixClient, roomId, users });
+      await reassignAdminRole(matrixClient, room, users);
       onCancel();
     } catch (error) {
       logger.error("Die Berechtigungen konnten nicht geändert werden", error);
@@ -116,13 +87,14 @@ export function AssignAdminView({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
-      <Box sx={{ overflowY: "auto" }}>
+      <InfoPanelHeader close={onClose} {...roomInfo} />
+      <Box sx={{ overflowY: "auto", flex: 1 }}>
         <Stack
           spacing={2}
           sx={{
             padding: 3,
             overflowY: "auto",
+            height: "100%",
           }}
         >
           <Typography level="title-lg">Admins bestimmen</Typography>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
index c965634d2..5d138bf76 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/InfoPanelHeader.tsx
@@ -4,20 +4,42 @@
  */
 
 import CloseIcon from "@mui/icons-material/Close";
-import { IconButton, Stack } from "@mui/joy";
+import { IconButton, Stack, Typography } from "@mui/joy";
 
+import { ChatAvatar } from "@/lib/businessModules/chat/components/ChatAvatar";
 import { ChatColumnHeaderWrapper } from "@/lib/businessModules/chat/components/ChatColumnHeaderWrapper";
-import {
-  ChatHeader,
-  ChatHeaderProps,
-} from "@/lib/businessModules/chat/components/ChatHeader";
+import { RoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
+import { getDepartmentNameFromUserId } from "@/lib/businessModules/chat/shared/utils";
 
-interface InfoPanelHeaderProps {
-  data: ChatHeaderProps;
+interface InfoPanelHeaderProps extends Partial<RoomInfo> {
+  userId?: string;
+  displayName?: string;
+  avatarUrl?: string;
   close: () => void;
+  type?: "roomInfo" | "memberInfo";
 }
 
-export function InfoPanelHeader({ data, close }: InfoPanelHeaderProps) {
+export function InfoPanelHeader({
+  room,
+  userId,
+  displayName,
+  communicationType,
+  dmRoomMember,
+  avatarUrl,
+  getAvatarUrl,
+  close,
+  type = "roomInfo",
+}: InfoPanelHeaderProps) {
+  const name = room?.name ?? displayName;
+  const currentUserId =
+    type === "memberInfo" ? userId : dmRoomMember?.member.userId;
+
+  function getAvatar() {
+    if (type === "memberInfo" && avatarUrl) return avatarUrl;
+    if (type === "roomInfo" && getAvatarUrl) return getAvatarUrl();
+    return null;
+  }
+
   return (
     <ChatColumnHeaderWrapper>
       <Stack
@@ -27,7 +49,34 @@ export function InfoPanelHeader({ data, close }: InfoPanelHeaderProps) {
           alignItems: "center",
         }}
       >
-        <ChatHeader {...data} variant="settings" />
+        <Stack
+          direction="row"
+          spacing={2}
+          sx={{
+            alignItems: "center",
+            width: "100%",
+            minWidth: 0,
+          }}
+        >
+          <ChatAvatar
+            name={name}
+            communicationType={communicationType}
+            avatarUrl={getAvatar()}
+            size="lg"
+            userId={currentUserId}
+          />
+          <Stack sx={{ flex: 1, overflow: "hidden" }}>
+            <Stack direction="row" spacing={0.5} sx={{ alignItems: "center" }}>
+              <Typography noWrap level="title-md" sx={{ minWidth: "5ch" }}>
+                {name}
+              </Typography>
+            </Stack>
+
+            <Typography noWrap sx={{ minWidth: "5ch" }}>
+              {getDepartmentNameFromUserId(currentUserId)?.username}
+            </Typography>
+          </Stack>
+        </Stack>
         <IconButton
           variant="outlined"
           aria-label="close sidebar"
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
index dbb9071d4..d4836d85e 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/MemberInfoView.tsx
@@ -53,12 +53,11 @@ export function MemberInfoView({ userId, onClose }: MemberInfoViewProps) {
   return (
     <>
       <InfoPanelHeader
-        data={{
-          avatarUrl: user?.avatar_url,
-          userId,
-          username: user?.display_name,
-        }}
+        avatarUrl={user?.avatar_url}
+        userId={userId}
+        displayName={user?.display_name}
         close={onClose}
+        type="memberInfo"
       />
       <Box
         sx={{
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
index 58d9f52ec..2f2aeb9fb 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RenameChat.tsx
@@ -10,7 +10,6 @@ import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik, FormikErrors } from "formik";
 
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
 
@@ -26,7 +25,7 @@ export function RenameChat({
   onCancel,
 }: Readonly<RenameChatProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { matrixClient } = useChatClientContext();
+  const { matrixClient } = roomInfo;
   const snackbar = useSnackbar();
 
   async function handleRenameChat(values: { name: string }) {
@@ -51,7 +50,7 @@ export function RenameChat({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
index 046896c7d..dcd750454 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomAvatar.tsx
@@ -14,14 +14,8 @@ import { EventType } from "matrix-js-sdk/lib/matrix";
 import { useState } from "react";
 
 import { InfoPanelHeader } from "@/lib/businessModules/chat/components/infoPanel/InfoPanelHeader";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useRoomInfo } from "@/lib/businessModules/chat/shared/hooks/useRoomInfo";
-import {
-  getMemberAvatarUrl,
-  getRoomAvatarUrl,
-  isDMRoom,
-} from "@/lib/businessModules/chat/shared/utils";
 import { FileField } from "@/lib/shared/components/formFields/file/FileField";
 import { FileType } from "@/lib/shared/components/formFields/file/FileType";
 import { FileLike } from "@/lib/shared/components/formFields/file/validators";
@@ -38,12 +32,10 @@ export function RoomAvatar({
   onCancel,
 }: Readonly<RoomAvatarProps>) {
   const roomInfo = useRoomInfo(roomId);
-  const { matrixClient } = useChatClientContext();
+  const { matrixClient, getAvatarUrl } = roomInfo;
   const snackbar = useSnackbar();
-  const initialAvatar = isDMRoom(roomInfo.communicationType)
-    ? getMemberAvatarUrl(matrixClient, roomInfo.dmRoomMember?.member)
-    : getRoomAvatarUrl(matrixClient, roomInfo.room);
-  const [preview, setPreview] = useState<string | null>(initialAvatar);
+
+  const [preview, setPreview] = useState(getAvatarUrl());
 
   async function handleSubmit(values: { avatar: File | undefined }) {
     try {
@@ -72,7 +64,7 @@ export function RoomAvatar({
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Stack sx={{ p: 3 }}>
         <Typography level="title-lg">Profilbild ändern</Typography>
         <Formik<{ avatar: File | undefined }>
diff --git a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
index fefff2912..c537423ed 100644
--- a/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
+++ b/employee-portal/src/lib/businessModules/chat/components/infoPanel/RoomInfoView.tsx
@@ -20,7 +20,6 @@ import {
   isGroupRoom,
   leaveRoom,
 } from "@/lib/businessModules/chat/shared//utils";
-import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { useInfoPanelContext } from "@/lib/businessModules/chat/shared/InfoPanelProvider";
 import { InfoPanelView } from "@/lib/businessModules/chat/shared/enums";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
@@ -39,13 +38,16 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
   const { closeInfoPanel, setInfoPanelView } = useInfoPanelContext();
   const [leaveDialogOpen, setLeaveDialogOpen] = useState(false);
   const [kickUserId, setKickUserId] = useState<string>();
-  const { matrixClient } = useChatClientContext();
   const snackbar = useSnackbar();
 
-  if (!roomInfo) return null;
-
-  const { room, communicationType, allRoomMembers, dmRoomMember, isAdmin } =
-    roomInfo;
+  const {
+    room,
+    communicationType,
+    allRoomMembers,
+    dmRoomMember,
+    checkIfAdmin,
+    matrixClient,
+  } = roomInfo;
 
   const joinedMembers = [
     ...filter(allRoomMembers, (x) => x.isRoomCreator),
@@ -57,12 +59,13 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
   const invitedMembers = [
     ...filter(allRoomMembers, (x) => x.member.membership === "invite"),
   ];
+  const isAdmin = checkIfAdmin();
 
   function handleLeaveRoomClick() {
     setLeaveDialogOpen(false);
     clearChatParams();
     closeInfoPanel();
-    void leaveRoom(roomInfo.matrixClient, room?.roomId);
+    void leaveRoom(matrixClient, room?.roomId);
   }
   async function handleRemoveUser() {
     if (!kickUserId) return;
@@ -80,7 +83,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
 
   return (
     <>
-      <InfoPanelHeader data={roomInfo} close={onClose} />
+      <InfoPanelHeader close={onClose} {...roomInfo} />
       <Box
         sx={{
           overflowY: "auto",
@@ -168,7 +171,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
             gap: 2,
           }}
         >
-          {!isDMRoom(roomInfo.communicationType) && (
+          {!isDMRoom(communicationType) && (
             <ButtonLink
               level="title-md"
               startDecorator={<PersonAddAltIcon />}
@@ -179,7 +182,7 @@ export function RoomInfoView({ roomId, onClose }: Readonly<RoomInfoViewProps>) {
               Mitglieder hinzufügen
             </ButtonLink>
           )}
-          {!isDMRoom(roomInfo.communicationType) && (
+          {!isDMRoom(communicationType) && (
             <ButtonLink
               level="title-md"
               startDecorator={<AdminPanelSettingsOutlinedIcon />}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
index c622e5eba..ec83e65d7 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatLifecycle.tsx
@@ -30,11 +30,12 @@ import { chatLogin } from "@/lib/businessModules/chat/matrix/login";
 import { restoreKeyBackupWithCache } from "@/lib/businessModules/chat/matrix/secretStorage";
 import { clearCachedCredentials } from "@/lib/businessModules/chat/matrix/tokens";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
+import { chatSearchParamNames } from "@/lib/businessModules/chat/shared/constants";
 import { ClientState } from "@/lib/businessModules/chat/shared/enums";
 import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { IStoredCredentials } from "@/lib/businessModules/chat/shared/types";
 import {
-  clearLoginToken,
+  clearSearchParams,
   delayed,
   validateChatUsername,
 } from "@/lib/businessModules/chat/shared/utils";
@@ -88,7 +89,7 @@ export function useChatLifecycle(
       logger.error("Error logging into matrix chat:", error);
       setClientState(ClientState.Error);
     }
-    void clearLoginToken();
+    void clearSearchParams(chatSearchParamNames.loginToken);
   }, [baseUrl, selfUser, setClientState]);
 
   /**
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
index 15e117e3a..2173e75e3 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useChatRoomList.tsx
@@ -158,12 +158,10 @@ export function useChatRoomList() {
     }
 
     function onRoomAvatar(event: MatrixEvent, room: Room) {
-      const updatedRoomWithCommunicationType =
-        getRoomNameAndCommunicationType(room);
       setRoomList((prevState) =>
         prevState.map((prevRoom) =>
-          prevRoom.room.roomId === updatedRoomWithCommunicationType.room.roomId
-            ? updatedRoomWithCommunicationType
+          prevRoom.room.roomId === room.roomId
+            ? { ...prevRoom, room }
             : prevRoom,
         ),
       );
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
index 1e84b31e8..aaa22197a 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useGetSelfUserPresence.tsx
@@ -14,14 +14,12 @@ export function useGetSelfUserPresence() {
   const chatContext = useContext(ChatClientContext);
 
   const isChatEnabled =
-    canAccessChat &&
-    userSettings.chatUsageEnabled &&
-    chatContext?.matrixClient &&
-    chatContext.usersPresence;
+    canAccessChat && userSettings.chatUsageEnabled && chatContext?.matrixClient;
 
   return useMemo(() => {
     let userId: string | null = null;
     let userPresence: Presence | undefined = undefined;
+    const sharePresence = userSettings.sharePresence;
 
     if (isChatEnabled) {
       userId = chatContext.matrixClient.getUserId();
@@ -33,6 +31,7 @@ export function useGetSelfUserPresence() {
     return {
       userId,
       userPresence,
+      sharePresence: sharePresence && isChatEnabled,
     };
   }, [
     chatContext?.matrixClient,
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
index 0c6f60d45..4552119e7 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useNewMessages.tsx
@@ -61,6 +61,7 @@ export function useNewMessages() {
                 mentions: messageContent["m.mentions"]?.user_ids,
                 messageType: MessageTypeEnum.ChatMessage,
                 sent: true,
+                removed: false,
               };
 
               if (sender?.userId !== currentMatrixClient.getUserId()) {
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
index b70a866f2..f273c2bc0 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomInfo.ts
@@ -3,17 +3,18 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Direction, EventType, MatrixClient, Room } from "matrix-js-sdk";
-import { useEffect, useMemo, useState } from "react";
-import { isStrictEqual } from "remeda";
+import { MatrixClient, Room } from "matrix-js-sdk";
+import { useCallback, useMemo } from "react";
+import { filter, find, isStrictEqual } from "remeda";
 
 import { useChatClientContext } from "@/lib/businessModules/chat/shared/ChatClientProvider";
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
-import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { ChatRoomMember } from "@/lib/businessModules/chat/shared/types";
 import {
   getMemberAvatarUrl,
+  getRoomAdmins,
   getRoomAvatarUrl,
+  getRoomCreator,
   getRoomNameAndCommunicationType,
   isDMRoom,
 } from "@/lib/businessModules/chat/shared/utils";
@@ -22,72 +23,70 @@ export interface RoomInfo {
   room: Room | null;
   roomCreator?: string;
   communicationType?: CommunicationType;
-  avatarUrl: string | null;
   allRoomMembers: ChatRoomMember[];
-  joinedMembers: ChatRoomMember[];
-  roomMembers: ChatRoomMember[];
   dmRoomMember: ChatRoomMember | undefined;
-  isAdmin: boolean;
   matrixClient: MatrixClient;
+  checkIfAdmin: () => boolean;
+  getAvatarUrl: () => string | null;
+  getJoinedMembers: () => ChatRoomMember[];
+  getJoinedAndInvitedMembers: () => ChatRoomMember[];
+  exceptMe: (roomMembers: ChatRoomMember[]) => ChatRoomMember[];
 }
 
 export function useRoomInfo(roomId: string): RoomInfo {
   const { matrixClient } = useChatClientContext();
-  const [isAdmin, setIsAdmin] = useState(false);
-
-  useEffect(() => {
-    void (async () => {
-      const userId = matrixClient.getUserId();
-      if (!userId) return;
-      try {
-        const data = await matrixClient.getStateEvent(
-          roomId,
-          EventType.RoomPowerLevels,
-          "",
-        );
-        const powerLevels = ("users" in data ? data.users : data) as Record<
-          string,
-          number
-        >;
-        const userPowerLevel = powerLevels?.[userId] ?? 0;
-        setIsAdmin(userPowerLevel === 100);
-      } catch (error) {
-        logger.error("Daten konnten nicht heruntergeladen werden", error);
-      }
-    })();
-  }, [matrixClient, roomId]);
 
   const room = matrixClient.getRoom(roomId);
+  const loggedInUserId = matrixClient.getUserId();
+
   const rct = useMemo(
     () => (room ? getRoomNameAndCommunicationType(room) : undefined),
     [room],
   );
-  const roomCreator = useMemo(
-    () =>
-      room
-        ?.getLiveTimeline()
-        .getState(Direction.Forward)
-        ?.getStateEvents(EventType.RoomCreate)?.[0]?.event.sender,
-    [room],
+
+  const roomCreator = useMemo(() => getRoomCreator(room), [room]);
+
+  const exceptMe = useCallback(
+    (roomMembers: ChatRoomMember[]) =>
+      filter(
+        roomMembers,
+        (m) => !isStrictEqual(m.member.userId, room?.myUserId),
+      ),
+    [room?.myUserId],
   );
 
-  const allRoomMembers =
-    room?.getMembers().map((member) => ({
-      member,
-      isRoomCreator: isStrictEqual(member.userId, roomCreator),
-    })) ?? [];
-  const joinedMembers = allRoomMembers?.filter(
-    (roomMember) => roomMember.member.membership === "join",
+  const checkIfAdmin = useCallback(
+    () =>
+      Boolean(
+        find(getRoomAdmins(room), (u) => isStrictEqual(u, loggedInUserId)),
+      ),
+    [loggedInUserId, room],
   );
-  const roomMembers = joinedMembers?.filter(
-    ({ member }) => !isStrictEqual(member.userId, room?.myUserId),
+
+  const allRoomMembers: ChatRoomMember[] = useMemo(
+    () =>
+      room?.getMembers().map((member) => ({
+        member,
+        isRoomCreator: isStrictEqual(member.userId, roomCreator),
+      })) ?? [],
+    [room, roomCreator],
   );
 
   const dmRoomMember = isDMRoom(rct?.communicationType)
-    ? roomMembers?.[0]
+    ? exceptMe(allRoomMembers)?.[0]
     : undefined;
 
-  const avatarUrl = useMemo(
+  const getJoinedMembers = useCallback(
+    () => filter(allRoomMembers, (m) => m.member.membership === "join"),
+    [allRoomMembers],
+  );
+
+  const getJoinedAndInvitedMembers = useCallback(
+    () => filter(allRoomMembers, (m) => m.member.membership !== "leave"),
+    [allRoomMembers],
+  );
+
+  const getAvatarUrl = useCallback(
     () =>
       isDMRoom(rct?.communicationType)
         ? getMemberAvatarUrl(matrixClient, dmRoomMember?.member)
@@ -99,12 +98,13 @@ export function useRoomInfo(roomId: string): RoomInfo {
     room,
     roomCreator,
     communicationType: rct?.communicationType,
-    avatarUrl,
-    roomMembers,
     allRoomMembers,
-    joinedMembers,
     dmRoomMember,
-    isAdmin,
     matrixClient,
+    getAvatarUrl,
+    getJoinedMembers,
+    getJoinedAndInvitedMembers,
+    checkIfAdmin,
+    exceptMe,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
index 94daff2c0..488b9f2d9 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/hooks/useRoomMessages.tsx
@@ -20,13 +20,13 @@ import {
   ClientState,
   MessageTypeEnum,
 } from "@/lib/businessModules/chat/shared/enums";
+import { logger } from "@/lib/businessModules/chat/shared/helpers";
 import { useChatSearchParams } from "@/lib/businessModules/chat/shared/hooks/useChatSearchParams";
 import {
   Message,
   ReadConfirmationsPerUser,
   RoomEventDetails,
   isChatMessageType,
-  isMessageTypeWithBody,
 } from "@/lib/businessModules/chat/shared/types";
 import { sortMessages } from "@/lib/businessModules/chat/shared/utils";
 
@@ -58,16 +58,18 @@ export function useRoomMessages() {
         txnId,
       );
       await matrixClient.sendTyping(selectedRoomId, false, 3000);
-    } catch {}
+    } catch {
+      logger.warn("Sending message failed", error);
+    }
   }
 
   const onMessage = useCallback(
-    async ({ event, room, removed }: RoomEventDetails) => {
+    async ({ event, room }: RoomEventDetails) => {
       if (event.isEncrypted()) {
         await matrixClient.decryptEventIfNeeded(event);
       }
       const messageContent = event.getContent();
-      if (!isMessageTypeWithBody(messageContent)) return;
+      // if (!isMessageTypeWithBody(messageContent)) return;
       const id =
         event.getId() ??
         format(addMilliseconds(new Date(), Math.random() * 1000), "T");
@@ -75,12 +77,13 @@ export function useRoomMessages() {
 
       return {
         sender,
-        content: removed ? "Nachricht gelöscht" : messageContent.body,
+        content: messageContent.body as string,
         timestamp: event.getDate(),
         id,
         roomId: room?.roomId,
         mentions: messageContent["m.mentions"]?.user_ids,
         messageType: MessageTypeEnum.ChatMessage,
+        removed: false,
       };
     },
     [matrixClient],
@@ -94,6 +97,26 @@ export function useRoomMessages() {
       removed: boolean,
     ) {
       const eventType = event.getType();
+      if (eventType === "m.room.redaction") {
+        if (!room) return;
+        const eventContent = event.getContent();
+        const messageId = (eventContent.redacts ||
+          event.event.redacts) as string;
+        setMessages((prevState) => {
+          if (!(room.roomId in prevState)) {
+            return prevState;
+          }
+          const roomMessages = prevState[room.roomId] ?? [];
+          return {
+            ...prevState,
+            [room.roomId]: roomMessages.map((message) =>
+              message.id === messageId
+                ? { ...message, content: "Nachricht gelöscht", removed: true }
+                : message,
+            ),
+          };
+        });
+      }
       if (
         !room ||
         (eventType !== "m.room.message" && eventType !== "m.room.encrypted")
@@ -101,7 +124,7 @@ export function useRoomMessages() {
         return;
       }
       const temporaryId = event.getId();
-      const newMessage = await onMessage({ event, room, removed });
+      const newMessage = await onMessage({ event, room });
       const messageWithSentStatus = {
         ...newMessage,
         sent: event.getSender() !== loggedInUserId,
@@ -189,7 +212,7 @@ export function useRoomMessages() {
               return;
             }
             const readReceipts = room.getReceiptsForEvent(event);
-            const message = await onMessage({ event, room, removed: false });
+            const message = await onMessage({ event, room });
             const readReceiptsObj =
               readReceipts?.reduce<ReadConfirmationsPerUser>(
                 (acc, { userId, data }) => {
@@ -207,8 +230,12 @@ export function useRoomMessages() {
             return { ...message, readReceipts: readReceiptsObj };
           }),
         );
+        const newMessagesWithRemovedMessages = updateMessagesWithRemovalEvents(
+          newRoomMessages,
+          events,
+        );
 
-        const filteredMessages = newRoomMessages
+        const filteredMessages = newMessagesWithRemovedMessages
           .filter(isChatMessageType)
           .filter((item) => !!item);
 
@@ -219,9 +246,7 @@ export function useRoomMessages() {
           if (!filteredMessages.length) {
             return { ...prevState, [roomId]: [] };
           }
-          const sortedMessagesFromRoom = sortMessages(
-            filteredMessages as Message[],
-          );
+          const sortedMessagesFromRoom = sortMessages(filteredMessages);
 
           return {
             ...prevState,
@@ -268,13 +293,17 @@ export function useRoomMessages() {
           ) {
             return;
           }
-          const message = await onMessage({ event, room, removed: false });
+          const message = await onMessage({ event, room });
           return { ...message, sent: true };
         }),
       );
+      const newMessagesWithRemovedMessages = updateMessagesWithRemovalEvents(
+        newMessages,
+        events,
+      );
 
       setMessages((prevState) => {
-        const correctNewMessages = newMessages
+        const correctNewMessages = newMessagesWithRemovedMessages
           .filter(isChatMessageType)
           .filter((item) => !!item);
         if (!correctNewMessages.length) {
@@ -320,3 +349,25 @@ export function useRoomMessages() {
     error,
   };
 }
+
+function updateMessagesWithRemovalEvents(
+  messages: (Omit<Message, "sent"> | undefined)[],
+  events: MatrixEvent[],
+) {
+  return messages.map((msg) => {
+    if (!msg) return;
+    const removalEvent = events.find(
+      (event) =>
+        event.getType() === "m.room.redaction" &&
+        event.getContent().redacts === msg.id,
+    );
+    if (removalEvent) {
+      return {
+        ...msg,
+        content: "Nachricht gelöscht",
+        removed: true,
+      };
+    }
+    return msg;
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
index a9b203e0c..8a4f306b2 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/chat/shared/sideNavigationItem.tsx
@@ -6,7 +6,10 @@
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
 import { Chat } from "@mui/icons-material";
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import {
+  SideNavigationItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { ChatMessageCounter } from "@/lib/businessModules/chat/components/ChatMessageCounter";
 import { useChat } from "@/lib/businessModules/chat/shared/ChatProvider";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
@@ -21,12 +24,8 @@ export const sideNavigationItem: SideNavigationItem = {
   chip: <ChatMessageCounter />,
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const { canAccessChat } = useChat();
 
-  if (!canAccessChat) {
-    return [];
-  }
-
-  return [sideNavigationItem];
+  return { isLoading: false, items: canAccessChat ? [sideNavigationItem] : [] };
 }
diff --git a/employee-portal/src/lib/businessModules/chat/shared/types.ts b/employee-portal/src/lib/businessModules/chat/shared/types.ts
index 488b4d676..ead169983 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/types.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/types.ts
@@ -37,6 +37,7 @@ export interface Message {
   readReceipts?: ReadConfirmationsPerUser;
   messageType: MessageTypeEnum;
   sent: boolean;
+  removed: boolean;
 }
 
 export function isChatMessageType(data: unknown): data is Message {
@@ -58,7 +59,7 @@ export interface CreateRoomOptions {
   invite: string[];
 }
 
-export type Presence = IPresenceOpts["presence"];
+export type Presence = IPresenceOpts["presence"] | "deactivated";
 export type UsersPresence = Record<string, Presence>;
 
 export type ReadConfirmationsPerUser = Record<
@@ -182,3 +183,7 @@ export interface UserDirectoryResponse {
   results: UserFromDirectory[];
   limited: boolean;
 }
+
+export interface UserToInvite extends UserFromDirectory {
+  department?: string;
+}
diff --git a/employee-portal/src/lib/businessModules/chat/shared/utils.ts b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
index b1b60c423..c5b25b315 100644
--- a/employee-portal/src/lib/businessModules/chat/shared/utils.ts
+++ b/employee-portal/src/lib/businessModules/chat/shared/utils.ts
@@ -17,6 +17,7 @@ import {
 } from "date-fns";
 import { de } from "date-fns/locale";
 import {
+  Direction,
   EventTimeline,
   EventType,
   MatrixClient,
@@ -26,7 +27,17 @@ import {
   RoomMember,
   User,
 } from "matrix-js-sdk/lib/matrix";
-import { isEmpty, isNonNullish, isStrictEqual, isString, last } from "remeda";
+import {
+  forEach,
+  isEmpty,
+  isNonNullish,
+  isStrictEqual,
+  isString,
+  keys,
+  last,
+  pickBy,
+  pipe,
+} from "remeda";
 
 import { CommunicationType } from "@/lib/businessModules/chat/shared/enums";
 import {
@@ -270,9 +281,24 @@ export function getStatusColor(status: Presence | undefined) {
     case "offline":
       return "danger.plainColor";
     case "unavailable":
+      return "warning.400";
+    case "deactivated":
       return "danger.plainDisabledColor";
     default:
-      return undefined;
+      return "danger.plainColor";
+  }
+}
+
+export function getPresenseLabel(status: Presence | undefined) {
+  switch (status) {
+    case "online":
+      return "Online";
+    case "offline":
+      return "Inaktiv";
+    case "unavailable":
+      return "Unischtbar";
+    default:
+      return "";
   }
 }
 
@@ -444,21 +470,17 @@ export function allMessagesRead(
   });
 }
 
-export async function reassignAdminRole({
-  matrixClient,
-  users,
-  roomId,
-}: {
-  matrixClient: MatrixClient;
-  users: Record<string, number>;
-  roomId: string;
-}) {
-  const powerLevels = await matrixClient.getStateEvent(
-    roomId,
-    EventType.RoomPowerLevels,
-    "",
-  );
-  await matrixClient.sendStateEvent(roomId, EventType.RoomPowerLevels, {
+export async function reassignAdminRole(
+  matrixClient: MatrixClient,
+  room: Room,
+  users: Record<string, number>,
+) {
+  const powerLevels = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomPowerLevels)?.[0]?.event.content;
+
+  await matrixClient.sendStateEvent(room.roomId, EventType.RoomPowerLevels, {
     ...powerLevels,
     users,
   });
@@ -538,19 +560,43 @@ export function getReadReceipts(
   );
 }
 
-export function clearSearchParam(paramName: string) {
+export function clearSearchParams(...paramNames: string[]) {
   const url = new URL(window.location.href);
-  const searchParam = url.searchParams.get(paramName);
-  if (isNonNullish(searchParam)) {
-    url.searchParams.delete(paramName);
-    window.history.replaceState(null, "", url.href);
-  }
-}
-
-export function clearLoginToken() {
-  return clearSearchParam("loginToken");
-}
-
-export function clearUserIdParam() {
-  return clearSearchParam("userId");
+  forEach(paramNames, (paramName) => {
+    const searchParam = url.searchParams.get(paramName);
+    if (isNonNullish(searchParam)) {
+      url.searchParams.delete(paramName);
+    }
+  });
+  window.history.replaceState(null, "", url.href);
+}
+
+export function getRoomAdmins(room: Room | null) {
+  const eventContent = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomPowerLevels)[0]
+    ?.getContent<{
+      users?: Record<string, number>;
+    }>();
+
+  return eventContent?.users
+    ? pipe(
+        eventContent.users,
+        pickBy((value) => value === 100),
+        keys(),
+      )
+    : [];
+}
+
+export function getRoomCreator(room: Room | null) {
+  const eventContent = room
+    ?.getLiveTimeline()
+    .getState(Direction.Forward)
+    ?.getStateEvents(EventType.RoomCreate)[0]
+    ?.getContent<{
+      creator: string;
+    }>();
+
+  return eventContent?.creator;
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/api/clients.ts b/employee-portal/src/lib/businessModules/inspection/api/clients.ts
index 5ee306af0..43e9c0983 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/clients.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/clients.ts
@@ -13,6 +13,7 @@ import {
   EditorApi,
   FacilityApi,
   FileApi,
+  ImporterApi,
   InboxProcedureApi,
   InspectionApi,
   InspectionFeatureTogglesApi,
@@ -146,3 +147,8 @@ export function useArchivingApi() {
   const configuration = useConfiguration();
   return new ArchivingApi(configuration);
 }
+
+export function useImportApi() {
+  const configuration = useConfiguration();
+  return new ImporterApi(configuration);
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
index c9668f2cf..2b6253010 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts
@@ -6,6 +6,8 @@
 import {
   ApproveInspectionRequest,
   FinalizeInspectionRequest,
+  ResolveFacilityDuplicateRequest,
+  ResolveInspectionDuplicateRequest,
   StartInspectionRequest,
   UpdateInspectionRequest,
 } from "@eshg/employee-portal-api/inspection";
@@ -66,6 +68,22 @@ export function useApproveInspection() {
   });
 }
 
+export function useResolveFacilityDuplicate() {
+  const inspectionApi = useInspectionApi();
+  return useHandledMutation({
+    mutationFn: (req: ResolveFacilityDuplicateRequest) =>
+      inspectionApi.resolveFacilityDuplicateRaw(req).then(unwrapRawResponse),
+  });
+}
+
+export function useResolveInspectionDuplicate() {
+  const inspectionApi = useInspectionApi();
+  return useHandledMutation({
+    mutationFn: (req: ResolveInspectionDuplicateRequest) =>
+      inspectionApi.resolveInspectionDuplicateRaw(req).then(unwrapRawResponse),
+  });
+}
+
 export function useCreateTestData() {
   const inspectionTestDataApi = useInspectionTestDataApi();
   const snackbar = useSnackbar();
diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts
new file mode 100644
index 000000000..9e001c39c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiImportStatistics,
+  ApiImportStatisticsFromJSON,
+  ApiResponse,
+  ImportProcessesRequest,
+} from "@eshg/employee-portal-api/inspection";
+import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
+
+import { useImportApi } from "@/lib/businessModules/inspection/api/clients";
+
+export interface ImportProcessResult {
+  file: File;
+  statistics: ApiImportStatistics;
+}
+
+export function useImportProcess() {
+  const importApi = useImportApi();
+
+  return useHandledMutation({
+    mutationFn: async (
+      request: ImportProcessesRequest,
+    ): Promise<ImportProcessResult> => {
+      return await importApi
+        .importProcessesRaw(request)
+        .then(parseImportResult);
+    },
+  });
+}
+
+async function parseImportResult(
+  response: ApiResponse<object>,
+): Promise<ImportProcessResult> {
+  const formData = await response.raw.formData();
+  const statisticsStr = formData.get("statistics");
+  const file = formData.get("file");
+
+  if (!(file instanceof File && typeof statisticsStr === "string")) {
+    throw new Error("Invalid response");
+  }
+
+  const statistics = ApiImportStatisticsFromJSON(JSON.parse(statisticsStr));
+
+  return {
+    file,
+    statistics,
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
index aa68e7cfa..6ceb9b403 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts
@@ -3,25 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  FacilityApi,
-  GetPendingFacilitiesRequest,
-} from "@eshg/employee-portal-api/inspection";
+import { GetPendingFacilitiesRequest } from "@eshg/employee-portal-api/inspection";
 import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse";
-import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
+import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useFacilityApi } from "@/lib/businessModules/inspection/api/clients";
 import { facilityApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 import { PendingFacilitiesFilters } from "@/lib/businessModules/inspection/shared/types";
 
-export function useGetFacility(facilityId: string) {
-  const facilityApi = useFacilityApi();
-  return useSuspenseQuery({
-    queryKey: facilityApiQueryKey(["getFacility", { facilityId }]),
-    queryFn: () => facilityApi.getFacility(facilityId),
-  });
-}
-
 export function useGetPendingFacilities(filters: PendingFacilitiesFilters) {
   const facilityApi = useFacilityApi();
 
@@ -34,19 +23,6 @@ export function useGetPendingFacilities(filters: PendingFacilitiesFilters) {
   });
 }
 
-export function getPendingFacilitiesQuery(
-  filters: PendingFacilitiesFilters,
-  facilityApi: FacilityApi,
-) {
-  const req = facilitiesFiltersToApi(filters);
-
-  return queryOptions({
-    queryKey: facilityApiQueryKey(["getPendingFacilities", { req }]),
-    queryFn: () =>
-      facilityApi.getPendingFacilitiesRaw(req).then(unwrapRawResponse),
-  });
-}
-
 function facilitiesFiltersToApi(
   filters: PendingFacilitiesFilters,
 ): GetPendingFacilitiesRequest {
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
index 865f400f8..7e580a837 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts
@@ -35,6 +35,20 @@ export function getAvailablePLDRsQueryKey(inspectionId: string) {
   ]);
 }
 
+export function getFacilityDuplicatesQueryKey(inspectionId: string) {
+  return inspectionApiQueryKey([
+    inspectionGettersQueryKey(inspectionId),
+    "getFacilityDuplicates",
+  ]);
+}
+
+export function getInspectionDuplicatesQueryKey(inspectionId: string) {
+  return inspectionApiQueryKey([
+    inspectionGettersQueryKey(inspectionId),
+    "getInspectionDuplicates",
+  ]);
+}
+
 export function useGetInspection(procedureId: string) {
   const inspectionApi = useInspectionApi();
   const getPreCacheForOfflineModeHeaders = useGetHeadersForOfflineCaching();
@@ -101,3 +115,19 @@ export function useGetAvailablePLDRs(inspectionId: string) {
       ),
   });
 }
+
+export function useGetFacilityDuplicates(procedureId: string) {
+  const inspectionApi = useInspectionApi();
+  return useSuspenseQuery({
+    queryKey: getFacilityDuplicatesQueryKey(procedureId),
+    queryFn: () => inspectionApi.getFacilityDuplicates(procedureId),
+  });
+}
+
+export function useGetInspectionDuplicates(procedureId: string) {
+  const inspectionApi = useInspectionApi();
+  return useSuspenseQuery({
+    queryKey: getInspectionDuplicatesQueryKey(procedureId),
+    queryFn: () => inspectionApi.getInspectionDuplicates(procedureId),
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
index 3f8d63e91..dc78cef33 100644
--- a/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
+++ b/employee-portal/src/lib/businessModules/inspection/api/queries/resources.ts
@@ -39,37 +39,37 @@ export function useGetResourcesWithEvents(props: {
   const calendarEventApi = useCalendarEventApi();
   return useSuspenseQuery({
     queryKey: resourceApiQueryKey(["getResourcesWithEvents", props]),
-    queryFn: () =>
-      resourceApi
-        // first get all resources for given resourceType
+    queryFn: async () => {
+      // first get all resources for given resourceType
+      const resources = await resourceApi
         .getResourcesRaw({ type: props.resourceType })
         .then(unwrapRawResponse)
-        .then((response) => response.elements)
-        // now determine which resources are free or blocked in [start - end]:
-        .then((resources) => {
-          if (resources.length > 0) {
-            return (
-              calendarEventApi
-                .getBlockingEventsOfResourceCalendarsRaw({
-                  apiGetBlockingEventsOfResourcesRequest: {
-                    resourceIds: resources.map((resource) => resource.id),
-                    timeRangeStart: props.start,
-                    timeRangeEnd: props.end,
-                  },
-                })
-                .then(unwrapRawResponse)
-                // return both resource and calendar response
-                .then((calendarResponse) => ({ resources, calendarResponse }))
-            );
-          } else {
-            return {
-              resources,
-              calendarResponse: {
-                notFoundResourceIds: [],
-                resourcesWithBlockingEvents: [],
-              } satisfies ApiGetBlockingEventsOfResourcesResponse,
-            };
-          }
-        }),
+        .then((response) => response.elements);
+
+      // now determine which resources are free or blocked in [start - end]:
+      if (resources.length > 0) {
+        return (
+          calendarEventApi
+            .getBlockingEventsOfResourceCalendarsRaw({
+              apiGetBlockingEventsOfResourcesRequest: {
+                resourceIds: resources.map((resource) => resource.id),
+                timeRangeStart: props.start,
+                timeRangeEnd: props.end,
+              },
+            })
+            .then(unwrapRawResponse)
+            // return both resource and calendar response
+            .then((calendarResponse) => ({ resources, calendarResponse }))
+        );
+      } else {
+        return {
+          resources,
+          calendarResponse: {
+            notFoundResourceIds: [],
+            resourcesWithBlockingEvents: [],
+          } satisfies ApiGetBlockingEventsOfResourcesResponse,
+        };
+      }
+    },
   });
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
index bf7531e2d..d64924bea 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/elements/ChecklistDefinitionElement.tsx
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiCLSectionContextElementsInner,
-  ApiInspectionFeature,
-} from "@eshg/employee-portal-api/inspection";
+import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd";
@@ -22,7 +19,6 @@ import {
 } from "@mui/icons-material";
 import { Box, Chip, Divider, Grid, IconButton, Stack } from "@mui/joy";
 
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import { NoteAndHelpTextInput } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/NoteAndHelpTextInput";
 import { ChecklistDefinitionElementInner } from "@/lib/businessModules/inspection/components/checklistDefinition/elements/inner/ChecklistDefinitionElementInner";
 import { CopyDeleteDropdown } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/CopyDeleteDropdown";
@@ -50,10 +46,6 @@ export function ChecklistDefinitionElement({
   readOnlyMode,
   dragHandleProps,
 }: Readonly<ChecklistDefinitionElementProps>) {
-  const isChecklistAudioFeatureEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.ChecklistAudios,
-  );
-
   const isImage = element.type === "IMAGE" || element.type === "CLImageContext";
   const isAudio = element.type === "AUDIO" || element.type === "CLAudioContext";
   const isSeparator =
@@ -202,19 +194,15 @@ export function ChecklistDefinitionElement({
                     </>
                   ),
                 },
-                ...(isChecklistAudioFeatureEnabled
-                  ? [
-                      {
-                        value: "AUDIO",
-                        label: (
-                          <>
-                            <Audiotrack />
-                            Audio hinzufügen
-                          </>
-                        ),
-                      },
-                    ]
-                  : []),
+                {
+                  value: "AUDIO",
+                  label: (
+                    <>
+                      <Audiotrack />
+                      Audio hinzufügen
+                    </>
+                  ),
+                },
               ]}
             />
             {!isImage && !isAudio && (
diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
index 861cf02b4..baee7a808 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/ChecklistDefinitionOverviewTable.tsx
@@ -144,8 +144,10 @@ export function ChecklistDefinitionOverviewTable({
           <DataTable
             data={checklists}
             columns={columns}
-            rowNavRoute={onRowClick}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: onRowClick,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx
new file mode 100644
index 000000000..42ccbab24
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine.tsx
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { Chip, Stack } from "@mui/joy";
+
+import { LineWithPossibleExclamationMark } from "@/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark";
+
+export interface DuplicateTileLineProps<T> {
+  dataset: T;
+  importedDataset: T;
+  textExtractor: (d: T) => string;
+  suppressExclamationMark?: boolean;
+  badgeText?: string;
+}
+
+export function DuplicateTileLine<T>({
+  dataset,
+  importedDataset,
+  textExtractor,
+  suppressExclamationMark,
+  badgeText,
+}: Readonly<DuplicateTileLineProps<T>>) {
+  const text = textExtractor(dataset);
+  const importedText = suppressExclamationMark
+    ? text
+    : textExtractor(importedDataset);
+
+  return (
+    <Stack direction="row" alignItems={"center"} gap={1} flexWrap="wrap">
+      <Stack direction="row" gap={0} flexGrow={1} justifyContent="flex-start">
+        <LineWithPossibleExclamationMark
+          text={text}
+          importedText={importedText}
+        />
+      </Stack>
+      {badgeText && (
+        <Stack direction="row" gap={0} flexGrow={1} justifyContent="flex-end">
+          <Chip
+            sx={() => ({
+              color: "white",
+              bgcolor: "black",
+              paddingTop: 0.5,
+              paddingBottom: 0.5,
+            })}
+          >
+            {badgeText}
+          </Chip>
+        </Stack>
+      )}
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx
new file mode 100644
index 000000000..3bb05ed3f
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiFacilityForDuplicateReview } from "@eshg/employee-portal-api/inspection";
+import { Radio, Sheet, Stack, Typography } from "@mui/joy";
+
+import { DuplicateTileLine } from "@/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine";
+
+export interface FacilityDuplicateTileProps {
+  facility: ApiFacilityForDuplicateReview;
+  importedFacility: ApiFacilityForDuplicateReview;
+  isImportedFacility: boolean;
+}
+
+export function FacilityDuplicateTile({
+  facility,
+  importedFacility,
+  isImportedFacility,
+}: Readonly<FacilityDuplicateTileProps>) {
+  const badgeText = isImportedFacility ? "Import" : "Stammdaten";
+  return (
+    <Sheet
+      aria-label="Einrichtung"
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.lg,
+        border: "1px solid",
+        borderColor: isImportedFacility ? "warning.300" : "divider",
+        backgroundColor: isImportedFacility ? "warning.100" : "transparent",
+      }}
+    >
+      <Radio
+        value={facility.referenceId}
+        sx={{ alignItems: "center", width: "100%" }}
+        label={
+          <Stack direction="column" gap={2}>
+            <Typography level="h4" component="p">
+              {isImportedFacility
+                ? "Daten aus Import bestätigen"
+                : "Zusammenführen mit"}
+            </Typography>
+            <Stack direction="column" gap={1}>
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.name}
+              />
+              {facility.objectType?.name && (
+                <DuplicateTileLine
+                  dataset={facility}
+                  importedDataset={importedFacility}
+                  textExtractor={(f) => f.objectType!.name}
+                />
+              )}
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.street + " " + f.houseNo}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.postalCode + " " + f.city}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.emailAddresses.join(", ")}
+              />
+              <DuplicateTileLine
+                dataset={facility}
+                importedDataset={importedFacility}
+                textExtractor={(f) => f.phoneNumbers.join(", ")}
+                badgeText={badgeText}
+              />
+            </Stack>
+          </Stack>
+        }
+      />
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx
new file mode 100644
index 000000000..0a5cd4faf
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiInspectionForDuplicateReview } from "@eshg/employee-portal-api/inspection";
+import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime";
+import { Sheet, Stack, Typography } from "@mui/joy";
+
+import { DuplicateTileLine } from "@/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine";
+import {
+  translateInspectionResult,
+  translateInspectionType,
+} from "@/lib/businessModules/inspection/shared/enums";
+
+export interface InspectionDuplicateTileProps {
+  inspection: ApiInspectionForDuplicateReview;
+  importedInspection: ApiInspectionForDuplicateReview;
+  isImportedInspection: boolean;
+}
+
+export function InspectionDuplicateTile({
+  inspection,
+  importedInspection,
+  isImportedInspection,
+}: Readonly<InspectionDuplicateTileProps>) {
+  const badgeText = isImportedInspection ? "Import" : "Stammdaten";
+
+  return (
+    <Sheet
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.lg,
+        border: "1px solid",
+        borderColor: isImportedInspection ? "warning.300" : "divider",
+        backgroundColor: isImportedInspection ? "warning.100" : "transparent",
+      }}
+      aria-label={"Einrichtung"}
+    >
+      <Stack direction="column" gap={2}>
+        <Typography level="h4" component="p">
+          {inspection.title}
+        </Typography>
+        <Stack direction="column" gap={1}>
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) => translateInspectionType(i.type)}
+            suppressExclamationMark={true}
+          />
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) =>
+              " Durchgeführt: " +
+              formatDate(i.executedTime) +
+              ", " +
+              formatTime(i.executedTime)
+            }
+          />
+          <DuplicateTileLine
+            dataset={inspection}
+            importedDataset={importedInspection}
+            textExtractor={(i) =>
+              "Ergebnis: " + translateInspectionResult(i.result)
+            }
+            badgeText={
+              inspection.numberOfIncidents == 0 ? badgeText : undefined
+            }
+          />
+          {inspection.numberOfIncidents != 0 && (
+            <DuplicateTileLine
+              dataset={inspection}
+              importedDataset={importedInspection}
+              textExtractor={(i) =>
+                "" +
+                i.numberOfIncidents +
+                " Vorkommnis" +
+                (i.numberOfIncidents != 1 && "se")
+              }
+              badgeText={
+                inspection.numberOfIncidents != 0 ? badgeText : undefined
+              }
+            />
+          )}
+        </Stack>
+      </Stack>
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx
new file mode 100644
index 000000000..98d530b4d
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/LineWithPossibleExclamationMark.tsx
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ErrorOutlineOutlined } from "@mui/icons-material";
+import { Stack, Typography } from "@mui/joy";
+
+export interface InspectionDuplicateTileProps {
+  text: string;
+  importedText: string;
+}
+
+export function LineWithPossibleExclamationMark({
+  text,
+  importedText,
+}: Readonly<InspectionDuplicateTileProps>) {
+  return (
+    <Stack direction="row" gap={0.5}>
+      {text !== importedText && (
+        <ErrorOutlineOutlined
+          sx={{ height: 16, marginTop: 0.4, color: "#9A5B13" }}
+        />
+      )}
+      <Typography>{text}</Typography>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
index 1b57b9f9f..87c44757c 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/NewFacilityButton.tsx
@@ -65,7 +65,7 @@ function NewFacilityButtonWithinOverlay() {
       {
         onSuccess: afterSave,
       },
-    ).catch();
+    );
   }
 
   async function handleSelectFacility(
@@ -85,7 +85,7 @@ function NewFacilityButtonWithinOverlay() {
           }
         },
       },
-    ).catch();
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
index 865120498..c34017018 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesOfflineTable.tsx
@@ -35,6 +35,9 @@ export function PendingFacilitiesOfflineTable() {
   const columns = createPendingFacilitiesColumns(
     false,
     handleViewIncidentsClick,
+    () => undefined,
+    () => undefined,
+    false,
   );
 
   const [userActivity, setUserActivity] =
@@ -63,8 +66,10 @@ export function PendingFacilitiesOfflineTable() {
           columns={columns}
           sorting={tableControl.tableSorting}
           noDataComponent={NoDataHint}
-          rowNavRoute={getPendingFacilityRowRoute}
-          focusColumnHeader={"Name"}
+          rowNavigation={{
+            route: getPendingFacilityRowRoute,
+            focusColumnAccessorKey: "name",
+          }}
           striped
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
index 1cbd43fb1..5c65558de 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesTable.tsx
@@ -5,17 +5,26 @@
 
 "use client";
 
-import { ApiObjectType } from "@eshg/employee-portal-api/inspection";
+import {
+  ApiInspectionFeature,
+  ApiObjectType,
+} from "@eshg/employee-portal-api/inspection";
 import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions";
 import { addDays, formatISO } from "date-fns";
 import { useMemo, useState } from "react";
 
 import { procedureStatusNames } from "@/lib/baseModule/api/procedures/enums";
 import { useGetPendingFacilities } from "@/lib/businessModules/inspection/api/queries/facility";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes";
 import { NewFacilityButton } from "@/lib/businessModules/inspection/components/facility/pending/NewFacilityButton";
 import { PendingFacilitiesIncidentsSidebar } from "@/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar";
+import { PotentialDuplicatesWarning } from "@/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning";
+import { useReviewFacilityDuplicateSidebar } from "@/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar";
+import { useReviewInspectionDuplicateSidebar } from "@/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar";
+import { ProcessImportButton } from "@/lib/businessModules/inspection/components/processImport/ProcessImportButton";
 import {
+  inspectionDuplicateFilterNames,
   inspectionPendingFacilityKindNames,
   inspectionPhaseNames,
   inspectionTypeNames,
@@ -53,13 +62,14 @@ const initialUserActivity: UserActivityState = { type: "view-table" };
 
 function createFilterDefinitions(
   objectTypes: ApiObjectType[],
+  isImportFeatureEnabled: boolean,
 ): FilterDefinition[] {
   const objectTypeOptions = objectTypes.map((o) => ({
     label: o.name,
     value: o.id,
   }));
 
-  return [
+  const filterDefinitions: FilterDefinition[] = [
     {
       type: "EnumSingle",
       key: "kind",
@@ -101,15 +111,32 @@ function createFilterDefinitions(
       name: "Begehung nach",
     },
   ];
+
+  if (isImportFeatureEnabled) {
+    filterDefinitions.push({
+      type: "EnumSingle",
+      key: "hasDuplicates",
+      name: "Duplikat",
+      options: optionsFromRecord(inspectionDuplicateFilterNames),
+    });
+  }
+
+  return filterDefinitions;
 }
 
 export function PendingFacilitiesTable(
   props: Readonly<{ filter: PendingFacilitiesFilters }>,
 ) {
   const isOfflineEnabled = useIsOfflineFeatureEnabled();
+  const isImportFeatureEnabled = useIsNewFeatureEnabled(
+    ApiInspectionFeature.Import,
+  );
   const { data: objectTypes } = useGetObjectTypes();
 
-  const filterDefinitions = createFilterDefinitions(objectTypes);
+  const filterDefinitions = createFilterDefinitions(
+    objectTypes,
+    isImportFeatureEnabled,
+  );
   const paramStateProvider = useSearchParamStateProvider(
     filterDefinitions,
     true,
@@ -133,12 +160,31 @@ export function PendingFacilitiesTable(
     filter: props.filter,
   });
 
+  function filterForDuplicates() {
+    filterSettings.filterSettingsProps.activeFilterProps.deleteAllFilterValues();
+    filterSettings.filterSettingsProps.onDraftValueChange("hasDuplicates", {
+      type: "EnumSingle",
+      key: "hasDuplicates",
+      selectedValue: "true",
+    });
+    paramStateProvider.setActiveValues([
+      {
+        type: "EnumSingle",
+        key: "hasDuplicates",
+        selectedValue: "true",
+      },
+    ]);
+  }
+
   const { data: procedures, isFetching } = useGetPendingFacilities(filter);
 
   const tableControl = useTableControl({ serverSideSorting: true });
   const columns = createPendingFacilitiesColumns(
     isOfflineEnabled,
     handleViewIncidentsClick,
+    openReviewFacilityDuplicateSidebar,
+    openReviewInspectionDuplicateSidebar,
+    isImportFeatureEnabled,
   );
 
   const [userActivity, setUserActivity] =
@@ -152,6 +198,18 @@ export function PendingFacilitiesTable(
     showSearch: false,
   });
 
+  const reviewFacilityDuplicateSidebar = useReviewFacilityDuplicateSidebar();
+  const reviewInspectionDuplicateSidebar =
+    useReviewInspectionDuplicateSidebar();
+
+  function openReviewFacilityDuplicateSidebar(inspectionId: string) {
+    reviewFacilityDuplicateSidebar.open({ inspectionId });
+  }
+
+  function openReviewInspectionDuplicateSidebar(inspectionId: string) {
+    reviewInspectionDuplicateSidebar.open({ inspectionId });
+  }
+
   function handleSidebarClosed() {
     setUserActivity(initialUserActivity);
   }
@@ -169,6 +227,13 @@ export function PendingFacilitiesTable(
 
   return (
     <>
+      {isImportFeatureEnabled &&
+        procedures.numberOfPossibleDuplicates !== 0 && (
+          <PotentialDuplicatesWarning
+            numberOfDuplicates={procedures.numberOfPossibleDuplicates}
+            filterForDuplicates={filterForDuplicates}
+          />
+        )}
       <TablePage
         fullHeight
         controls={
@@ -198,7 +263,12 @@ export function PendingFacilitiesTable(
                 />
               </>
             }
-            right={<NewFacilityButton />}
+            right={
+              <>
+                <ProcessImportButton />
+                <NewFacilityButton />
+              </>
+            }
           />
         }
         filterSettings={
@@ -222,8 +292,10 @@ export function PendingFacilitiesTable(
             data={procedures.elements}
             columns={columns}
             sorting={tableControl.tableSorting}
-            rowNavRoute={getPendingFacilityRowRoute}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: getPendingFacilityRowRoute,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx
new file mode 100644
index 000000000..98af88e08
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PotentialDuplicatesWarning.tsx
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
+import { WarningAmberOutlined } from "@mui/icons-material";
+import { Sheet, Stack, Typography } from "@mui/joy";
+
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+
+export interface PotentialDuplicatesWarningProps {
+  numberOfDuplicates: number;
+  filterForDuplicates: () => void;
+}
+
+export function PotentialDuplicatesWarning({
+  numberOfDuplicates,
+  filterForDuplicates,
+}: Readonly<PotentialDuplicatesWarningProps>) {
+  return (
+    <Sheet
+      sx={{
+        padding: 2,
+        borderRadius: (theme) => theme.radius.sm,
+        border: "1px solid",
+        borderColor: "warning.300",
+        backgroundColor: "warning.100",
+        marginBottom: 2,
+      }}
+      aria-label={"Einrichtung"}
+    >
+      <ButtonBar
+        left={
+          <Stack direction="row" gap={1}>
+            <WarningAmberOutlined sx={{ color: "warning.600" }} />{" "}
+            <Typography sx={{ color: "warning.600" }}>
+              {numberOfDuplicates} potentielle{numberOfDuplicates == 1 && "s"}{" "}
+              Duplikat
+              {numberOfDuplicates != 1 && "e"}
+            </Typography>
+          </Stack>
+        }
+        right={
+          <ButtonLink
+            underline="none"
+            color="neutral"
+            textColor={"warning.600"}
+            fontWeight="lg"
+            onClick={filterForDuplicates}
+            sx={{ color: "warning.600" }}
+          >
+            FILTERN
+          </ButtonLink>
+        }
+      />
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx
new file mode 100644
index 000000000..b4006a086
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewFacilityDuplicateSidebar.tsx
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, RadioGroup, Stack } from "@mui/joy";
+import { ReactNode, useState } from "react";
+
+import { useResolveFacilityDuplicate } from "@/lib/businessModules/inspection/api/mutations/inspection";
+import { useGetFacilityDuplicates } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { FacilityDuplicateTile } from "@/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function useReviewFacilityDuplicateSidebar() {
+  return useSidebar({
+    component: ReviewFacilityDuplicateSidebar,
+  });
+}
+
+interface ReviewFacilityDuplicateSidebarProps extends DrawerProps {
+  inspectionId: string;
+}
+
+function ReviewFacilityDuplicateSidebar({
+  inspectionId,
+  onClose,
+}: ReviewFacilityDuplicateSidebarProps): ReactNode {
+  const { data: facilityDuplicateReview } =
+    useGetFacilityDuplicates(inspectionId);
+  const snackbar = useSnackbar();
+
+  const { mutateAsync: resolveFacilityDuplicate } =
+    useResolveFacilityDuplicate();
+
+  const [selectedReferenceId, setSelectedReferenceId] = useState(
+    facilityDuplicateReview.importedFacility.referenceId,
+  );
+
+  async function handleSubmit() {
+    const payload = {
+      id: inspectionId,
+      apiResolveFacilityDuplicateRequest: {
+        chosenReferenceId: selectedReferenceId,
+      },
+    };
+    await resolveFacilityDuplicate(payload, {
+      onSuccess: () =>
+        snackbar.confirmation(
+          selectedReferenceId ===
+            facilityDuplicateReview.importedFacility.referenceId
+            ? "Die Einrichtung wurde bestätigt."
+            : "Die Einrichtungen wurden zusammengeführt.",
+        ),
+    });
+    onClose();
+  }
+
+  return (
+    <>
+      <SidebarContent title={"Duplikatprüfung (Einrichtung)"}>
+        <Stack direction={"column"} spacing={2}>
+          <Alert
+            color="primary"
+            message="Es gibt ein potentielles Duplikat in der Datenbank. Sie können die importierte Einrichtung bestätigen oder mit einer vorhandenen Einrichtung zusammenführen."
+          />
+          <RadioGroup
+            defaultValue={facilityDuplicateReview.importedFacility.referenceId}
+            name="facilities-radio-group"
+            onChange={(event) => setSelectedReferenceId(event.target.value)}
+          >
+            <Stack direction={"column"} spacing={2}>
+              <FacilityDuplicateTile
+                facility={facilityDuplicateReview.importedFacility}
+                importedFacility={facilityDuplicateReview.importedFacility}
+                isImportedFacility={true}
+              ></FacilityDuplicateTile>
+              {facilityDuplicateReview.existingFacilities.map((facility) => (
+                <FacilityDuplicateTile
+                  key={facility.referenceId}
+                  facility={facility}
+                  importedFacility={facilityDuplicateReview.importedFacility}
+                  isImportedFacility={false}
+                ></FacilityDuplicateTile>
+              ))}
+            </Stack>
+          </RadioGroup>
+        </Stack>
+      </SidebarContent>
+
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <Button
+              component="a"
+              onClick={handleSubmit}
+              variant="solid"
+              color="primary"
+              sx={{ alignSelf: "end" }}
+            >
+              Bestätigen
+            </Button>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx
new file mode 100644
index 000000000..bb7c45ce1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/ReviewInspectionDuplicateSidebar.tsx
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, Stack, Typography } from "@mui/joy";
+import { ReactNode } from "react";
+
+import { useResolveInspectionDuplicate } from "@/lib/businessModules/inspection/api/mutations/inspection";
+import { useGetInspectionDuplicates } from "@/lib/businessModules/inspection/api/queries/inspection";
+import { InspectionDuplicateTile } from "@/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function useReviewInspectionDuplicateSidebar() {
+  return useSidebar({
+    component: ReviewInspectionDuplicateSidebar,
+  });
+}
+
+interface ReviewInspectionDuplicateSidebarProps extends DrawerProps {
+  inspectionId: string;
+}
+
+function ReviewInspectionDuplicateSidebar({
+  inspectionId,
+  onClose,
+}: ReviewInspectionDuplicateSidebarProps): ReactNode {
+  const { data: inspectionDuplicates } =
+    useGetInspectionDuplicates(inspectionId);
+  const snackbar = useSnackbar();
+
+  const resolveInspectionDuplicate = useResolveInspectionDuplicate();
+
+  async function handleSubmit(keepInspection: boolean) {
+    const payload = {
+      id: inspectionId,
+      apiResolveInspectionDuplicateRequest: {
+        keepInspection: keepInspection,
+      },
+    };
+    await resolveInspectionDuplicate.mutateAsync(payload, {
+      onSuccess: () =>
+        snackbar.confirmation(
+          keepInspection
+            ? "Der Vorgang wurde bestätigt."
+            : "Der Vorgang wurde verworfen.",
+        ),
+    });
+    onClose();
+  }
+
+  return (
+    <>
+      <SidebarContent title={"Duplikatprüfung (Vorgang)"}>
+        <Stack direction={"column"} spacing={2}>
+          <Alert
+            color="primary"
+            message="Es gibt ein potentielles Duplikat in der Datenbank. Sie können den importierten Vorgang bestätigen oder mit einem vorhandenen Vorgang zusammenführen."
+          />
+          <Stack direction={"column"} spacing={2}>
+            <Typography level="h4" component="p" sx={{ marginTop: 2 }}>
+              Importierter Vorgang:
+            </Typography>
+            <InspectionDuplicateTile
+              inspection={inspectionDuplicates.importedInspection}
+              importedInspection={inspectionDuplicates.importedInspection}
+              isImportedInspection={true}
+            ></InspectionDuplicateTile>
+            <Typography level="h4" component="p" sx={{ marginTop: 2 }}>
+              Bereits existierende Vorgänge:
+            </Typography>
+            {inspectionDuplicates.existingInspections.map((inspection) => (
+              <InspectionDuplicateTile
+                key={inspection.externalId}
+                inspection={inspection}
+                importedInspection={inspectionDuplicates.importedInspection}
+                isImportedInspection={false}
+              ></InspectionDuplicateTile>
+            ))}
+          </Stack>
+        </Stack>
+      </SidebarContent>
+
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <>
+              <Button
+                component="a"
+                onClick={() => handleSubmit(false)}
+                variant="solid"
+                color="neutral"
+                sx={{
+                  alignSelf: "end",
+                }}
+              >
+                Verwerfen
+              </Button>
+              <Button
+                component="a"
+                onClick={() => handleSubmit(true)}
+                variant="solid"
+                color="primary"
+                sx={{
+                  alignSelf: "end",
+                }}
+              >
+                Import bestätigen
+              </Button>
+            </>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
index b685173ca..50826fadd 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/columns.tsx
@@ -9,10 +9,11 @@ import {
 } from "@eshg/employee-portal-api/inspection";
 import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
-import { Stack } from "@mui/joy";
+import { IconButton, Stack } from "@mui/joy";
 import { ColumnHelper, Row, createColumnHelper } from "@tanstack/react-table";
 
 import { translateProcedureStatus } from "@/lib/baseModule/api/procedures/enums";
+import { DuplicateIcon } from "@/lib/businessModules/inspection/components/icons/DuplicateIcon";
 import { OfflineSwitch } from "@/lib/businessModules/inspection/components/inspection/OfflineSwitch";
 import {
   translateInspectionPhase,
@@ -30,8 +31,46 @@ export function createPendingFacilitiesColumns(
     inspectionId: string,
     facilityName: string,
   ) => void,
+  openReviewFacilityDuplicateSidebar: (inspectionId: string) => void,
+  openInspectionFacilityDuplicateSidebar: (inspectionId: string) => void,
+  isImportFeatureEnabled: boolean,
 ) {
   return [
+    isImportFeatureEnabled && offlineSwitch
+      ? columnHelper.accessor("possibleFacilityDuplicate", {
+          header: "",
+          cell: (ctx) =>
+            (ctx.getValue() && (
+              <IconButton
+                aria-label="Einrichtungsduplikat"
+                sx={{ color: "warning.900" }}
+                onClick={() =>
+                  openReviewFacilityDuplicateSidebar(
+                    ctx.row.original.inspection!.id,
+                  )
+                }
+              >
+                <DuplicateIcon />
+              </IconButton>
+            )) ||
+            (ctx.row.original.inspection!.possibleInspectionDuplicate && (
+              <IconButton
+                aria-label="Vorgangsduplikat"
+                sx={{ color: "warning.900" }}
+                onClick={() =>
+                  openInspectionFacilityDuplicateSidebar(
+                    ctx.row.original.inspection!.id,
+                  )
+                }
+              >
+                <DuplicateIcon />
+              </IconButton>
+            )),
+          meta: {
+            width: 48,
+          },
+        })
+      : null,
     columnHelper.accessor("kind", {
       header: "Art",
       cell: (ctx) => translatePendingFacilityKind(ctx.getValue()),
diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
index ceb109be2..a3a4138c1 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchTable.tsx
@@ -162,8 +162,10 @@ export function FacilityWebSearchTable({
       <DataTable
         data={data}
         columns={columns}
-        rowNavRoute={getRowRoute}
-        focusColumnHeader={"Name"}
+        rowNavigation={{
+          route: getRowRoute,
+          focusColumnAccessorKey: "name",
+        }}
         striped
       />
     </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx b/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx
new file mode 100644
index 000000000..92b3c49a7
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/icons/DuplicateIcon.tsx
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { SvgIcon, SvgIconProps } from "@mui/joy";
+
+export function DuplicateIcon(props: SvgIconProps) {
+  return (
+    <SvgIcon {...props}>
+      <svg
+        width="16"
+        height="20"
+        viewBox="0 0 16 20"
+        fill="none"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12.1667 0.833984H2.16667C1.25 0.833984 0.5 1.58398 0.5 2.50065V14.1673H2.16667V2.50065C6.07191 2.50065 8.26142 2.50065 12.1667 2.50065V0.833984ZM13.8333 4.16732H5.5C4.58333 4.16732 3.83333 4.91732 3.83333 5.83398V17.5006C3.83333 18.4173 4.58333 19.1673 5.5 19.1673H13.8333C14.75 19.1673 15.5 18.4173 15.5 17.5006V5.83398C15.5 4.91732 14.75 4.16732 13.8333 4.16732ZM13.8333 17.5006H5.5V5.83398H13.8333V17.5006Z"
+          fill="#9A5B13"
+        />
+        <path
+          d="M8.83333 7.50065H10.5V12.5007H8.83333V7.50065Z"
+          fill="#9A5B13"
+        />
+        <path
+          d="M8.83333 14.1673H10.5V15.834H8.83333V14.1673Z"
+          fill="#9A5B13"
+        />
+      </svg>
+    </SvgIcon>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
index 45c3a1405..bb7c7ecd2 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/FinalizeInspectionModalContent.tsx
@@ -98,7 +98,7 @@ export function FinalizeInspectionModalContent({
         id: inspectionId,
         finalizeInspectionRequest: { signer },
         signature,
-      }).catch();
+      });
 
       handleSubmitSuccess(phase);
     }
@@ -108,7 +108,7 @@ export function FinalizeInspectionModalContent({
     const { phase } = await finalizeInspection({
       id: inspectionId,
       finalizeInspectionRequest: {},
-    }).catch();
+    });
     handleSubmitSuccess(phase);
   }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
index ae5fff613..3ba71a3b5 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/InspectionTabExecution.tsx
@@ -124,6 +124,7 @@ export function InspectionTabExecution({
   const currentSelectedNonCoreVersions =
     getCurrentSelectedNonCoreVersions(checklists);
   const { tabs, tabsList } = createTabs(checklists);
+  const hasChecklists = checklists.length > 0;
 
   const lockedByDifferentUser =
     inspection.lockedByUser !== undefined &&
@@ -134,7 +135,9 @@ export function InspectionTabExecution({
     !inspectionIsBeforePhase(inspection.phase, ApiInspectionPhase.Executed);
 
   const [tabState, setTabState] = useState<ActiveTabState>(() => ({
-    tab: InspectionExecutionTabType.CHECKLIST,
+    tab: hasChecklists
+      ? InspectionExecutionTabType.CHECKLIST
+      : InspectionExecutionTabType.INCIDENTS,
     tabId: tabsList[0]!.SidePanelProps.tabId,
     fallbackTabId: tabsList[0]!.fallbackTabId,
   }));
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
index ed2413273..d2a27604e 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistFileElement.tsx
@@ -180,7 +180,7 @@ function ClFileCard({
       externalId: file.fileID,
       fileName: file.fileName,
       inspectionExternalId,
-    }).catch();
+    });
   }
 
   const fileType = type === "AUDIO" ? CustomFileType.Audio : ApiFileType.Jpeg;
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
index a55733216..f0b1156d0 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/ChecklistSectionElement.tsx
@@ -227,7 +227,7 @@ function ElementWrapper({
   // invalid element, the browser tries to scroll to it.
   useEffect(() => {
     if (invalidElementIds.has(elementId)) {
-      void validate().then().catch();
+      void validate();
     }
 
     async function validate() {
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
index c870fb242..e7ab2a6e9 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/InspectionTabPlanning.tsx
@@ -6,7 +6,6 @@
 import {
   ApiInspection,
   ApiInspectionAvailableCLDVersionsResponse,
-  ApiInspectionFeature,
   ApiInspectionPhase,
 } from "@eshg/employee-portal-api/inspection";
 import { useWindowDimensions } from "@eshg/lib-portal/hooks/useWindowDimension";
@@ -16,7 +15,6 @@ import { useSuspenseQueries } from "@tanstack/react-query";
 import { useUserApi } from "@/lib/baseModule/api/clients";
 import { headerHeightDesktop } from "@/lib/baseModule/components/layout/sizes";
 import { useInspectionApi } from "@/lib/businessModules/inspection/api/clients";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import {
   getAvailableCLDVsQuery,
   getInspectionQuery,
@@ -60,9 +58,6 @@ export function InspectionTabPlanning({
         ),
       ],
     });
-  const isPacklistsEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.Packlists,
-  );
 
   const theme = useTheme();
   const { width } = useWindowDimensions();
@@ -155,7 +150,6 @@ export function InspectionTabPlanning({
             lockedByDifferentUser={lockedByDifferentUser}
             hasReachedExecuting={hasReachedExecuting}
             inspection={inspection}
-            isPacklistsEnabled={isPacklistsEnabled}
           />
         </Box>
       </Box>
@@ -182,7 +176,6 @@ export function InspectionTabPlanning({
           lockedByDifferentUser={lockedByDifferentUser}
           hasReachedExecuting={hasReachedExecuting}
           inspection={inspection}
-          isPacklistsEnabled={isPacklistsEnabled}
         />
         <LeftColumnBottomElements
           isOffline={isOffline}
@@ -263,13 +256,11 @@ function RightColumnElements({
   lockedByDifferentUser,
   hasReachedExecuting,
   inspection,
-  isPacklistsEnabled,
 }: {
   isOffline: boolean;
   lockedByDifferentUser: boolean;
   hasReachedExecuting: boolean;
   inspection: ApiInspection;
-  isPacklistsEnabled: boolean;
 }) {
   return (
     <>
@@ -283,13 +274,11 @@ function RightColumnElements({
         inspection={inspection}
         facilityAddress={inspection.facility.baseFacility.contactAddress}
       />
-      {isPacklistsEnabled && (
-        <PacklistTile
-          readonly={lockedByDifferentUser && !isOffline}
-          isOffline={isOffline}
-          inspection={inspection}
-        />
-      )}
+      <PacklistTile
+        readonly={lockedByDifferentUser && !isOffline}
+        isOffline={isOffline}
+        inspection={inspection}
+      />
     </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
index 3dded7772..9e8d6690c 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionTabReportResult.tsx
@@ -36,6 +36,7 @@ export function InspectionTabReportResult({
         m={2}
         spacing={3}
         sx={{
+          flexGrow: "1",
           overflow: { xxs: "auto", lg: "hidden" },
           flexDirection: { xxs: undefined, lg: "row" },
         }}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx
new file mode 100644
index 000000000..eb16cff90
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportButton.tsx
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
+import FileUploadOutlinedIcon from "@mui/icons-material/FileUploadOutlined";
+import { Button } from "@mui/joy";
+
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
+import { useProcessImportSidebar } from "@/lib/businessModules/inspection/components/processImport/ProcessImportSidebar";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
+
+export function ProcessImportButton() {
+  const isEnabled = useIsNewFeatureEnabled(ApiInspectionFeature.Import);
+  const hasImportRole = useHasUserRoleCheck(ApiUserRole.InspectionImport);
+  const { open } = useProcessImportSidebar();
+
+  if (!isEnabled || !hasImportRole) {
+    return null;
+  }
+
+  return (
+    <Button
+      onClick={open}
+      variant="outlined"
+      startDecorator={<FileUploadOutlinedIcon />}
+    >
+      Daten Importieren
+    </Button>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx
new file mode 100644
index 000000000..aaf992c0c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportForm.tsx
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { DownloadLink } from "@eshg/lib-portal/api/files/DownloadLink";
+import { useFileDownload } from "@eshg/lib-portal/api/files/download";
+import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
+import { FileDownload } from "@mui/icons-material";
+import { Button, Stack } from "@mui/joy";
+import { Formik } from "formik";
+
+import { useImportApi } from "@/lib/businessModules/inspection/api/clients";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import { FileField } from "@/lib/shared/components/formFields/file/FileField";
+import { FileType } from "@/lib/shared/components/formFields/file/FileType";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export interface ProcessImportFormValues {
+  file: File | null;
+}
+
+export interface ProcessImportFormProps {
+  onSubmit: (values: ProcessImportFormValues) => Promise<void>;
+  onClose: () => void;
+}
+
+const INITIAL_VALUES: ProcessImportFormValues = {
+  file: null,
+};
+
+export function ProcessImportForm({
+  onSubmit: handleSubmit,
+  onClose: handleClose,
+}: Readonly<ProcessImportFormProps>) {
+  return (
+    <Formik onSubmit={handleSubmit} initialValues={INITIAL_VALUES}>
+      {({ isSubmitting }) => (
+        <SidebarForm>
+          <SidebarContent title="Daten importieren">
+            <Stack gap={2}>
+              <FileField
+                label="Wählen Sie eine XLSX-Datei aus"
+                name="file"
+                required="Datei ist erforderlich"
+                accept={FileType.Xlsx}
+              />
+              <DownloadTemplateButton />
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <ButtonBar
+              right={
+                <>
+                  <Button onClick={handleClose} variant="soft" color="neutral">
+                    Abbrechen
+                  </Button>
+                  <SubmitButton submitting={isSubmitting}>
+                    Importieren
+                  </SubmitButton>
+                </>
+              }
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
+
+function DownloadTemplateButton() {
+  const importApi = useImportApi();
+  const templateFile = useFileDownload(() =>
+    importApi.getInspectionImportTemplateRaw(),
+  );
+
+  return (
+    <DownloadLink
+      downloadContainerRef={templateFile.downloadContainerRef}
+      startDecorator={<FileDownload />}
+      fontSize="sm"
+      onDownload={() => templateFile.download()}
+      sx={{ justifyContent: "flex-start" }}
+    >
+      Beispiel-Datei herunterladen
+    </DownloadLink>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx
new file mode 100644
index 000000000..4acd4c179
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportPending.tsx
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { CircularProgress, Stack, Typography } from "@mui/joy";
+
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export function ProcessImportPending() {
+  return (
+    <SidebarContent title="Daten Importieren">
+      <Stack
+        direction="column"
+        alignItems="center"
+        gap={3}
+        sx={{ marginTop: 6 }}
+      >
+        <CircularProgress variant="plain" size="lg" />
+        <Typography level="body-xs" textAlign="center" width={0.75}>
+          Der Import kann einige Zeit in Anspruch nehmen. Bitte schließen Sie
+          dieses Fenster nicht.
+        </Typography>
+      </Stack>
+    </SidebarContent>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx
new file mode 100644
index 000000000..2391deca5
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportResult.tsx
@@ -0,0 +1,154 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download";
+import { HiddenContainer } from "@eshg/lib-portal/components/HiddenContainer";
+import type { SvgIconComponent } from "@mui/icons-material";
+import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined";
+import FileDownloadOutlined from "@mui/icons-material/FileDownloadOutlined";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import {
+  Alert,
+  Button,
+  ColorPaletteProp,
+  Sheet,
+  Stack,
+  Typography,
+} from "@mui/joy";
+import { PropsWithChildren, useRef } from "react";
+
+import { ImportProcessResult } from "@/lib/businessModules/inspection/api/mutations/processImport";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+
+export interface ProcessImportResultProps {
+  result: ImportProcessResult;
+  onClose: () => void;
+}
+
+export function ProcessImportResult({
+  result,
+  onClose: handleClose,
+}: Readonly<ProcessImportResultProps>) {
+  const { file, statistics } = result;
+
+  return (
+    <>
+      <SidebarContent title="Daten Importieren">
+        <Stack spacing={3}>
+          <Typography color="success" fontWeight="md">
+            Import erfolgreich
+          </Typography>
+          <InfoSheet>
+            <InfoText iconComponent={InfoOutlinedIcon} iconColor="primary">
+              {statistics.total} Datensätze
+            </InfoText>
+            <InfoText
+              iconComponent={ErrorOutlineOutlinedIcon}
+              iconColor="danger"
+            >
+              {statistics.duplicated} Duplikate in der Datei
+            </InfoText>
+            <InfoText
+              iconComponent={ErrorOutlineOutlinedIcon}
+              iconColor="danger"
+            >
+              {statistics.failed} Fehlerhafte Datensätze
+            </InfoText>
+          </InfoSheet>
+          <Section title="Vorgänge und Einrichtungen">
+            <Alert variant="soft" color="primary">
+              {statistics.created} Vorgänge neu angelegt
+            </Alert>
+          </Section>
+          <Section title="Bitte laden Sie die Ergebnis-Datei herunter.">
+            <FileDownload file={file} />
+          </Section>
+        </Stack>
+      </SidebarContent>
+      <SidebarActions>
+        <ButtonBar
+          right={
+            <>
+              <Button onClick={handleClose} variant="soft" color="neutral">
+                Duplikate prüfen
+              </Button>
+              <Button onClick={handleClose}>Fertig</Button>
+            </>
+          }
+        />
+      </SidebarActions>
+    </>
+  );
+}
+
+function InfoSheet({ children }: Readonly<PropsWithChildren>) {
+  return (
+    <Sheet variant="soft" sx={{ p: 3 }}>
+      <Stack spacing={3}>{children}</Stack>
+    </Sheet>
+  );
+}
+
+function InfoText({
+  iconComponent: IconComponent,
+  iconColor,
+  children,
+}: Readonly<
+  PropsWithChildren<{
+    iconComponent: SvgIconComponent;
+    iconColor: ColorPaletteProp;
+  }>
+>) {
+  return (
+    <Typography
+      startDecorator={<IconComponent color={iconColor} size="sm" />}
+      gap={2}
+      fontWeight="md"
+    >
+      {children}
+    </Typography>
+  );
+}
+
+function Section({
+  title,
+  children,
+}: Readonly<PropsWithChildren<{ title: string }>>) {
+  return (
+    <Stack gap={1}>
+      <Typography fontWeight="md">{title}</Typography>
+      {children}
+    </Stack>
+  );
+}
+
+function FileDownload({ file }: Readonly<{ file: File }>) {
+  const downloadContainerRef = useRef<HTMLDivElement>(null);
+
+  function handleDownload() {
+    const downloadContainer = downloadContainerRef.current;
+    if (downloadContainer === null) {
+      throw new Error("Download container is not initialized");
+    }
+    downloadFileAndOpen(file, downloadContainer);
+  }
+
+  return (
+    <>
+      <Button
+        onClick={handleDownload}
+        variant="soft"
+        color="warning"
+        startDecorator={<FileDownloadOutlined />}
+        sx={{ justifyContent: "flex-start" }}
+      >
+        {file.name}
+      </Button>
+      <HiddenContainer ref={downloadContainerRef} />
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx
new file mode 100644
index 000000000..6a97279b2
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/components/processImport/ProcessImportSidebar.tsx
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useImportProcess } from "@/lib/businessModules/inspection/api/mutations/processImport";
+import {
+  ProcessImportForm,
+  ProcessImportFormValues,
+} from "@/lib/businessModules/inspection/components/processImport/ProcessImportForm";
+import { ProcessImportPending } from "@/lib/businessModules/inspection/components/processImport/ProcessImportPending";
+import { ProcessImportResult } from "@/lib/businessModules/inspection/components/processImport/ProcessImportResult";
+import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext";
+import { useSidebar } from "@/lib/shared/components/drawer/useSidebar";
+
+export function useProcessImportSidebar() {
+  return useSidebar({
+    component: ProcessImportSidebar,
+  });
+}
+
+function ProcessImportSidebar({ onClose }: DrawerProps) {
+  const {
+    mutateAsync: importProcess,
+    reset,
+    status,
+    data: result,
+  } = useImportProcess();
+
+  function handleClose() {
+    reset();
+    onClose();
+  }
+
+  async function handleSubmit({ file }: ProcessImportFormValues) {
+    if (!file) {
+      throw new Error("No file selected");
+    }
+
+    await importProcess({
+      file,
+    });
+  }
+
+  switch (status) {
+    case "pending":
+      return <ProcessImportPending />;
+    case "success":
+      return <ProcessImportResult result={result} onClose={handleClose} />;
+    default:
+      return (
+        <ProcessImportForm onSubmit={handleSubmit} onClose={handleClose} />
+      );
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
index 1ff025d99..d25a8947f 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx
@@ -132,8 +132,10 @@ export function ChecklistDefinitionRepoOverviewTable() {
           <DataTable
             data={repoMetadataList}
             columns={columns}
-            rowNavRoute={getRepoOverviewRowRoute}
-            focusColumnHeader={"Name"}
+            rowNavigation={{
+              route: getRepoOverviewRowRoute,
+              focusColumnAccessorKey: "name",
+            }}
             striped
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
index dd5f763b1..872b39c7a 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/EditTextBlockSidebar.tsx
@@ -79,7 +79,7 @@ function EditTextBlockSidebarWithQueriesAndMutations({
           snackbar.confirmation("Textbaustein wurde erzeugt.");
           handleClose();
         },
-      }).catch();
+      });
     } else {
       await updateTextBlock(
         {
@@ -92,7 +92,7 @@ function EditTextBlockSidebarWithQueriesAndMutations({
             handleClose();
           },
         },
-      ).catch();
+      );
     }
   }
 
diff --git a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
index dd723a6b9..2bd96f746 100644
--- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx
@@ -86,7 +86,7 @@ export function TextBlocksTable({
       confirmLabel: "Löschen",
       color: "danger",
       onConfirm: async () => {
-        await deleteTextBlock.mutateAsync(textBlock.id ?? "").catch();
+        await deleteTextBlock.mutateAsync(textBlock.id ?? "");
       },
     });
   }
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
index 6cbd4da22..435d29901 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
+++ b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts
@@ -82,6 +82,7 @@ export const inspectionTypeNames = {
   [ApiInspectionType.Initial]: "Erstbegehung",
   [ApiInspectionType.Complaint]: "Beschwerde",
   [ApiInspectionType.DocumentInspection]: "Dokumentenprüfung",
+  [ApiInspectionType.Import]: "Import",
 } satisfies Record<ApiInspectionType, string>;
 
 export function translateInspectionType(type: ApiInspectionType) {
@@ -119,3 +120,8 @@ export function translateInspectionAnnouncement(
 ) {
   return inspectionAnnouncementNames[type];
 }
+
+export const inspectionDuplicateFilterNames = {
+  ["true"]: "Ja",
+  ["false"]: "Nein",
+} satisfies Record<string, string>;
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
index 58f8230e8..9212cb4a3 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/RegisterServiceWorker.tsx
@@ -5,6 +5,7 @@
 
 "use client";
 
+import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { getErrorDescription } from "@eshg/lib-portal/errorHandling/errorMappers";
 import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers";
@@ -14,6 +15,7 @@ import { ErrorBoundary, FallbackProps } from "react-error-boundary";
 import { isEmpty } from "remeda";
 import { Workbox, WorkboxLifecycleEvent } from "workbox-window";
 
+import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/inspection/api/queries/feature";
 import { usePrecacheInspections } from "@/lib/businessModules/inspection/shared/offline/usePrecacheInspections";
 import { useServiceWorkerMessageListeners } from "@/lib/businessModules/inspection/shared/offline/useServiceWorkerMessageListeners";
 import { LoadingOverlay } from "@/lib/shared/components/LoadingOverlay";
@@ -38,7 +40,9 @@ export function RegisterServiceWorker({
 }>) {
   const [controlling, setControlling] = useState(false);
   const queryClient = useQueryClient();
-
+  const { data: isOfflineEnabled } = useIsNewFeatureEnabledUnsuspended(
+    ApiInspectionFeature.Offline,
+  );
   useEffect(() => {
     window.workbox?.getSW().then(
       (sw) => {
@@ -50,9 +54,7 @@ export function RegisterServiceWorker({
             setControlling(false);
             break;
           case "activated":
-            window.workbox?.update().catch((reason) => {
-              throw reason;
-            });
+            void window.workbox?.update();
             handleControlling();
             break;
           case "redundant":
@@ -63,7 +65,7 @@ export function RegisterServiceWorker({
       (reason) => {
         setControlling(false);
         // eslint-disable-next-line no-console
-        console.warn("could get workbox service-worker", reason);
+        console.warn("could not get workbox service-worker", reason);
       },
     );
 
@@ -90,13 +92,13 @@ export function RegisterServiceWorker({
   // register service worker
   const hasInspections = !isEmpty(inspectionIds);
   useEffect(() => {
-    if (!workboxRegistered && hasInspections) {
+    if (isOfflineEnabled && !workboxRegistered && hasInspections) {
       window.workbox?.register().catch((reason) => {
         throw reason;
       });
       workboxRegistered = true;
     }
-  }, [hasInspections]);
+  }, [isOfflineEnabled, hasInspections]);
 
   return (
     <>
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
index d25b9450e..198e65276 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/ServiceWorkerProvider.tsx
@@ -21,6 +21,7 @@ import {
   deleteAllEncryptedCaches,
   deleteInspectionFromAllCaches,
 } from "@/lib/businessModules/inspection/shared/offline/deleteInspectionFromAllCaches";
+import { unregisterServiceWorker } from "@/lib/businessModules/inspection/shared/offline/unregisterServiceWorker";
 import {
   getInspectionIdsOfProcedureBaseDataRequests,
   useGetPrecachedInspections,
@@ -158,6 +159,11 @@ function ServiceWorkerProviderInner({
 function ServiceWorkerProviderMock({
   children,
 }: Readonly<{ children: ReactNode }>) {
+  // unregister real service worker
+  useEffect(() => {
+    unregisterServiceWorker();
+  }, []);
+
   return (
     <ServiceWorkerContext.Provider value={EMPTY_CONTEXT}>
       {children}
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx b/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx
new file mode 100644
index 000000000..77383cea7
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/unregisterServiceWorker.tsx
@@ -0,0 +1,15 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  UNREGISTER,
+  createUnregisterBroadCastChannelEndpoint,
+} from "@/serviceWorker/common/unregisterBroadCastChannel";
+
+const unregisterChannel = createUnregisterBroadCastChannelEndpoint();
+
+export function unregisterServiceWorker() {
+  unregisterChannel.postMessage(UNREGISTER);
+}
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
index f0708afcc..aeeb09ff6 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
+++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts
@@ -15,7 +15,6 @@ import {
 } from "@eshg/employee-portal-api/base";
 import {
   ApiInspection,
-  ApiInspectionFeature,
   ChecklistApi,
   EditorApi,
   FileApi,
@@ -63,7 +62,6 @@ import {
 } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys";
 import { getChecklistsQueryKey } from "@/lib/businessModules/inspection/api/queries/checklist";
 import { getDepartmentQueryKey } from "@/lib/businessModules/inspection/api/queries/department";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/inspection/api/queries/feature";
 import {
   getAvailableCLDVsQueryKey,
   getAvailablePLDRsQueryKey,
@@ -115,9 +113,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
   const fetchApprovalRequests = useHasUserRoleCheck(
     ApiUserRole.InspectionLeader,
   );
-  const isPacklistsEnabled = useIsNewFeatureEnabled(
-    ApiInspectionFeature.Packlists,
-  );
 
   const { removeFromPrecache } = useServiceWorker();
   const snackbar = useSnackbar();
@@ -142,7 +137,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
       procedureApi,
       packlistApi,
       fetchApprovalRequests,
-      isPacklistsEnabled,
     })
       .then((inspectionIdToRemove) => {
         if (inspectionIdToRemove) {
@@ -171,7 +165,6 @@ export function usePrecacheInspections(inspectionIds: string[]) {
     incidentApi,
     inspectionApi,
     inspectionFeatureTogglesApi,
-    isPacklistsEnabled,
     packlistApi,
     procedureApi,
     progressEntryApi,
@@ -201,7 +194,6 @@ async function prefetchAll({
   procedureApi,
   packlistApi,
   fetchApprovalRequests,
-  isPacklistsEnabled,
 }: {
   inspectionIds: string[];
   cacheHeaders: (inspectionId?: string) => RequestInit;
@@ -220,7 +212,6 @@ async function prefetchAll({
   procedureApi: ProcedureApi;
   packlistApi: PacklistApi;
   fetchApprovalRequests: boolean;
-  isPacklistsEnabled: boolean;
 }) {
   // 1. pre-fetch inspection procedure related queries
   for (const inspectionId of inspectionIds) {
@@ -386,21 +377,19 @@ async function prefetchAll({
     }
 
     // 1.9 pre-fetch useGetPacklists()
-    if (isPacklistsEnabled) {
-      inspPromises.push(
-        queryClient.fetchQuery({
-          queryKey: getPacklistsQueryKey(inspectionId),
-          queryFn: () => packlistApi.getPacklists(inspectionId, headers),
-        }),
-      );
-      // 1.9.1 pre-fetch useGetAvailablePLDRs()
-      inspPromises.push(
-        queryClient.fetchQuery({
-          queryKey: getAvailablePLDRsQueryKey(inspectionId),
-          queryFn: () => inspectionApi.getAvailablePLDs(inspectionId, headers),
-        }),
-      );
-    }
+    inspPromises.push(
+      queryClient.fetchQuery({
+        queryKey: getPacklistsQueryKey(inspectionId),
+        queryFn: () => packlistApi.getPacklists(inspectionId, headers),
+      }),
+    );
+    // 1.9.1 pre-fetch useGetAvailablePLDRs()
+    inspPromises.push(
+      queryClient.fetchQuery({
+        queryKey: getAvailablePLDRsQueryKey(inspectionId),
+        queryFn: () => inspectionApi.getAvailablePLDs(inspectionId, headers),
+      }),
+    );
 
     // 1.10 pre-fetch inspection related pages
     const pages = [
diff --git a/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
index fac43f18c..a23d0b931 100644
--- a/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/inspection/shared/sideNavigationItem.tsx
@@ -4,15 +4,13 @@
  */
 
 import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
-import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection";
 import { EmojiTransportation } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled as useIsNewBaseFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
-import { useIsNewFeatureEnabledUnsuspended as useIsNewInspectionFeatureEnabledUnsuspended } from "@/lib/businessModules/inspection/api/queries/feature";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./routes";
@@ -58,6 +56,11 @@ const defaultSubItems: SideNavigationSubItem[] = [
     href: routes.facilities.webSearch.index,
     accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
   },
+  {
+    name: "Packlistendefinitionen",
+    href: routes.packlists.definitions.index,
+    accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
+  },
 ];
 
 const inboxNavigationItem: SideNavigationSubItem = {
@@ -66,31 +69,20 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
 };
 
-const packlistsNavigationItem: SideNavigationSubItem = {
-  name: "Packlistendefinitionen",
-  href: routes.packlists.definitions.index,
-  accessCheck: hasUserRole(ApiUserRole.InspectionProcedureEdit),
-};
-
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewBaseFeatureEnabled(ApiBaseFeature.Inbox);
-  const { data: isPacklistsEnabled, isError: isPacklistsEnabledError } =
-    useIsNewInspectionFeatureEnabledUnsuspended(ApiInspectionFeature.Packlists);
 
-  const subItemsWithPacklists = isPacklistsEnabled
-    ? [...defaultSubItems, packlistsNavigationItem]
+  const subItems = isInboxEnabled
+    ? [...defaultSubItems, inboxNavigationItem]
     : defaultSubItems;
 
-  const subItems = isInboxEnabled
-    ? [...subItemsWithPacklists, inboxNavigationItem]
-    : subItemsWithPacklists;
-  return [
-    {
-      ...sideNavigationItem,
-      error: isPacklistsEnabledError
-        ? "Bei der Verbindung zum Begehungsmodul ist ein Fehler aufgetreten."
-        : undefined,
-      subItems,
-    },
-  ];
+  return {
+    isLoading: false,
+    items: [
+      {
+        ...sideNavigationItem,
+        subItems,
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts b/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
index 6251cad36..01278416f 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
+++ b/employee-portal/src/lib/businessModules/measlesProtection/api/mutations/procedures.ts
@@ -395,8 +395,8 @@ export function usePatchFacilityMutation({
   return useMutation({
     mutationFn: ({ facility }: PatchFacilityParams) => {
       // Todo: call backend to patch the facility
-      return new Promise<MeaslesFacility | undefined>((res) => {
-        setTimeout(() => res(facility), 1000);
+      return new Promise<MeaslesFacility | undefined>((resolve) => {
+        setTimeout(() => resolve(facility), 1000);
       });
     },
     mutationKey: measlesProtectionApiQueryKey(["procedures"]),
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
index 480dc5f27..b286517c9 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
@@ -64,13 +64,14 @@ export function CreateAppointmentBlockGroupForm({
 
   async function handleSubmit(values: AppointmentBlockGroupValues) {
     const appointmentBlockGroupValues = mapFormValues(values);
-    await createDailyAppointmentBlocksForGroup
-      .mutateAsync(appointmentBlockGroupValues, {
+    await createDailyAppointmentBlocksForGroup.mutateAsync(
+      appointmentBlockGroupValues,
+      {
         onSuccess: () => {
           router.push(routes.appointmentBlockGroups.index);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
index a0bea3ab9..555a61c3e 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/createProceduresForm/NewPersonButton.tsx
@@ -107,11 +107,9 @@ export function NewPersonButton() {
   });
 
   function createDraftProcedure(person: LegacyPerson) {
-    return createDraftProcedureMutation
-      .mutateAsync({
-        person: mapToApiAffectedPersonDetails(person),
-      })
-      .catch();
+    return createDraftProcedureMutation.mutateAsync({
+      person: mapToApiAffectedPersonDetails(person),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
index fa978a5b2..15c758cdb 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionLetterSidebar.tsx
@@ -63,16 +63,14 @@ export function AccessRestrictionLetterSidebar({ id }: { id: string }) {
       if (!isNullish(data.document)) {
         formData.append("file", data.document);
       }
-      return addAccessRestrictionLetter
-        .mutateAsync({
-          id,
-          data: {
-            recipientId: data.recipientId,
-            sentAt: new Date(data.sentAt),
-          },
-          formData,
-        })
-        .catch();
+      return addAccessRestrictionLetter.mutateAsync({
+        id,
+        data: {
+          recipientId: data.recipientId,
+          sentAt: new Date(data.sentAt),
+        },
+        formData,
+      });
     },
     [addAccessRestrictionLetter, id],
   );
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
index 0809774d7..62a08bd74 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddCustodianSidebar.tsx
@@ -52,12 +52,10 @@ export function AddCustodianSidebar({
       open={open}
       validate={validateCustodianAge}
       onSubmit={(data) =>
-        addCustodian
-          .mutateAsync({
-            procedureId: procedure.id,
-            data: mapToAddCustodianRequest(data),
-          })
-          .catch()
+        addCustodian.mutateAsync({
+          procedureId: procedure.id,
+          data: mapToAddCustodianRequest(data),
+        })
       }
       onClose={handleClose}
     />
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
index c2970814a..9e2d4af47 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AdditionalInfoSection.tsx
@@ -21,11 +21,8 @@ import {
 import { ReopenProcedureModal } from "@/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ReopenProcedureModal";
 import { useProceduresContext } from "@/lib/businessModules/measlesProtection/shared/ProceduresContext";
 import { ConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialog";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AdditionalInfoUpdateSidebar } from "./AdditionalInfoUpdateSidebar";
@@ -73,36 +70,39 @@ export function AdditionalInfoSection({
 
   return (
     <Stack rowGap={2}>
-      <DetailsCard title={"Zusatzinfos"} actionButton={editAction}>
-        <ValueList>
-          <LabeledValue
-            label="Personenstatus"
-            value={
-              procedure.affectedPerson.roleStatus
-                ? roleStatusNames[procedure.affectedPerson.roleStatus]
-                : "-"
-            }
-          />
-          <LabeledValue
-            label="Meldedatum"
-            value={formatDate(procedure.reportData?.reportingDate)}
-          />
-          <LabeledValue
-            label="Meldegrund"
-            value={
-              procedure.reportData?.reportingReason
-                ? reportingReasonNames[procedure.reportData?.reportingReason]
-                : "-"
-            }
-          />
-          {procedure.reportData?.reportingReason == ApiReportingReason.Other ? (
-            <LabeledValue
-              label="Kommentar zum Meldegrund"
-              value={procedure.reportData?.commentReportingReason}
+      <Sheet>
+        <DetailsSection title={"Zusatzinfos"} buttons={editAction}>
+          <Stack gap={1}>
+            <DetailsCell
+              label="Personenstatus"
+              value={
+                procedure.affectedPerson.roleStatus
+                  ? roleStatusNames[procedure.affectedPerson.roleStatus]
+                  : "-"
+              }
+            />
+            <DetailsCell
+              label="Meldedatum"
+              value={formatDate(procedure.reportData?.reportingDate)}
             />
-          ) : null}
-        </ValueList>
-      </DetailsCard>
+            <DetailsCell
+              label="Meldegrund"
+              value={
+                procedure.reportData?.reportingReason
+                  ? reportingReasonNames[procedure.reportData?.reportingReason]
+                  : "-"
+              }
+            />
+            {procedure.reportData?.reportingReason ==
+            ApiReportingReason.Other ? (
+              <DetailsCell
+                label="Kommentar zum Meldegrund"
+                value={procedure.reportData?.commentReportingReason}
+              />
+            ) : null}
+          </Stack>
+        </DetailsSection>
+      </Sheet>
       <Sheet component="section">
         {!procedure.isOpen ? (
           <Button color="danger" onClick={handleReopenProcedure} fullWidth>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
index f924631cd..0d9ac0b59 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AddressDetails.tsx
@@ -8,11 +8,9 @@ import {
   ApiPostboxAddress,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { Row } from "@eshg/lib-portal/components/Row";
+import { Stack } from "@mui/joy";
 
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { BaseAddress } from "@/lib/shared/helpers/address";
 import { translateCountry } from "@/lib/shared/helpers/i18n";
 
@@ -22,7 +20,7 @@ interface AddressDetailsProps {
 
 export function AddressDetails({ address }: AddressDetailsProps) {
   if (address == null) {
-    return <ValueList></ValueList>;
+    return null;
   }
 
   if (address.type === "PostboxAddress") {
@@ -38,30 +36,30 @@ function streetAndHouseNumber(street: string, houseNumber?: string): string {
 
 function DomesticAddressDetails(address: ApiDomesticAddress) {
   return (
-    <ValueList>
-      <LabeledValue
+    <Stack gap={1}>
+      <DetailsCell
         label="Straße und Haus Nr."
         value={streetAndHouseNumber(address.street, address.houseNumber)}
       />
-      <LabeledValue label="Adresszusatz" value={address.addressAddition} />
+      <DetailsCell label="Adresszusatz" value={address.addressAddition} />
       <Row columnGap={3} justifyContent="start">
-        <LabeledValue label="Postleitzahl" value={address.postalCode} />
-        <LabeledValue label="Ort" value={address.city} />
+        <DetailsCell label="Postleitzahl" value={address.postalCode} />
+        <DetailsCell label="Ort" value={address.city} />
       </Row>
-      <LabeledValue label="Land" value={translateCountry(address.country)} />
-    </ValueList>
+      <DetailsCell label="Land" value={translateCountry(address.country)} />
+    </Stack>
   );
 }
 
 function PostboxAddressDetails(address: ApiPostboxAddress) {
   return (
-    <ValueList>
-      <LabeledValue label="Postfach" value={address.postbox} />
+    <Stack gap={1}>
+      <DetailsCell label="Postfach" value={address.postbox} />
       <Row columnGap={3} justifyContent="start">
-        <LabeledValue label="Postleitzahl" value={address.postalCode} />
-        <LabeledValue label="Ort" value={address.city} />
+        <DetailsCell label="Postleitzahl" value={address.postalCode} />
+        <DetailsCell label="Ort" value={address.city} />
       </Row>
-      <LabeledValue label="Land" value={translateCountry(address.country)} />
-    </ValueList>
+      <DetailsCell label="Land" value={translateCountry(address.country)} />
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
index bdd5fc3bd..3ec89b989 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AffectedPerson.tsx
@@ -9,10 +9,11 @@ import {
   ApiDraftMeaslesProcedure,
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const COLUMN_STYLE: SxProps = {
   flexGrow: 1,
@@ -28,14 +29,16 @@ export function AffectedPerson({
   const person = procedure.affectedPerson;
 
   return (
-    <DetailsCard title={title}>
-      <CentralFilePersonDetails
-        person={{
-          ...person,
-          contactAddress: person.address,
-        }}
-        columnSx={COLUMN_STYLE}
-      />
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title={title}>
+        <CentralFilePersonDetails
+          person={{
+            ...person,
+            contactAddress: person.address,
+          }}
+          columnSx={COLUMN_STYLE}
+        />
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
index a42233222..5f0bb861f 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AppointmentSidebar.tsx
@@ -37,17 +37,15 @@ export function AddAppointmentSidebar({ id }: { id: string }) {
   const bookAppointmentForProcedure = useBookAppointmentForProcedure();
 
   async function handleSubmit(data: typeof initialValues) {
-    await bookAppointmentForProcedure
-      .mutateAsync(
-        {
-          procedureId: id,
-          apiBookAppointmentRequest: mapRequiredValue(data.appointment),
-        },
-        {
-          onSuccess: handleClose,
-        },
-      )
-      .catch();
+    await bookAppointmentForProcedure.mutateAsync(
+      {
+        procedureId: id,
+        apiBookAppointmentRequest: mapRequiredValue(data.appointment),
+      },
+      {
+        onSuccess: handleClose,
+      },
+    );
   }
 
   function handleClose() {
@@ -72,17 +70,15 @@ export function EditAppointmentSidebar({ id }: { id: string }) {
   }
 
   async function handleSubmit(data: typeof initialValues) {
-    await bookAppointmentForProcedure
-      .mutateAsync(
-        {
-          procedureId: id,
-          apiBookAppointmentRequest: mapRequiredValue(data.appointment),
-        },
-        {
-          onSuccess: handleClose,
-        },
-      )
-      .catch();
+    await bookAppointmentForProcedure.mutateAsync(
+      {
+        procedureId: id,
+        apiBookAppointmentRequest: mapRequiredValue(data.appointment),
+      },
+      {
+        onSuccess: handleClose,
+      },
+    );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
index 6d633fc03..e651bb540 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Custodians.tsx
@@ -7,10 +7,11 @@ import {
   ApiDraftMeaslesProcedure,
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const COLUMN_STYLE: SxProps = {
   flexGrow: 1,
@@ -29,17 +30,16 @@ export function Custodians({
   const custodians = procedure.custodians ?? [];
 
   return custodians.map((person, index) => (
-    <DetailsCard
-      key={`custodian-${index}`}
-      title="PSB - Personensorgeberechtigte:r"
-    >
-      <CentralFilePersonDetails
-        person={{
-          ...person,
-          contactAddress: person.address,
-        }}
-        columnSx={COLUMN_STYLE}
-      />
-    </DetailsCard>
+    <Sheet key={`custodian-${index}`}>
+      <DetailsSection title="PSB - Personensorgeberechtigte:r">
+        <CentralFilePersonDetails
+          person={{
+            ...person,
+            contactAddress: person.address,
+          }}
+          columnSx={COLUMN_STYLE}
+        />
+      </DetailsSection>
+    </Sheet>
   ));
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
index c905ca7d0..36c868d7d 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditAccessRestrictionSidebar.tsx
@@ -17,7 +17,7 @@ import { useCallback } from "react";
 
 import { useUpdateAccessRestrictionMutation } from "@/lib/businessModules/measlesProtection/api/mutations/procedures";
 import { DateAndButtonRow } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/DateAndButtonRow";
-import { LabeledValue } from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
 import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar";
@@ -109,11 +109,11 @@ export function EditAccessRestrictionSidebarForm({
       <SidebarForm onSubmit={handleRawSubmit}>
         <SidebarContent title={"Betretungsverbot bearbeiten"}>
           <Stack gap={3}>
-            <LabeledValue
+            <DetailsCell
               label={fields.restrictionIssuedDate.label}
               value={formatDate(accessRestriction.restrictionIssuedDate)}
             />
-            <LabeledValue
+            <DetailsCell
               label={fields.restrictionStartDate.label}
               value={formatDate(accessRestriction.restrictionStartDate)}
             />
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
index 10b7bf50d..ab69f31b6 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/EditFacilitySidebar.tsx
@@ -45,12 +45,9 @@ export function EditFacilitySidebar({
   const patchFacilityMutation = usePatchFacilityMutation();
 
   function patchFacility(data: MeaslesFacility) {
-    return patchFacilityMutation
-      .mutateAsync({ facility: data })
-      .then(() => {
-        snackbar.confirmation("Einrichtung erfolgreich geändert.");
-      })
-      .catch();
+    return patchFacilityMutation.mutateAsync({ facility: data }).then(() => {
+      snackbar.confirmation("Einrichtung erfolgreich geändert.");
+    });
   }
 
   if (!facility) {
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
index a45a9b99a..029167e8d 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/Facility.tsx
@@ -8,12 +8,13 @@ import {
   ApiMeaslesProtectionProcedure,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards";
+import { Sheet } from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 
 import { facilityTypeNames } from "@/lib/businessModules/measlesProtection/components/procedures/constants";
 import { CentralFileFacilityDetails } from "@/lib/shared/components/centralFile/display/CentralFileFacilityDetails";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
 import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 import { FacilityContacts } from "./FacilityContact";
 
@@ -36,32 +37,36 @@ export function Facility({
   return (
     procedure.facility && (
       <>
-        <DetailsCard title="Einrichtung">
-          <CentralFileFacilityDetails
-            facility={{
-              ...procedure.facility,
-              // TODO: The API type here is wrong, these should both be lists
-              emailAddresses: isNonEmptyString(procedure.facility.emailAddress)
-                ? [procedure.facility.emailAddress]
-                : [],
-              phoneNumbers: isNonEmptyString(procedure.facility.phoneNumber)
-                ? [procedure.facility.phoneNumber]
-                : [],
-            }}
-            columnSx={COLUMN_STYLE}
-          >
-            <DetailsCell
-              name="type"
-              label="Einrichtungsart"
-              value={facilityTypeNames[facility.type]}
-            />
-            <DetailsCell
-              name="extra_type"
-              label="Anderer Einrichtungstyp"
-              value={facility.otherFacilityTypeInformation}
-            />
-          </CentralFileFacilityDetails>
-        </DetailsCard>
+        <Sheet>
+          <DetailsSection title="Einrichtung">
+            <CentralFileFacilityDetails
+              facility={{
+                ...procedure.facility,
+                // TODO: The API type here is wrong, these should both be lists
+                emailAddresses: isNonEmptyString(
+                  procedure.facility.emailAddress,
+                )
+                  ? [procedure.facility.emailAddress]
+                  : [],
+                phoneNumbers: isNonEmptyString(procedure.facility.phoneNumber)
+                  ? [procedure.facility.phoneNumber]
+                  : [],
+              }}
+              columnSx={COLUMN_STYLE}
+            >
+              <DetailsCell
+                name="type"
+                label="Einrichtungsart"
+                value={facilityTypeNames[facility.type]}
+              />
+              <DetailsCell
+                name="extra_type"
+                label="Anderer Einrichtungstyp"
+                value={facility.otherFacilityTypeInformation}
+              />
+            </CentralFileFacilityDetails>
+          </DetailsSection>
+        </Sheet>
         <FacilityContacts persons={facility.contactPersons} />
       </>
     )
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
index 7c8bd6701..4e44a119e 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/FacilityContact.tsx
@@ -5,13 +5,14 @@
 
 import { ApiFacilityContactPerson } from "@eshg/employee-portal-api/measlesProtection";
 import { Row } from "@eshg/lib-portal/components/Row";
-import { Grid } from "@mui/joy";
+import { Grid, Sheet, Stack } from "@mui/joy";
 
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+  ExternalLinkDetailsCell,
+  emailHref,
+} from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell";
 import { SALUTATION_VALUES } from "@/lib/shared/components/personSidebar/constants";
 
 export function FacilityContact({
@@ -20,29 +21,31 @@ export function FacilityContact({
   person: ApiFacilityContactPerson;
 }) {
   return (
-    <DetailsCard title="Kontaktperson der Einrichtung">
-      <ValueList>
-        <Row>
-          <LabeledValue
-            label="Anrede"
-            value={person.salutation && SALUTATION_VALUES[person.salutation]}
+    <Sheet>
+      <DetailsSection title="Kontaktperson der Einrichtung">
+        <Stack gap={1}>
+          <Row>
+            <DetailsCell
+              label="Anrede"
+              value={person.salutation && SALUTATION_VALUES[person.salutation]}
+            />
+            <DetailsCell label="Titel" value={person.title} />
+          </Row>
+          <Row>
+            <DetailsCell label="Vorname" value={person.firstName} />
+            <DetailsCell label="Name" value={person.lastName} />
+          </Row>
+        </Stack>
+        <Stack gap={1}>
+          <ExternalLinkDetailsCell
+            label="E-Mail-Adresse"
+            value={person.emailAddress}
+            href={emailHref}
           />
-          <LabeledValue label="Titel" value={person.title} />
-        </Row>
-        <Row>
-          <LabeledValue label="Vorname" value={person.firstName} />
-          <LabeledValue label="Name" value={person.lastName} />
-        </Row>
-      </ValueList>
-      <ValueList>
-        <LabeledValue
-          label="E-Mail-Adresse"
-          value={person.emailAddress}
-          href={`mailto:${person.emailAddress}`}
-        />
-        <LabeledValue label="Telefonnummer" value={person.phoneNumber} />
-      </ValueList>
-    </DetailsCard>
+          <DetailsCell label="Telefonnummer" value={person.phoneNumber} />
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
index 2da7bdd9d..564acadb2 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewCustodianButton.tsx
@@ -10,10 +10,10 @@ import {
   ApiCustodianDetails,
 } from "@eshg/employee-portal-api/measlesProtection";
 import { Add } from "@mui/icons-material";
-import { Button } from "@mui/joy";
+import { Button, Sheet } from "@mui/joy";
 
 import { mapToApiPersonAddress } from "@/lib/businessModules/measlesProtection/shared/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import {
   LegacyPerson,
   LegacyPersonFormConfig,
@@ -68,14 +68,18 @@ export function mapToAddCustodianRequest(
 export function NewCustodianButton() {
   const [_, setAddCustodianOpen] = useSearchParam("add-custodian", "boolean");
   return (
-    <DetailsCard title={"PSB - Personensorgeberechtigte:r"}>
-      <Button
-        startDecorator={<Add />}
-        variant="plain"
-        onClick={() => setAddCustodianOpen(true)}
-      >
-        Hinzufügen
-      </Button>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title={"PSB - Personensorgeberechtigte:r"}>
+        <div>
+          <Button
+            startDecorator={<Add />}
+            variant="plain"
+            onClick={() => setAddCustodianOpen(true)}
+          >
+            Hinzufügen
+          </Button>
+        </div>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
index 0e3586a75..2ee40662e 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilityButton.tsx
@@ -6,23 +6,27 @@
 "use client";
 
 import { Add } from "@mui/icons-material";
-import { Button } from "@mui/joy";
+import { Button, Sheet } from "@mui/joy";
 
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 export function NewFacilityButton() {
   const [_open, setOpen] = useSearchParam("new-facility", "boolean");
 
   return (
-    <DetailsCard title="Einrichtung">
-      <Button
-        startDecorator={<Add />}
-        variant="plain"
-        onClick={() => setOpen(true)}
-      >
-        Hinzufügen
-      </Button>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection title="Einrichtung">
+        <div>
+          <Button
+            startDecorator={<Add />}
+            variant="plain"
+            onClick={() => setOpen(true)}
+          >
+            Hinzufügen
+          </Button>
+        </div>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
index a6dfca5fd..527d41821 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/NewFacilitySidebar.tsx
@@ -29,9 +29,7 @@ export function NewFacilitySidebar({
   });
 
   async function handleSaveFacility(facility: MeaslesFacility) {
-    return addFacility
-      .mutateAsync({ procedureId: procedure.id, facility })
-      .catch();
+    return addFacility.mutateAsync({ procedureId: procedure.id, facility });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
index ff809d124..50b72c241 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/ProofTab.tsx
@@ -16,7 +16,7 @@ import {
 import { Row } from "@eshg/lib-portal/components/Row";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { Add } from "@mui/icons-material";
-import { Button, Grid, Stack } from "@mui/joy";
+import { Button, Grid, Sheet, Stack } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
 
 import {
@@ -38,11 +38,8 @@ import {
   formatName,
   getPersonByIdFromProcedure,
 } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { AccessRestrictionSidebar } from "./AccessRestrictionSidebar";
@@ -167,39 +164,41 @@ function ProofSubmissionsCard({
   procedureClosed,
 }: Readonly<ProofSubmissionsProps>) {
   return (
-    <DetailsCard title="Nachweisvorlage" fullHeight={true}>
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {proofSubmissions.map((proof) => (
-          <ProofTabEntry key={proof.externalId}>
-            <LabeledValue
-              label="Resultat"
-              value={submissionResultLabels[proof.submissionResult]}
-            />
-            <Row>
-              {proof.submissionResult ===
-              ApiSubmissionResult.TempMedicalAttest ? (
-                <LabeledValue
-                  label="Frist zum medizinischen Attest"
-                  value={formatDate(proof.medicalAttestDeadline)}
-                />
-              ) : null}
-              <LabeledValue
-                label="Vorlagedatum"
-                value={formatDate(proof.submissionDate)}
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title="Nachweisvorlage">
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {proofSubmissions.map((proof) => (
+            <ProofTabEntry key={proof.externalId}>
+              <DetailsCell
+                label="Resultat"
+                value={submissionResultLabels[proof.submissionResult]}
               />
-            </Row>
-            {proof.proofSubmissionDocumentId && (
-              <ProofTabFileCard fileId={proof.proofSubmissionDocumentId} />
-            )}
-          </ProofTabEntry>
-        ))}
-        {!procedureClosed && (
-          <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
-            Hinzufügen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+              <Row>
+                {proof.submissionResult ===
+                ApiSubmissionResult.TempMedicalAttest ? (
+                  <DetailsCell
+                    label="Frist zum medizinischen Attest"
+                    value={formatDate(proof.medicalAttestDeadline)}
+                  />
+                ) : null}
+                <DetailsCell
+                  label="Vorlagedatum"
+                  value={formatDate(proof.submissionDate)}
+                />
+              </Row>
+              {proof.proofSubmissionDocumentId && (
+                <ProofTabFileCard fileId={proof.proofSubmissionDocumentId} />
+              )}
+            </ProofTabEntry>
+          ))}
+          {!procedureClosed && (
+            <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
+              Hinzufügen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
@@ -215,31 +214,33 @@ function FineCard({
   procedureClosed,
 }: Readonly<FineCardProps>) {
   return (
-    <DetailsCard title="Bußgeld" fullHeight={true}>
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {monetaryFines.length > 0 && (
-          <ValueList style={{ flexBasis: "auto" }}>
-            {monetaryFines.map((fine) => (
-              <LabeledValue
-                key={fine.externalId}
-                label="Erteilungsdatum"
-                value={formatDate(fine.fineIssuedDate)}
-              />
-            ))}
-          </ValueList>
-        )}
-        {!procedureClosed && (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            disabled={procedureClosed}
-            onClick={onClick}
-          >
-            Bußgeld erteilen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title="Bußgeld">
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {monetaryFines.length > 0 && (
+            <Stack gap={1} style={{ flexBasis: "auto" }}>
+              {monetaryFines.map((fine) => (
+                <DetailsCell
+                  key={fine.externalId}
+                  label="Erteilungsdatum"
+                  value={formatDate(fine.fineIssuedDate)}
+                />
+              ))}
+            </Stack>
+          )}
+          {!procedureClosed && (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              disabled={procedureClosed}
+              onClick={onClick}
+            >
+              Bußgeld erteilen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
 
@@ -257,39 +258,41 @@ function ProofRequestLetterCard({
   proofSubmissionLetters,
 }: Readonly<ProofRequestLetterCardProps>) {
   return (
-    <DetailsCard title={"Anschreiben Nachweisvorlage"} fullHeight={true}>
-      <Stack spacing={3} width={"100%"} alignItems={"start"}>
-        {proofSubmissionLetters.map((letter, index) => (
-          <ProofTabEntry rowLayout key={index}>
-            <LabeledValue
-              label="Empfänger"
-              value={formatName(
-                getPersonByIdFromProcedure(letter.recipientId, procedure),
-              )}
-            />
-            <LabeledValue
-              label="Versanddatum"
-              value={formatDate(letter.pdf.createdAt)}
-            />
-            <LabeledValue label="Frist" value={formatDate(letter.deadline)} />
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection title={"Anschreiben Nachweisvorlage"}>
+        <Stack spacing={3} width={"100%"} alignItems={"start"}>
+          {proofSubmissionLetters.map((letter, index) => (
+            <ProofTabEntry rowLayout key={index}>
+              <DetailsCell
+                label="Empfänger"
+                value={formatName(
+                  getPersonByIdFromProcedure(letter.recipientId, procedure),
+                )}
+              />
+              <DetailsCell
+                label="Versanddatum"
+                value={formatDate(letter.pdf.createdAt)}
+              />
+              <DetailsCell label="Frist" value={formatDate(letter.deadline)} />
 
-            <ProofTabFileCard
-              fileId={letter.pdf.fileId}
-              fileData={letter.pdf}
-            />
-          </ProofTabEntry>
-        ))}
-        {!procedureClosed && (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            disabled={procedureClosed}
-            onClick={onClick}
-          >
-            Anschreiben erstellen
-          </Button>
-        )}
-      </Stack>
-    </DetailsCard>
+              <ProofTabFileCard
+                fileId={letter.pdf.fileId}
+                fileData={letter.pdf}
+              />
+            </ProofTabEntry>
+          ))}
+          {!procedureClosed && (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              disabled={procedureClosed}
+              onClick={onClick}
+            >
+              Anschreiben erstellen
+            </Button>
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
index 8edf47f84..18e3bc7b9 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/UpdateProcedureSection.tsx
@@ -16,7 +16,7 @@ import { Formik, useFormikContext } from "formik";
 import { PropsWithChildren, useCallback } from "react";
 
 import { WrappedSelectField } from "@/lib/businessModules/measlesProtection/shared/WrappedSelectField";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 import {
   OtherComment,
@@ -102,11 +102,13 @@ export function UpdateProcedureSection({
       initialValues={initialValues}
     >
       <Stack rowGap={3}>
-        <DetailsCard title={title}>
-          <Stack gap={2} width="100%">
-            <UpdateProcedureSectionFields errorMessages={errorMessages} />
-          </Stack>
-        </DetailsCard>
+        <Sheet>
+          <DetailsSection title={title}>
+            <Stack gap={2} width="100%">
+              <UpdateProcedureSectionFields errorMessages={errorMessages} />
+            </Stack>
+          </DetailsSection>
+        </Sheet>
         <EditActions isDraft={isDraft} isOpen={!procedureClosed} />
       </Stack>
     </ProcedureForm>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
index 002b85891..0096db1da 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AccessRestrictionCard.tsx
@@ -10,7 +10,7 @@ import {
 } from "@eshg/employee-portal-api/measlesProtection";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
 import { Add, EditOutlined } from "@mui/icons-material";
-import { Button, IconButton, Stack } from "@mui/joy";
+import { Button, IconButton, Sheet, Stack } from "@mui/joy";
 
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/measlesProtection/api/queries/featureTogglesApi";
 import { ACCESS_RESTRICTION_FIELDS } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/AccessRestrictionSidebar";
@@ -19,11 +19,8 @@ import {
   formatName,
   getPersonByIdFromProcedure,
 } from "@/lib/businessModules/measlesProtection/components/procedures/procedureDetails/helpers";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 import { ProofTabEntry } from "./ProofTabEntry";
@@ -55,77 +52,83 @@ export function AccessRestrictionCard({
   );
 
   return (
-    <DetailsCard
-      title="Betretungsverbot"
-      fullHeight={true}
-      {...(isEditAccessRestrictionEnabled &&
-        !procedureClosed &&
-        accessRestriction && {
-          actionButton: (
-            <IconButton
-              color="primary"
-              variant="outlined"
-              onClick={() => setEditOpen(true)}
-            >
-              <EditOutlined />
-            </IconButton>
-          ),
-        })}
-    >
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {accessRestriction ? (
-          <>
-            <ValueList rowLayout>
-              <LabeledValue
-                label={fields.restrictionIssuedDate.label}
-                value={formatDate(accessRestriction.restrictionIssuedDate)}
-              />
-              <LabeledValue
-                label={fields.restrictionStartDate.label}
-                value={formatDate(accessRestriction.restrictionStartDate)}
-              />
-              {accessRestriction.restrictionTerminationDate && (
-                <LabeledValue
-                  label={fields.restrictionTerminationDate.label}
-                  value={formatDate(
-                    accessRestriction.restrictionTerminationDate,
-                  )}
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection
+        title="Betretungsverbot"
+        {...(isEditAccessRestrictionEnabled &&
+          !procedureClosed &&
+          accessRestriction && {
+            buttons: (
+              <IconButton
+                aria-label="Betretungsverbot bearbeiten"
+                color="primary"
+                variant="outlined"
+                onClick={() => setEditOpen(true)}
+              >
+                <EditOutlined />
+              </IconButton>
+            ),
+          })}
+      >
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {accessRestriction ? (
+            <>
+              <Stack gap={3} flexDirection={"row"}>
+                <DetailsCell
+                  label={fields.restrictionIssuedDate.label}
+                  value={formatDate(accessRestriction.restrictionIssuedDate)}
                 />
-              )}
-            </ValueList>
-            {accessRestriction.letters?.map((letter) => (
-              <ProofTabEntry key={letter.externalId}>
-                <LabeledValue label="" value="Anschreiben" />
-                <LabeledValue
-                  label="Empfänger"
-                  value={formatName(
-                    getPersonByIdFromProcedure(letter.recipientId, procedure),
-                  )}
-                  sx={{ width: "100%" }}
+                <DetailsCell
+                  label={fields.restrictionStartDate.label}
+                  value={formatDate(accessRestriction.restrictionStartDate)}
                 />
-                {letter.documentFileId && (
-                  <ProofTabFileCard fileId={letter.documentFileId} />
+                {accessRestriction.restrictionTerminationDate && (
+                  <DetailsCell
+                    label={fields.restrictionTerminationDate.label}
+                    value={formatDate(
+                      accessRestriction.restrictionTerminationDate,
+                    )}
+                  />
                 )}
-              </ProofTabEntry>
-            ))}
-            {!procedureClosed && (
+              </Stack>
+              {accessRestriction.letters?.map((letter) => (
+                <ProofTabEntry key={letter.externalId}>
+                  <DetailsCell label="" value="Anschreiben" />
+                  <DetailsCell
+                    label="Empfänger"
+                    value={formatName(
+                      getPersonByIdFromProcedure(letter.recipientId, procedure),
+                    )}
+                    sx={{ width: "100%" }}
+                  />
+                  {letter.documentFileId && (
+                    <ProofTabFileCard fileId={letter.documentFileId} />
+                  )}
+                </ProofTabEntry>
+              ))}
+              {!procedureClosed && (
+                <Button
+                  variant="plain"
+                  startDecorator={<Add />}
+                  onClick={onClickAddLetter}
+                >
+                  Anschreiben hinzufügen
+                </Button>
+              )}
+            </>
+          ) : (
+            !procedureClosed && (
               <Button
                 variant="plain"
                 startDecorator={<Add />}
-                onClick={onClickAddLetter}
+                onClick={onClick}
               >
-                Anschreiben hinzufügen
+                Betretungsverbot erteilen
               </Button>
-            )}
-          </>
-        ) : (
-          !procedureClosed && (
-            <Button variant="plain" startDecorator={<Add />} onClick={onClick}>
-              Betretungsverbot erteilen
-            </Button>
-          )
-        )}
-      </Stack>
-    </DetailsCard>
+            )
+          )}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
index 5609b5c03..9bac42e1b 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/AppointmentCard.tsx
@@ -6,7 +6,7 @@
 import { ApiAppointment } from "@eshg/employee-portal-api/measlesProtection";
 import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime";
 import { Add, DeleteOutline, EditOutlined } from "@mui/icons-material";
-import { Button, Stack } from "@mui/joy";
+import { Button, Sheet, Stack } from "@mui/joy";
 
 import { useDeleteAppointmentForProcedure } from "@/lib/businessModules/measlesProtection/api/mutations/appointmentBookingApi";
 import {
@@ -14,11 +14,8 @@ import {
   ActionsMenu,
 } from "@/lib/shared/components/buttons/ActionsMenu";
 import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam";
 
 export interface AppointmentCardProps {
@@ -67,47 +64,51 @@ export function AppointmentCard({
     },
   ];
   return (
-    <DetailsCard
-      title="Termin"
-      fullHeight={true}
-      actionButton={
-        appointment && (
-          <ActionsMenu
-            actionItems={appointmentCardActions}
-            aria-label="Aktionen"
-            sx={{
-              border: (theme) =>
-                `1px solid ${theme.palette.primary.outlinedBorder}`,
-            }}
-          />
-        )
-      }
-    >
-      <Stack spacing={3} alignItems={"start"} width={"100%"}>
-        {appointment ? (
-          <ValueList style={{ flexBasis: "auto" }}>
-            <LabeledValue label="Datum" value={formatDate(appointment.start)} />
-            <LabeledValue
-              label="Zeitraum"
-              value={
-                "Von " +
-                formatTime(appointment.start) +
-                " Uhr bis " +
-                formatTime(appointment.end) +
-                " Uhr"
-              }
+    <Sheet sx={{ height: "100%" }}>
+      <DetailsSection
+        title="Termin"
+        buttons={
+          appointment && (
+            <ActionsMenu
+              actionItems={appointmentCardActions}
+              aria-label="Aktionen"
+              sx={{
+                border: (theme) =>
+                  `1px solid ${theme.palette.primary.outlinedBorder}`,
+              }}
             />
-          </ValueList>
-        ) : !procedureClosed ? (
-          <Button
-            variant="plain"
-            startDecorator={<Add />}
-            onClick={() => setAddingAppointment(true)}
-          >
-            Hinzufügen
-          </Button>
-        ) : null}
-      </Stack>
-    </DetailsCard>
+          )
+        }
+      >
+        <Stack spacing={3} alignItems={"start"} width={"100%"}>
+          {appointment ? (
+            <Stack gap={1} style={{ flexBasis: "auto" }}>
+              <DetailsCell
+                label="Datum"
+                value={formatDate(appointment.start)}
+              />
+              <DetailsCell
+                label="Zeitraum"
+                value={
+                  "Von " +
+                  formatTime(appointment.start) +
+                  " Uhr bis " +
+                  formatTime(appointment.end) +
+                  " Uhr"
+                }
+              />
+            </Stack>
+          ) : !procedureClosed ? (
+            <Button
+              variant="plain"
+              startDecorator={<Add />}
+              onClick={() => setAddingAppointment(true)}
+            >
+              Hinzufügen
+            </Button>
+          ) : null}
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
index c6fefe79d..1a53c8096 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/procedureDetails/proof/ProofTabEntry.tsx
@@ -3,10 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { Stack } from "@mui/joy";
 import { ReactNode } from "react";
 
-import { ValueList } from "@/lib/shared/components/detailsCard/LabeledValue";
-
 interface ProofTabEntryProps {
   children: ReactNode;
   rowLayout?: boolean;
@@ -16,8 +15,9 @@ export function ProofTabEntry({
   rowLayout,
 }: Readonly<ProofTabEntryProps>) {
   return (
-    <ValueList
-      rowLayout={rowLayout}
+    <Stack
+      gap={3}
+      flexDirection={rowLayout ? "row" : "column"}
       sx={(theme) => ({
         flexBasis: "auto",
         background: theme.palette.background.level1,
@@ -26,9 +26,10 @@ export function ProofTabEntry({
           xxs: theme.spacing(2),
         },
         width: "100%",
+        flexWrap: "wrap",
       })}
     >
       {children}
-    </ValueList>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
index 1fd587b24..f1c78a772 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/components/procedures/proceduresTable/ProceduresTable.tsx
@@ -226,10 +226,11 @@ export function ProceduresTable() {
               onReopenProcedure: (procedureId) =>
                 openProcedureReopenModal(procedureId),
             })}
-            rowNavRoute={({ original: { id: procedureId } }) =>
-              routes.procedures.details(procedureId).index
-            }
-            focusColumnHeader="affectedPerson.lastName"
+            rowNavigation={{
+              route: ({ original: { id: procedureId } }) =>
+                routes.procedures.details(procedureId).index,
+              focusColumnAccessorKey: "affectedPerson.lastName",
+            }}
           />
         </TableSheet>
       </TablePage>
diff --git a/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
index c94191596..ccbfd7a1c 100644
--- a/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/measlesProtection/shared/sideNavigationItem.tsx
@@ -8,8 +8,8 @@ import { Coronavirus } from "@mui/icons-material";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
@@ -39,10 +39,10 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.MeaslesProtectionAdmin),
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
   const subItems = isInboxEnabled
     ? [...defaultSubItems, inboxNavigationItem]
     : defaultSubItems;
-  return [{ ...sideNavigationItem, subItems }];
+  return { isLoading: false, items: [{ ...sideNavigationItem, subItems }] };
 }
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts
new file mode 100644
index 000000000..f678cc9b7
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  Configuration,
+  MedicalRegistryApi,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider";
+
+function useConfiguration() {
+  const configurationParameters = useApiConfiguration(
+    "PUBLIC_MEDICAL_REGISTRY_BACKEND_URL",
+  );
+  return new Configuration(configurationParameters);
+}
+
+export function useMedicalRegistryApi() {
+  const config = useConfiguration();
+  return new MedicalRegistryApi(config);
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts
new file mode 100644
index 000000000..2edad3590
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys.ts
@@ -0,0 +1,12 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { queryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory";
+
+const apiQueryKey = queryKeyFactory(["medicalRegistry"]);
+
+export const medicalRegistryApiQueryKey = queryKeyFactory(
+  apiQueryKey(["medicalRegistryApi"]),
+);
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts
new file mode 100644
index 000000000..4d72782fe
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiProcedureStatus } from "@eshg/employee-portal-api/businessProcedures";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients";
+
+import { medicalRegistryApiQueryKey } from "./apiQueryKeys";
+
+interface PageRequest {
+  pageNumber?: number;
+  pageSize?: number;
+  filterByCertificateRequested?: boolean;
+  filterByStatus?: Set<ApiProcedureStatus>;
+}
+
+export function useGetMedicalRegistryProcedureOverviewQuery(page: PageRequest) {
+  const medicalRegistryApi = useMedicalRegistryApi();
+
+  return useSuspenseQuery({
+    queryFn: ({ signal }) =>
+      medicalRegistryApi.getProcedureOverview(
+        page.pageSize,
+        page.pageNumber,
+        page.filterByCertificateRequested,
+        page.filterByStatus,
+
+        { signal },
+      ),
+
+    queryKey: medicalRegistryApiQueryKey([
+      "getProcedureOverview",
+      page,
+      Array.from(page.filterByStatus ?? []),
+    ]),
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx
new file mode 100644
index 000000000..ab16592eb
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip.tsx
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiMedicalRegistryEntry,
+  ApiProcedureStatus,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { Chip } from "@mui/joy";
+
+interface MedicalRegistryProcedureChipProps {
+  procedure: ApiMedicalRegistryEntry;
+}
+
+export function MedicalRegistryProcedureChip({
+  procedure,
+}: MedicalRegistryProcedureChipProps) {
+  if (procedure.status === ApiProcedureStatus.Closed) {
+    return <Chip color="success">Geschlossen</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_ENTRY") {
+    return <Chip color="neutral">Offen</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_CITIZEN_DRAFT") {
+    return <Chip color="danger">Externer Entwurf</Chip>;
+  }
+  if (procedure.type === "MEDICAL_REGISTRY_EMPLOYEE_DRAFT") {
+    return <Chip color="warning">Interner Entwurf</Chip>;
+  }
+
+  return null;
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx
new file mode 100644
index 000000000..5bf560fc7
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresSearchBar.tsx
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { Row } from "@eshg/lib-portal/components/Row";
+import { NavigationLink } from "@eshg/lib-portal/components/navigation/NavigationLink";
+import { Add } from "@mui/icons-material";
+import { Button } from "@mui/joy";
+
+import { useSearchParamLink } from "@/lib/shared/hooks/searchParams/useSearchParam";
+
+export function MedicalRegistryProceduresSearchBar() {
+  const openNewProcedureSidebarLink = useSearchParamLink("add-procedure", true);
+
+  return (
+    <Row justifyContent="flex-end">
+      <NavigationLink href={openNewProcedureSidebarLink} passHref>
+        <Button startDecorator={<Add />}>Eintrag erstellen</Button>
+      </NavigationLink>
+    </Row>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx
new file mode 100644
index 000000000..8a9bce954
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx
@@ -0,0 +1,142 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiMedicalRegistryEntry,
+  ApiProfessionalAddress,
+} from "@eshg/employee-portal-api/medicalRegistry";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createColumnHelper } from "@tanstack/react-table";
+
+import { useGetMedicalRegistryProcedureOverviewQuery } from "@/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries";
+import { MedicalRegistryProcedureChip } from "@/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProcedureChip";
+import { routes } from "@/lib/businessModules/medicalRegistry/shared/routes";
+import { Pagination } from "@/lib/shared/components/pagination/Pagination";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+import { translateCountry } from "@/lib/shared/helpers/i18n";
+import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
+import { useTablePageParams } from "@/lib/shared/hooks/useTablePageParams";
+
+import { MedicalRegistryProceduresSearchBar } from "./MedicalRegistryProceduresSearchBar";
+
+const columnHelper = createColumnHelper<ApiMedicalRegistryEntry>();
+
+function formatAddress(address: ApiProfessionalAddress) {
+  return `${address.street} ${address.houseNumber}, ${address.postalCode} ${address.city}, ${translateCountry(address.country)}`;
+}
+
+function getProceduresColumns() {
+  return [
+    columnHelper.accessor("lastName", {
+      header: "Name",
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("firstName", {
+      header: "Vorname",
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("dateOfBirth", {
+      header: "Geburtsdatum",
+      cell: ({ getValue }) => formatDate(getValue()),
+      enableSorting: false,
+      meta: {
+        width: 180,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("address", {
+      header: "Adresse",
+      cell: ({ getValue }) => formatAddress(getValue()),
+      enableSorting: false,
+      meta: {
+        width: 350,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.accessor("certificateRequested", {
+      header: "Bescheinigung abgefragt",
+      cell: ({ getValue }) => (getValue() ? "Ja" : "Nein"),
+      enableSorting: false,
+      meta: {
+        width: 270,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+    columnHelper.display({
+      header: "Status",
+      cell: ({ row }) => {
+        const procedure = row.original;
+        return <MedicalRegistryProcedureChip procedure={procedure} />;
+      },
+      enableSorting: false,
+      meta: {
+        width: 200,
+        canNavigate: {
+          parentRow: true,
+        },
+      },
+    }),
+  ];
+}
+
+export function MedicalRegistryProceduresTable() {
+  const tablePage = useTablePageParams();
+  const {
+    data: { medicalRegistryEntries, totalElements },
+    isLoading,
+  } = useGetMedicalRegistryProcedureOverviewQuery(tablePage);
+
+  const tableControl = useTableControl();
+
+  return (
+    <TablePage
+      aria-label="Vorgänge"
+      controls={<MedicalRegistryProceduresSearchBar />}
+    >
+      {" "}
+      <TableSheet
+        loading={isLoading}
+        footer={
+          <Pagination
+            totalCount={totalElements}
+            {...tableControl.paginationProps}
+          />
+        }
+      >
+        <DataTable
+          data={medicalRegistryEntries}
+          columns={getProceduresColumns()}
+          rowNavigation={{
+            route: ({ original: { id: procedureId } }) =>
+              routes.procedures.byId(procedureId).details,
+            focusColumnAccessorKey: "id",
+          }}
+        />
+      </TableSheet>
+    </TablePage>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts b/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
index 55a5ad4d2..3cbdacb95 100644
--- a/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/routes.ts
@@ -5,11 +5,15 @@
 
 const basePath = "/medical-registry";
 const proceduresPath = `${basePath}/procedures`;
+const proceduresSearchPath = `${basePath}/search-procedure`;
 
 export const routes = {
   procedures: {
+    index: `${proceduresPath}`,
     byId: (procedureId: string) => ({
       index: `${proceduresPath}/${procedureId}`,
+      details: `${proceduresPath}/${procedureId}/details`,
     }),
   },
+  proceduresSearch: { index: `${proceduresSearchPath}` },
 } as const;
diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx
new file mode 100644
index 000000000..b96fc95b4
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/sideNavigationItem.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { MedicalServicesOutlined } from "@mui/icons-material";
+
+import {
+  SideNavigationSubItem,
+  UseSideNavigationItemsResult,
+} from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { hasUserRole } from "@/lib/shared/helpers/accessControl";
+
+import { routes } from "./routes";
+
+const sideNavigationItem = {
+  name: "Medizinalaufsicht",
+  decorator: <MedicalServicesOutlined />,
+};
+
+const defaultSubItems: SideNavigationSubItem[] = [
+  {
+    name: "Berufskartei",
+    href: routes.procedures.index,
+    accessCheck: hasUserRole(ApiUserRole.MedicalRegistryAdmin),
+  },
+];
+
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
+  const subItems = defaultSubItems;
+  return {
+    isLoading: false,
+    items: [{ ...sideNavigationItem, subItems }],
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
index 6ddbc778f..014029266 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
+++ b/employee-portal/src/lib/businessModules/schoolEntry/api/models/ProcedureDetails.ts
@@ -28,6 +28,8 @@ export interface ProcedureDetails extends Procedure {
   readonly waitingRoom?: WaitingRoom;
   readonly isDeletable: boolean;
   readonly schoolInfoLetterCreatedAt?: Date;
+  readonly hasInformationBlock: boolean;
+  readonly hasBeenClosed: boolean;
 }
 
 export function mapProcedureDetails(
@@ -48,5 +50,7 @@ export function mapProcedureDetails(
     waitingRoom: mapOptional(response.waitingRoom, mapWaitingRoom),
     isDeletable: response.isDeletable,
     schoolInfoLetterCreatedAt: response.schoolInfoLetterCreatedAt,
+    hasInformationBlock: response.hasInformationBlock,
+    hasBeenClosed: response.hasBeenClosed,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
index 68a305739..909d615d5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx
@@ -143,11 +143,9 @@ export function CreateAppointmentBlockGroupForm() {
   }, [validateAppointmentBlockGroup]);
 
   async function handleSubmit(values: CreateAppointmentBlockGroupValues) {
-    await createDailyAppointmentBlockGroup
-      .mutateAsync(mapFormValues(values), {
-        onSuccess: () => router.push(routes.appointmentBlockGroups.overview),
-      })
-      .catch();
+    await createDailyAppointmentBlockGroup.mutateAsync(mapFormValues(values), {
+      onSuccess: () => router.push(routes.appointmentBlockGroups.overview),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
index 17c950ba0..9d8b5be9d 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/CreateLabelSidebar.tsx
@@ -39,9 +39,9 @@ function CreateLabelSidebar(props: DrawerProps) {
   const createLabel = useCreateLabel();
 
   async function handleSubmit(data: LabelValues) {
-    await createLabel
-      .mutateAsync(mapToRequest(data), { onSuccess: () => props.onClose() })
-      .catch();
+    await createLabel.mutateAsync(mapToRequest(data), {
+      onSuccess: () => props.onClose(),
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
index 96e4dd8af..bef05420d 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/labels/UpdateLabelSidebar.tsx
@@ -51,11 +51,12 @@ function UpdateLabelSidebar(props: UpdateLabelProps) {
   async function handleSubmit(labelFormValues: LabelValues) {
     const labelId = props.label.id;
     const labelVersion = props.label.version;
-    await updateLabel
-      .mutateAsync(mapToRequest(labelId, labelFormValues, labelVersion), {
+    await updateLabel.mutateAsync(
+      mapToRequest(labelId, labelFormValues, labelVersion),
+      {
         onSuccess: () => props.onClose(),
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
index 5478af262..4490434ca 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/AnamnesisForm.tsx
@@ -10,6 +10,7 @@ import {
   ApiSchoolEntryCountryCode,
 } from "@eshg/employee-portal-api/schoolEntry";
 import { SoftRequiredBooleanSelectField } from "@eshg/lib-portal/businessModules/schoolEntry/features/procedures/fieldVariants";
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
 import { HorizontalField } from "@eshg/lib-portal/components/formFields/HorizontalField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import {
@@ -81,6 +82,7 @@ export interface AdditionalChildInfoValues {
 }
 
 export interface DaycareAndSchoolInfoValues {
+  wasInDaycare: OptionalFieldValue<boolean>;
   inDaycareSince: MonthAndYear;
   daycareName: OptionalFieldValue<string>;
   schoolName: OptionalFieldValue<string>;
@@ -168,21 +170,30 @@ export function AnamnesisForm(props: AnamnesisFormProps) {
             <ConfirmLeaveDirtyFormEffect />
             <Divider />
             <Stack direction="row" gap={4} flexWrap="wrap">
-              <FormLabel sx={{ fontWeight: "bold" }}>
-                in Kindertagesstätte seit
-              </FormLabel>
-              <MonthAndYearFields
-                testId="inDaycareSince"
-                fieldName={daycareAndSchoolInfo("inDaycareSince")}
-                date={values.daycareAndSchoolInfo.inDaycareSince}
-              />
-              <InputField
-                name={daycareAndSchoolInfo("daycareName")}
-                label={<FlexLabel>Name Kindertagesstätte</FlexLabel>}
-                type="text"
+              <BooleanSelectField
                 component={HorizontalField}
-                sx={TEXT_INPUT_STYLE}
+                name={daycareAndSchoolInfo("wasInDaycare")}
+                label={<FormLabel>war im Kindergarten</FormLabel>}
+                allowDeselection
+                sx={{ ...BOOLEAN_SELECT_STYLE }}
               />
+              {values.daycareAndSchoolInfo.wasInDaycare.valueOf() === true && (
+                <>
+                  <FormLabel>seit</FormLabel>
+                  <MonthAndYearFields
+                    testId="inDaycareSince"
+                    fieldName={daycareAndSchoolInfo("inDaycareSince")}
+                    date={values.daycareAndSchoolInfo.inDaycareSince}
+                  />
+                  <InputField
+                    name={daycareAndSchoolInfo("daycareName")}
+                    label={<FlexLabel>Name Kindertagesstätte</FlexLabel>}
+                    type="text"
+                    component={HorizontalField}
+                    sx={TEXT_INPUT_STYLE}
+                  />
+                </>
+              )}
               <SoftRequiredBooleanSelectField
                 name="childLanguageScreening"
                 label={"Kiss"}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
index 61969aead..8d060f54b 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/BirthDataAndChildInformationForm.tsx
@@ -16,10 +16,10 @@ import {
   OptionalFieldValue,
   SetFieldValueHelper,
 } from "@eshg/lib-portal/types/form";
-import { InfoOutlined } from "@mui/icons-material";
-import { FormLabel, Stack, Tooltip, Typography } from "@mui/joy";
+import { FormLabel, Stack, Typography } from "@mui/joy";
 
 import { BOOLEAN_SELECT_STYLE } from "@/lib/businessModules/schoolEntry/features/procedures/styles";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 import { isInteger } from "@/lib/shared/helpers/guards";
 
 import { AnamnesisFormValues, TEXT_INPUT_STYLE } from "./AnamnesisForm";
@@ -78,17 +78,10 @@ export function BirthDataAndChildInformationForm(
             softRequired
           />
           <FormLabel sx={LABEL_TEXT_STYLE}>in g</FormLabel>
-          <Tooltip
+          <InfoIconTooltipButton
             title="(300 - 6000, 9999 - unbekannt)"
-            color="success"
-            variant="outlined"
-          >
-            <InfoOutlined
-              color="primary"
-              size="sm"
-              sx={{ marginBottom: "auto", marginTop: "auto" }}
-            />
-          </Tooltip>
+            tooltipColor="success"
+          />
         </Stack>
         <BooleanSelectField
           name={developmentInfo("gestationalAge")}
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
index 833bcfa9f..d65772f02 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/anamnesis/IllnessAndAccidentInfoForm.tsx
@@ -10,11 +10,11 @@ import { HorizontalField } from "@eshg/lib-portal/components/formFields/Horizont
 import { InputArrayField } from "@eshg/lib-portal/components/formFields/InputArrayField";
 import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
 import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
-import { InfoOutlined } from "@mui/icons-material";
-import { FormLabel, Stack, Tooltip, Typography } from "@mui/joy";
+import { FormLabel, Stack, Typography } from "@mui/joy";
 
 import { FlexLabel } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/FlexLabel";
 import { BOOLEAN_SELECT_STYLE } from "@/lib/businessModules/schoolEntry/features/procedures/styles";
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 
 export function IllnessAndAccidentInfoForm() {
   const illnessAndAccidentInfo = createFieldNameMapper(
@@ -30,13 +30,10 @@ export function IllnessAndAccidentInfoForm() {
           label={
             <FlexLabel>
               Schwere Infektionskrankheiten
-              <Tooltip
-                title=" z.B. Hirnhautentzündung oder andere schwere Erkrankungen"
-                color="success"
-                variant="outlined"
-              >
-                <InfoOutlined color="primary" />
-              </Tooltip>
+              <InfoIconTooltipButton
+                title="z.B. Hirnhautentzündung oder andere schwere Erkrankungen"
+                tooltipColor="success"
+              />
             </FlexLabel>
           }
           component={HorizontalField}
@@ -61,13 +58,10 @@ export function IllnessAndAccidentInfoForm() {
           label={
             <FlexLabel>
               Regelmäßige Medikamenteneinnahme
-              <Tooltip
+              <InfoIconTooltipButton
                 title="Präparat und Dosierung"
-                color="success"
-                variant="outlined"
-              >
-                <InfoOutlined color="primary" />
-              </Tooltip>
+                tooltipColor="success"
+              />
             </FlexLabel>
           }
           type="text"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
index 3cd5915a9..f92acc8d4 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/importData/ImportDataSidebar.tsx
@@ -64,7 +64,7 @@ function ImportDataSidebar(props: SidebarWithFormRefProps) {
   const importData = useImportData();
 
   async function handleSubmit(values: ImportDataValues) {
-    await importData.mutateAsync(values).catch();
+    await importData.mutateAsync(values);
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
index 5015ff688..4076c1be5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/new/CreateProcedureSidebar.tsx
@@ -91,14 +91,15 @@ export function CreateProcedureSidebar() {
     child: ApiCreatePerson,
     type: OptionalFieldValue<ApiSchoolEntryProcedureType>,
   ) {
-    await createProcedure
-      .mutateAsync(mapToCreateProcedureRequest(child, type), {
+    await createProcedure.mutateAsync(
+      mapToCreateProcedureRequest(child, type),
+      {
         onSuccess: (response) => {
           closeSidebar();
           router.push(routes.procedures.byId(response.procedureId).details);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
index 3f825eb92..26a77fef2 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal.tsx
@@ -20,14 +20,13 @@ export function DeleteProcedureModal(props: DeleteProcedureModalProps) {
   const router = useRouter();
 
   async function handleSubmit() {
-    await deleteProcedure
-      .mutateAsync({
-        procedureId: props.procedure.id,
-        apiDeleteProcedureRequest: {
-          version: props.procedure.version,
-        },
-      })
-      .catch();
+    await deleteProcedure.mutateAsync({
+      procedureId: props.procedure.id,
+      apiDeleteProcedureRequest: {
+        version: props.procedure.version,
+      },
+    });
+    // TODO: ISSUE-6052: move onClose and router.push into onSuccess(?)
     props.onClose();
     router.push(routes.procedures.overview);
   }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
index 73b7e4a00..088e9e164 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/PersonDetailsPanel.tsx
@@ -54,11 +54,9 @@ export function PersonDetailsPanel({
   const { syncBarrier } = useSyncBarrier(syncRoute, person);
 
   async function handleConfirm() {
-    await removeCustodian
-      .mutateAsync({
-        procedureVersion: procedure.version,
-      })
-      .catch();
+    await removeCustodian.mutateAsync({
+      procedureVersion: procedure.version,
+    });
   }
 
   function handleDelete() {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
index e5ae9375a..6e43a57a0 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureActionsPanel.tsx
@@ -3,11 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiSchoolEntryFeature } from "@eshg/employee-portal-api/schoolEntry";
 import { ReactNode } from "react";
 
 import { ProcedureDetails } from "@/lib/businessModules/schoolEntry/api/models/ProcedureDetails";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/schoolEntry/api/queries/featureTogglesApi";
 import { CloseProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/CloseProcedureModal";
 import { DeleteProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/DeleteProcedureModal";
 import { ReopenProcedureModal } from "@/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal";
@@ -15,16 +13,9 @@ import { OpenModalButton } from "@/lib/shared/components/buttons/OpenModalButton
 import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel";
 
 export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
-  const closeProcedureEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.CloseProcedure,
-  );
-  const reopenProcedureEnabled = useIsNewFeatureEnabled(
-    ApiSchoolEntryFeature.ReopenProcedure,
-  );
-
   const buttons: ReactNode[] = [];
 
-  if (closeProcedureEnabled && !props.procedure.isClosed) {
+  if (!props.procedure.isClosed) {
     buttons.push(
       <OpenModalButton
         key="closeProcedure"
@@ -37,7 +28,7 @@ export function ProcedureActionsPanel(props: { procedure: ProcedureDetails }) {
     );
   }
 
-  if (reopenProcedureEnabled && props.procedure.isClosed) {
+  if (props.procedure.isClosed) {
     buttons.push(
       <OpenModalButton
         key="reopenProcedure"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
index 74206cf43..3fd6413b5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ProcedureDetails.tsx
@@ -6,6 +6,7 @@
 "use client";
 
 import { ApiLocationSelectionMode } from "@eshg/employee-portal-api/schoolEntry";
+import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Grid, Stack } from "@mui/joy";
 import { isDefined } from "remeda";
 
@@ -26,7 +27,13 @@ interface ProcedureDetailsProps {
 }
 
 export function ProcedureDetails(props: ProcedureDetailsProps) {
-  const procedure = props.procedure;
+  const { procedure, locationSelectionMode } = props;
+
+  useControlledAlert({
+    type: "error",
+    open: procedure.hasInformationBlock,
+    message: "Für diesen Vorgang wurde eine Auskunftssperre erteilt.",
+  });
 
   return (
     <PageGrid>
@@ -55,7 +62,7 @@ export function ProcedureDetails(props: ProcedureDetailsProps) {
         <Stack spacing={SPACING}>
           <ProcedureDetailsSection procedure={procedure} />
           <ProcedureActionsPanel procedure={procedure} />
-          {props.locationSelectionMode === ApiLocationSelectionMode.None &&
+          {locationSelectionMode === ApiLocationSelectionMode.None &&
             !procedure.isClosed &&
             isDefined(procedure.appointment) && (
               <WaitingRoomPanel procedure={procedure} />
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
index 85b57f994..1e2a847f5 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/ReopenProcedureModal.tsx
@@ -19,14 +19,13 @@ interface ReopenProcedureModalProps extends Omit<BaseModalProps, "children"> {
 export function ReopenProcedureModal(props: ReopenProcedureModalProps) {
   const reopenProcedure = useReopenProcedure();
   async function handleSubmit() {
-    await reopenProcedure
-      .mutateAsync({
-        procedureId: props.procedure.id,
-        apiReopenProcedureRequest: {
-          version: props.procedure.version,
-        },
-      })
-      .catch();
+    await reopenProcedure.mutateAsync({
+      procedureId: props.procedure.id,
+      apiReopenProcedureRequest: {
+        version: props.procedure.version,
+      },
+    });
+    // TODO: ISSUE-6052: move onClose into onSuccess(?)
     props.onClose();
   }
 
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
index 5a135dad9..a4c62a059 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/UpdateProcedureSidebar.tsx
@@ -75,6 +75,7 @@ export interface UpdateProcedureValues {
   isDeceased: boolean;
   deceased: OptionalFieldValue<string>;
   schoolYear: OptionalFieldValue<number>;
+  hasBeenClosed: boolean;
 }
 
 interface UpdateProcedureSidebarProps extends SidebarWithFormRefProps {
@@ -136,6 +137,7 @@ function useUpdateProcedureForm(
         ? toDateString(procedure.deceased)
         : "",
       schoolYear: parseOptionalValue(procedure.schoolYear),
+      hasBeenClosed: procedure.hasBeenClosed,
     },
     onSubmit: (values) =>
       updateProcedure
@@ -256,6 +258,11 @@ function UpdateProcedureSidebar(props: UpdateProcedureSidebarProps) {
                     "Erfassen Sie das Gesundheitsamt, um einen Termin zuweisen und eine Einladung versenden zu können.",
                 },
               )}
+              {displayWarningWhen(values.hasBeenClosed, {
+                title: "Keine Terminauswahl möglich",
+                message:
+                  "Ein neuer Termin kann nicht ausgewählt werden, weil der Vorgang bereits abgeschlossen wurde.",
+              })}
               {values.appointment !== null && (
                 <CheckboxField
                   name="isInvitationSent"
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
index a207e3f78..c6a0f72d1 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/procedureDetails/WaitingRoomPanel.tsx
@@ -42,15 +42,13 @@ export function WaitingRoomPanel(props: { procedure: ProcedureDetails }) {
   const updateWaitingRoomDetails = useUpdateWaitingRoomDetails();
 
   async function handleSubmit(values: WaitingRoomValues) {
-    await updateWaitingRoomDetails
-      .mutateAsync(
-        mapToRequest(
-          props.procedure.id,
-          values,
-          props.procedure.waitingRoom?.version ?? 0,
-        ),
-      )
-      .catch();
+    await updateWaitingRoomDetails.mutateAsync(
+      mapToRequest(
+        props.procedure.id,
+        values,
+        props.procedure.waitingRoom?.version ?? 0,
+      ),
+    );
   }
 
   async function handleReset(setFieldValue: SetFieldValueHelper) {
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
index cd19d6985..559f917db 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureTableTitle.tsx
@@ -108,14 +108,12 @@ export function ProceduresTableTitle(props: ProcedureTableTitleProps) {
 
   async function handleClickBulkAppointmentButton() {
     bulkAppointmentCreationMessage.close();
-    await createAppointmentsInBulk
-      .mutateAsync(
-        {
-          procedureIds: selectedProcedureIds,
-        },
-        { onSuccess: bulkAppointmentCreationMessage.open },
-      )
-      .catch();
+    await createAppointmentsInBulk.mutateAsync(
+      {
+        procedureIds: selectedProcedureIds,
+      },
+      { onSuccess: bulkAppointmentCreationMessage.open },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
index 188dca9ed..01bab49b8 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx
@@ -174,8 +174,10 @@ export function ProceduresTable(props: ProceduresTableProps) {
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
           rowSelectionProps={rowSelectionProps}
-          rowNavRoute={(row) => routes.procedures.byId(row.original.id).details}
-          focusColumnHeader="Name"
+          rowNavigation={{
+            route: (row) => routes.procedures.byId(row.original.id).details,
+            focusColumnAccessorKey: "child.lastName",
+          }}
           minWidth={1600}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
index 8a85334a9..a7e717709 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/sopessExamination/FormSectionTitle.tsx
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { InfoOutlined } from "@mui/icons-material";
-import { Stack, Tooltip, Typography } from "@mui/joy";
+import { Stack, Typography } from "@mui/joy";
+
+import { InfoIconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton";
 
 interface FormSectionTitleProps {
   title: string;
@@ -13,11 +14,9 @@ interface FormSectionTitleProps {
 
 export function FormSectionTitle(props: FormSectionTitleProps) {
   return (
-    <Stack gap={1} direction="row">
+    <Stack gap={1} direction="row" alignItems="center">
       <Typography level="title-sm">{props.title}</Typography>
-      <Tooltip title={props.tooltip} color="success" variant="outlined">
-        <InfoOutlined color="primary" size="sm" />
-      </Tooltip>
+      <InfoIconTooltipButton title={props.tooltip} tooltipColor="success" />
     </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
index 616cc8a5a..1542146d4 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/features/waitingRoom/WaitingRoomTable.tsx
@@ -63,7 +63,10 @@ export function WaitingRoomTable() {
           columns={COLUMNS}
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
-          rowNavRoute={(row) => routes.procedures.byId(row.original.id).details}
+          rowNavigation={{
+            route: (row) => routes.procedures.byId(row.original.id).details,
+            focusColumnAccessorKey: "child.lastName",
+          }}
           minWidth={1200}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
index 746128f02..c949b520e 100644
--- a/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/schoolEntry/shared/sideNavigationItem.tsx
@@ -10,8 +10,8 @@ import { useQuery } from "@tanstack/react-query";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useConfigApi } from "@/lib/businessModules/schoolEntry/api/clients";
 import { getLocationSelectionModeQuery } from "@/lib/businessModules/schoolEntry/api/queries/configApi";
@@ -50,15 +50,18 @@ const inboxNavigationItem: SideNavigationSubItem = {
   accessCheck: hasUserRole(ApiUserRole.SchoolEntryAdmin),
 };
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
 
   const configApi = useConfigApi();
-  const { data: locationSelectionMode, isError: isLocationModeError } =
-    useQuery({
-      ...getLocationSelectionModeQuery(configApi),
-      throwOnError: false,
-    });
+  const {
+    data: locationSelectionMode,
+    isError: isLocationModeError,
+    isLoading: isLocationModeLoading,
+  } = useQuery({
+    ...getLocationSelectionModeQuery(configApi),
+    throwOnError: false,
+  });
 
   const hasLocationMode =
     locationSelectionMode !== ApiLocationSelectionMode.None;
@@ -78,5 +81,8 @@ export function useSideNavigationItems(): SideNavigationItem[] {
       : undefined,
   };
 
-  return [{ ...sideNavigationItem, subItems }];
+  return {
+    isLoading: isLocationModeLoading,
+    items: [{ ...sideNavigationItem, subItems }],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts
new file mode 100644
index 000000000..ce8b0b02d
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetails.ts
@@ -0,0 +1,13 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface EvaluationDetails {
+  businessModule: string;
+  attributeLabels: string[];
+  analyses: {
+    name: string;
+    diagramTitles: string[];
+  }[];
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts
new file mode 100644
index 000000000..6abe55023
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationTemplatesOverview.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiUser } from "@eshg/employee-portal-api/base";
+
+export interface EvaluationTemplateTableView {
+  totalNumberOfElements: number;
+  evaluationTemplates: EvaluationTemplateWithUserInfo[];
+}
+
+export interface EvaluationTemplate {
+  id: string;
+  name: string;
+  analysisCount: number;
+  createdAt: Date;
+  userId: string;
+  businessModuleName: string;
+}
+
+export interface EvaluationTemplateWithUserInfo extends EvaluationTemplate {
+  user: ApiUser | undefined;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts b/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts
new file mode 100644
index 000000000..c32d0eb9e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/pageRequest.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface PageRequest {
+  page: number;
+  pageSize: number;
+  sortDirection: "ASC" | "DESC" | undefined;
+  sortKey: string | undefined;
+}
+
+export function mapPageRequest<ApiSortKey>(
+  pageRequest: PageRequest,
+  sortKeyMapper: (sortKey: string | undefined) => ApiSortKey | undefined,
+) {
+  return {
+    ...pageRequest,
+    sortKey: sortKeyMapper(pageRequest.sortKey),
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
index 638c4ce50..84742c2be 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticDetailsViewTypes.ts
@@ -48,6 +48,7 @@ export interface StatisticDetailsView {
   evaluations: Evaluation[];
   attributes: FlatAttribute[];
   userId: string;
+  anonymized: boolean;
 }
 
 export interface Diagram<T> {
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts
new file mode 100644
index 000000000..435c3efc9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticOverview.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiStatisticState,
+  ApiUser,
+} from "@eshg/employee-portal-api/statistics";
+
+export interface StatisticOverviewTableItem {
+  createdAt: Date;
+  id: string;
+  name: string;
+  dataSourceName: string;
+  state: ApiStatisticState;
+  timeRangeEnd: Date;
+  timeRangeStart: Date;
+  userId: string;
+  user: ApiUser | undefined;
+  anonymized: boolean;
+}
+
+export interface StatisticOverview {
+  data: StatisticOverviewTableItem[];
+  totalNumberOfElements: number;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts b/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
index f878c4762..66f191cd3 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/models/statisticReports.ts
@@ -24,6 +24,7 @@ export interface StatisticReports {
   title: string;
   reports: ReportData[];
   activeSeries?: ActiveSeriesInfo;
+  anonymized: boolean;
 }
 
 export type ReportData = SingleReport | ReportSeries;
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
index c2a89ce2c..3fcd12f5d 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAutoReportSeries.ts
@@ -89,9 +89,6 @@ export function useAddAutoReportSeries(onSuccess: () => void) {
   });
 
   return async (statisticId: string, model: AutomateReportFormModel) => {
-    return mutation
-      .mutateAsync({ statisticId, model }, { onSuccess })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({ statisticId, model }, { onSuccess });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
index 50086f39b..7f825b728 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddDiagram.ts
@@ -37,8 +37,8 @@ export function useAddDiagram() {
     param: UseAddDiagramParams,
     options: { onSuccess?: () => void },
   ) => {
-    await addDiagramMutation
-      .mutateAsync(param, { onSuccess: options.onSuccess })
-      .catch();
+    await addDiagramMutation.mutateAsync(param, {
+      onSuccess: options.onSuccess,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
index 8abfc93a0..559e54021 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluation.ts
@@ -171,7 +171,7 @@ export function useAddEvaluation(statisticId: string, onClose: () => void) {
           onSuccess: onClose,
         },
       )
-      .then((it) => it.id)
-      .catch();
+      // TODO: ISSUE-6052: don't use response data. Combine multiple API calls into a single one.
+      .then((it) => it.id);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts
new file mode 100644
index 000000000..9d712f70e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import { SaveAsEvaluationTemplateFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel";
+
+export function useAddEvaluationTemplate(onSuccess?: () => void) {
+  const snackbar = useSnackbar();
+  const statisticsApi = useEvaluationTemplateApi();
+
+  const mutation = useHandledMutation({
+    mutationFn: ({
+      evaluationId,
+      model,
+    }: {
+      evaluationId: string;
+      model: SaveAsEvaluationTemplateFormModel;
+    }) =>
+      statisticsApi.addEvaluationTemplate({
+        type: "AddEvaluationTemplateFromEvaluationRequest",
+        evaluationId: evaluationId,
+        name: model.name,
+        description:
+          model.description.length === 0 ? undefined : model.description,
+      }),
+    onSuccess: () => {
+      snackbar.confirmation("Vorlage erstellt");
+      onSuccess?.();
+    },
+  });
+
+  return async (
+    evaluationId: string,
+    model: SaveAsEvaluationTemplateFormModel,
+  ) => {
+    return mutation.mutateAsync({ evaluationId, model });
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
index 628528b64..9f52c9c44 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddFilterTemplate.ts
@@ -31,6 +31,6 @@ export function useAddFilterTemplate(attributes: FlatAttribute[]) {
     onSuccess: () => snackbar.confirmation("Vorlage gespeichert"),
   });
   return async (useAddFilterTemplate: UseAddFilterTemplate) => {
-    return mutation.mutateAsync(useAddFilterTemplate).catch();
+    return mutation.mutateAsync(useAddFilterTemplate);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
index f128d8e7b..8889f6749 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddGeoShape.ts
@@ -26,8 +26,8 @@ export function useAddGeoShape() {
     param: AddGeoShapeValues,
     options: { onSuccess?: () => void },
   ) => {
-    await addGeoShapeMutation
-      .mutateAsync(param, { onSuccess: options.onSuccess })
-      .catch();
+    await addGeoShapeMutation.mutateAsync(param, {
+      onSuccess: options.onSuccess,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
index f80d0d4f0..5aa714c3e 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddReport.ts
@@ -39,12 +39,9 @@ export function useAddReport(onSuccess: () => void) {
   });
 
   return async (statisticId: string, model: AddReportFormModel) => {
-    return mutation
-      .mutateAsync({
-        statisticId: statisticId,
-        model: model,
-      })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({
+      statisticId: statisticId,
+      model: model,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
index 6027b3561..18e67a7e3 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddStatistic.ts
@@ -19,7 +19,7 @@ export function useAddStatistic({ onSuccess }: { onSuccess: () => void }) {
     onSuccess: () => snackbar.confirmation("Auswertung wird erstellt"),
   });
   return (apiAddStatisticRequest: ApiAddStatisticRequest) =>
-    mutation.mutateAsync(apiAddStatisticRequest, { onSuccess }).catch();
+    mutation.mutateAsync(apiAddStatisticRequest, { onSuccess });
 }
 
 function mapAddStatistic(apiAddStatisticRequest: ApiAddStatisticRequest) {
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
index fa401e2c0..ce64409ed 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDeactivateReportSeries.ts
@@ -12,14 +12,7 @@ export function useDeactivateReportSeries() {
   const snackbar = useSnackbar();
   const api = useReportSeriesApi();
   const mutation = useHandledMutation({
-    mutationFn: (seriesId: string) =>
-      // Currently the openAPI generator doesn't map type to @type. This seems to be a bug. The current solution is a quick fix. One potential workaround would be to use an extra endpoint.
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-      api.updateReportSeries(seriesId, {
-        type: "DeactivateAutoReportSeriesRequest",
-        "@type": "DeactivateAutoReportSeriesRequest",
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      } as any),
+    mutationFn: (seriesId: string) => api.deactivateReportSeries(seriesId),
     onSuccess: () => {
       snackbar.confirmation("Automatisierung deaktiviert");
     },
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
index c3d32d0e5..1c8e2081b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useDuplicateStatistic.ts
@@ -25,5 +25,5 @@ export function useDuplicateStatistic({
     },
   });
   return (params: ApiCloneStatisticRequest) =>
-    mutation.mutateAsync(params, { onSuccess }).catch();
+    mutation.mutateAsync(params, { onSuccess });
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
index dd9fa1ea4..1563cecd3 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useEditStatisticName.ts
@@ -22,6 +22,6 @@ export function useEditStatisticName(statisticId: string) {
   });
 
   return async (name: string) => {
-    return mutation.mutateAsync(name).catch();
+    return mutation.mutateAsync(name);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
index 6d881c69a..865644789 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useGetFilterTemplateFilters.ts
@@ -18,6 +18,6 @@ export function useGetFilterTemplateFilters() {
   });
 
   return async (filterTemplateId: string) => {
-    return mutation.mutateAsync(filterTemplateId).catch();
+    return mutation.mutateAsync(filterTemplateId);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
index e0f3f6d7a..6ff4ba04b 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDataBasis.ts
@@ -43,11 +43,9 @@ export function useUpdateDataBasis({
   });
 
   return async (statisticId: string, timeSpan: TimeSpan) => {
-    return mutation
-      .mutateAsync({
-        statisticId: statisticId,
-        timeSpan: timeSpan,
-      })
-      .catch();
+    return mutation.mutateAsync({
+      statisticId: statisticId,
+      timeSpan: timeSpan,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
index 20100651c..e882ffd77 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateDiagram.ts
@@ -40,9 +40,6 @@ export function useUpdateDiagram(
   });
 
   return async (model: UpdateDiagramFormModel) => {
-    return mutation
-      .mutateAsync(model)
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync(model);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
index 58c9553f2..9a936e5ec 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateEvaluation.ts
@@ -32,6 +32,6 @@ export function useUpdateEvaluation(
   });
 
   return async (model: UpdateEvaluationFormModel) => {
-    await mutation.mutateAsync(model).catch();
+    await mutation.mutateAsync(model);
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
index 730d52caf..b0d622ba1 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useUpdateReport.ts
@@ -20,7 +20,6 @@ export function useUpdateReport(onSuccess: () => void) {
           props.model.description.trim().length > 0
             ? props.model.description.trim()
             : undefined,
-        type: "UpdateNameAndDescriptionReportSeriesRequest",
       }),
     onSuccess: () => {
       snackbar.confirmation("Report bearbeitet");
@@ -29,12 +28,9 @@ export function useUpdateReport(onSuccess: () => void) {
   });
 
   return async (seriesId: string, model: UpdateReportFormModel) => {
-    return mutation
-      .mutateAsync({
-        seriesId,
-        model: model,
-      })
-      .then(() => void 0)
-      .catch();
+    await mutation.mutateAsync({
+      seriesId,
+      model: model,
+    });
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
index 2e29becbf..1c5cf6058 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts
@@ -153,6 +153,7 @@ export function mapToStatisticDetailsView(
     attributes: attributes,
     evaluations: mapEvaluations(result.evaluations, attributes),
     userId: result.user!.userId,
+    anonymized: result.statisticInfo.anonymized,
   } satisfies StatisticDetailsView;
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts
new file mode 100644
index 000000000..7f5401f4e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetails.ts
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiExpectedEvaluationTemplate } from "@eshg/employee-portal-api/statistics";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import { EvaluationDetails } from "@/lib/businessModules/statistics/api/models/evaluationDetails";
+import { evaluationTemplateApiQueryKey } from "@/lib/businessModules/statistics/api/queries/apiQueryKeys";
+import { getAttributeLabel } from "@/lib/businessModules/statistics/components/statistics/getAttributeLabel";
+
+export function mapToEvaluationDetails(
+  result: ApiExpectedEvaluationTemplate,
+): EvaluationDetails {
+  return {
+    businessModule: result.dataSources[0]!.businessModuleName,
+    attributeLabels: result.dataSources[0]!.dataAttributes.flatMap((it) => {
+      if (it.baseDataAttributes.length === 0) {
+        return [getAttributeLabel(it)];
+      }
+      return it.baseDataAttributes.map((bAttr) => getAttributeLabel(it, bAttr));
+    }),
+    analyses: result.analysisInfos.map((it) => ({
+      name: it.name,
+      diagramTitles: it.diagramTitles,
+    })),
+  };
+}
+
+export function useGetEvaluationDetails(
+  evaluationId: string,
+): EvaluationDetails {
+  const evaluationTemplateApi = useEvaluationTemplateApi();
+  const queryResult = useSuspenseQuery({
+    queryKey: evaluationTemplateApiQueryKey([
+      "getTemplateInformation",
+      evaluationId,
+    ]),
+    queryFn: () => evaluationTemplateApi.getTemplateInformation(evaluationId),
+    select: mapToEvaluationDetails,
+  });
+  return queryResult.data;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts
new file mode 100644
index 000000000..a0f52806a
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview.ts
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiEvaluationTemplateSortKey,
+  ApiGetEvaluationTemplatesResponse,
+} from "@eshg/employee-portal-api/statistics";
+import { useSuspenseQuery } from "@tanstack/react-query";
+
+import { useEvaluationTemplateApi } from "@/lib/businessModules/statistics/api/clients";
+import {
+  EvaluationTemplateTableView,
+  EvaluationTemplateWithUserInfo,
+} from "@/lib/businessModules/statistics/api/models/evaluationTemplatesOverview";
+import {
+  PageRequest,
+  mapPageRequest,
+} from "@/lib/businessModules/statistics/api/models/pageRequest";
+
+import { evaluationTemplateApiQueryKey } from "./apiQueryKeys";
+
+export function mapToEvaluationTemplatesToTableView(
+  response: ApiGetEvaluationTemplatesResponse,
+): EvaluationTemplateTableView {
+  const templates = response.evaluationTemplates.map((template) => {
+    return {
+      analysisCount: template.analysisCount,
+      businessModuleName: template.businessModuleNames[0]!,
+      createdAt: template.createdAt,
+      id: template.id,
+      name: template.name,
+      userId: template.userId,
+      user: response.resolvedUsers[template.userId],
+    } satisfies EvaluationTemplateWithUserInfo;
+  });
+  return {
+    totalNumberOfElements: response.totalNumberOfElements,
+    evaluationTemplates: templates,
+  };
+}
+
+export function mapPageRequestSortKey(key: string | undefined) {
+  switch (key) {
+    case "name":
+      return ApiEvaluationTemplateSortKey.Name;
+    case "createdAt":
+      return ApiEvaluationTemplateSortKey.CreatedAt;
+    default:
+      return undefined;
+  }
+}
+
+export function useGetEvaluationTemplatesOverview(
+  evaluationTemplatesOverviewRequest: PageRequest,
+) {
+  const evaluationTemplateApi = useEvaluationTemplateApi();
+  const queryResult = useSuspenseQuery({
+    queryKey: evaluationTemplateApiQueryKey([
+      "getEvaluationTemplateOverview",
+      evaluationTemplatesOverviewRequest,
+    ]),
+    queryFn: () =>
+      evaluationTemplateApi.getEvaluationTemplateOverview(
+        mapPageRequest(
+          evaluationTemplatesOverviewRequest,
+          mapPageRequestSortKey,
+        ),
+      ),
+    select: mapToEvaluationTemplatesToTableView,
+  });
+  return queryResult.data;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
index 6fe15bfe1..4aac367fd 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticReports.ts
@@ -141,6 +141,7 @@ export function mapToStatisticReports(
         : mapSingleReport(reportSeriesEntry);
     }),
     activeSeries: mapActiveSeries(response),
+    anonymized: response.anonymized,
   };
 }
 
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
index cb71d65a2..9e6d6581c 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatistics.ts
@@ -13,6 +13,7 @@ import { useSuspenseQuery } from "@tanstack/react-query";
 
 import { useStatisticApi } from "@/lib/businessModules/statistics/api/clients";
 import { mapTimeRangeEndApiToFrontend } from "@/lib/businessModules/statistics/api/mapper/mapTimeRangeEnd";
+import { StatisticOverview } from "@/lib/businessModules/statistics/api/models/statisticOverview";
 
 import { getStatisticsQueryKey } from "./apiQueryKeys";
 
@@ -37,12 +38,15 @@ export function useGetStatistics(statisticsRequest: GetStatisticsRequest) {
 
 function mapGetStatistics(
   apiGetStatisticsResponse: ApiGetStatisticsResponse,
-): ApiGetStatisticsResponse {
+): StatisticOverview {
   return {
-    ...apiGetStatisticsResponse,
-    statistics: apiGetStatisticsResponse.statistics.map((statistic) => ({
+    totalNumberOfElements: apiGetStatisticsResponse.totalNumberOfElements,
+    data: apiGetStatisticsResponse.statistics.map((statistic) => ({
       ...statistic,
       timeRangeEnd: mapTimeRangeEndApiToFrontend(statistic.timeRangeEnd),
+      user: apiGetStatisticsResponse.resolvedUsers[statistic.userId],
+      dataSourceName: statistic.dataSourceNames[0]!,
+      anonymized: statistic.anonymized,
     })),
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
index 96f50f352..8efe4952f 100644
--- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
+++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetStatisticsOverviewPage.ts
@@ -22,7 +22,7 @@ export function useGetStatisticsOverviewPage(
   const dataSourceApi = useDataSourceApi();
   const evaluationTemplateApi = useEvaluationTemplateApi();
   const [
-    { data: statistics, isFetching: statisticsIsFetching },
+    { data: statisticsOverview, isFetching: statisticsOverviewIsFetching },
     { data: availableDataSources },
     { data: evaluationTemplates },
   ] = useSuspenseQueries({
@@ -34,8 +34,8 @@ export function useGetStatisticsOverviewPage(
   });
 
   return {
-    statistics,
-    statisticsIsFetching,
+    statisticsOverview,
+    statisticsOverviewIsFetching,
     availableDataSources,
     evaluationTemplates,
   };
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
index 0f39a68e8..5dfecf726 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportDetails.tsx
@@ -25,6 +25,7 @@ export function ReportDetails(props: ReportDetailsView) {
           attributes={props.attributes}
           evaluatedDataAmountTotal={props.dataSource.datasetAmount}
           isReport
+          anonymized
         />
       </Box>
       <ReportDetailsTile {...reportDetailsTileProps} />
diff --git a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
index cfc19c23f..46a076299 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/reports/ReportsOverview.tsx
@@ -140,11 +140,13 @@ export function ReportsOverview() {
               <NoSearchResults info="Keine Reports vorhanden" />
             </Box>
           )}
-          rowNavRoute={(row) =>
-            row.original.type !== "SERIES"
-              ? routes.reports.details(row.original.reportId).index
-              : undefined
-          }
+          rowNavigation={{
+            route: (row) =>
+              row.original.type !== "SERIES"
+                ? routes.reports.details(row.original.reportId).index
+                : undefined,
+            focusColumnAccessorKey: "name",
+          }}
           getSubRows={getSubRows}
         />
       </TableSheet>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx
new file mode 100644
index 000000000..8dbec3b80
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/CollapsableList.tsx
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink";
+import { Stack, Typography } from "@mui/joy";
+import { useState } from "react";
+
+export function CollapsableList({
+  items,
+  shownItemsWhileCollapsed = 5,
+}: {
+  items: string[];
+  shownItemsWhileCollapsed?: number;
+}) {
+  const [collapsed, setCollapsed] = useState<boolean>(true);
+  const resultItems = collapsed
+    ? items.slice(0, shownItemsWhileCollapsed)
+    : items;
+
+  return (
+    <Stack>
+      <Typography level="body-md">{resultItems.join(", ")}</Typography>
+      {shownItemsWhileCollapsed < items.length && (
+        <ButtonLink
+          sx={{ alignSelf: "flex-start" }}
+          variant="plain"
+          onClick={() => setCollapsed(!collapsed)}
+        >
+          {collapsed ? "Alle anzeigen" : "Weniger anzeigen"}
+        </ButtonLink>
+      )}
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
index eecb1b85f..194f3718b 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordion.tsx
@@ -33,12 +33,13 @@ export interface EvaluationAccordionProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport?: boolean;
+  anonymized: boolean;
 }
 
 export function EvaluationAccordion(props: EvaluationAccordionProps) {
   const isReport = props.isReport ?? false;
   const [sortOrder, setSortOrder] = useState<EvaluationSortOrder>(
-    EvaluationSortOrder.NewestFirst,
+    EvaluationSortOrder.NameAscending,
   );
   const [expandedAccordions, setExpandedAccordions] = useState<
     Record<string, boolean>
@@ -93,6 +94,7 @@ export function EvaluationAccordion(props: EvaluationAccordionProps) {
           evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
           onDiagramCreateClicked={props.onDiagramCreateClicked}
           isReport={isReport}
+          anonymized={props.anonymized}
         />
       ))}
     </Stack>
@@ -107,6 +109,7 @@ interface EvaluationAccordionItemProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport: boolean;
+  anonymized: boolean;
 }
 
 function EvaluationAccordionItem(props: EvaluationAccordionItemProps) {
@@ -191,6 +194,7 @@ function EvaluationAccordionItem(props: EvaluationAccordionItemProps) {
                 evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
                 onDiagramCreateClicked={props.onDiagramCreateClicked}
                 isReport={props.isReport}
+                anonymized={props.anonymized}
               />
             )}
           </Suspense>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
index 75b894a74..a029c5c2c 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationAccordionDetails.tsx
@@ -38,6 +38,7 @@ export interface EvaluationAccordionDetailsProps {
   evaluatedDataAmountTotal: number;
   onDiagramCreateClicked?: (evaluationId: string) => void;
   isReport: boolean;
+  anonymized: boolean;
 }
 
 export function EvaluationAccordionDetails(
@@ -107,6 +108,7 @@ export function EvaluationAccordionDetails(
               evaluationDiagram={it}
               evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
               isReport={props.isReport}
+              anonymized={props.anonymized}
             />
           </Stack>
         ))}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
index 3df6c1e3c..d86e3d7e4 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationChartDiagram.tsx
@@ -3,7 +3,16 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
+import {
+  Delete,
+  Download,
+  Edit,
+  OpenInFullOutlined,
+} from "@mui/icons-material";
+import { IconButton, Stack, Typography } from "@mui/joy";
 import { useState } from "react";
+import { isObjectType } from "remeda";
 
 import {
   DiagramType,
@@ -16,6 +25,9 @@ import {
   EvaluationDiagramPieChart,
   EvaluationDiagramScatterChart,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
+import { useDeleteDiagram } from "@/lib/businessModules/statistics/api/mutations/useDeleteDiagram";
+import { useExportDiagramData } from "@/lib/businessModules/statistics/api/mutations/useExportDiagramData";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import { EvaluationDiagramBox } from "@/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
 import { ChoroplethMap } from "@/lib/businessModules/statistics/components/shared/charts/ChoroplethMap";
@@ -25,15 +37,27 @@ import { LineChart } from "@/lib/businessModules/statistics/components/shared/ch
 import { PieChart } from "@/lib/businessModules/statistics/components/shared/charts/PieChart";
 import { ScatterChart } from "@/lib/businessModules/statistics/components/shared/charts/ScatterChart";
 import { ImageType } from "@/lib/businessModules/statistics/components/shared/charts/types";
+import { UpdateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/UpdateDiagramSidebar/UpdateDiagramSidebar";
+import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
 import { BaseModal } from "@/lib/shared/components/BaseModal";
+import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
+import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
+import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
 
 export function EvaluationChartDiagram(props: {
   configuration: EvaluationDiagramConfiguration;
   evaluationDiagram: EvaluationDiagram;
   evaluatedDataAmountTotal: number;
   isReport: boolean;
+  anonymized: boolean;
 }) {
   const [eChartApi, setEChartApi] = useState<ChartApi | null>(null);
+  const [isUpdateDiagramSidebarOpen, setIsUpdateDiagramSidebarOpen] =
+    useState(false);
+  const exportData = useExportDiagramData(props.evaluationDiagram.diagramId);
+  const deleteDiagram = useDeleteDiagram(props.evaluationDiagram.diagramId);
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const canWrite = useStatisticRoleChecks().canWrite();
 
   function onExportAsImage(wantedImageType: ImageType) {
     if (!eChartApi) {
@@ -92,7 +116,8 @@ export function EvaluationChartDiagram(props: {
             diagramData={
               props.evaluationDiagram.data as EvaluationDiagramHistogram["data"]
             }
-            configuration={props.configuration}
+            grouping={props.configuration.grouping}
+            scaling={props.configuration.scaling}
             eChartApi={setEChartApi}
           />
         );
@@ -103,7 +128,10 @@ export function EvaluationChartDiagram(props: {
               props.evaluationDiagram
                 .data as EvaluationDiagramChoroplethMap["data"]
             }
-            configuration={props.configuration}
+            colorScheme={props.configuration.colorScheme}
+            characteristicParameter={
+              props.configuration.characteristicParameter
+            }
             geoJson={
               (props.evaluationDiagram as EvaluationDiagramChoroplethMap)
                 .geoJson
@@ -116,8 +144,24 @@ export function EvaluationChartDiagram(props: {
 
   const [openFullScreenChart, setOpenFullScreenChart] = useState(false);
   const chart = getChart();
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+  const canExportData = props.anonymized && exportDataFeatureToggle;
+
   return (
     <>
+      {isUpdateDiagramSidebarOpen && (
+        <OverlayBoundary>
+          <UpdateDiagramSidebar
+            open={isUpdateDiagramSidebarOpen}
+            onClose={() => setIsUpdateDiagramSidebarOpen(false)}
+            diagramId={props.evaluationDiagram.diagramId}
+            title={props.evaluationDiagram.title}
+            description={props.evaluationDiagram.description}
+          />
+        </OverlayBoundary>
+      )}
       <BaseModal
         open={openFullScreenChart}
         onClose={() => setOpenFullScreenChart(false)}
@@ -128,21 +172,86 @@ export function EvaluationChartDiagram(props: {
           marginTop: "2.25rem",
         }}
       >
-        {chart}
+        <EvaluationDiagramBox
+          description={props.evaluationDiagram.description}
+          filterLabels={props.evaluationDiagram.filterLabels}
+          evaluatedDataAmount={props.evaluationDiagram.evaluatedDataAmount}
+          evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
+          chart={chart}
+        />
       </BaseModal>
       <EvaluationDiagramBox
-        diagramId={props.evaluationDiagram.diagramId}
-        title={props.evaluationDiagram.title}
         description={props.evaluationDiagram.description}
         filterLabels={props.evaluationDiagram.filterLabels}
         evaluatedDataAmount={props.evaluationDiagram.evaluatedDataAmount}
         evaluatedDataAmountTotal={props.evaluatedDataAmountTotal}
-        onExportAsImage={onExportAsImage}
-        isReport={props.isReport}
-        openChartInFullScreenDialog={() => setOpenFullScreenChart(true)}
-      >
-        {chart}
-      </EvaluationDiagramBox>
+        chart={chart}
+        header={
+          <Stack
+            direction="row"
+            justifyContent="space-between"
+            alignItems="center"
+            minWidth={0}
+          >
+            <Typography level="title-md" data-testid="evaluation-diagram-title">
+              {props.evaluationDiagram.title}
+            </Typography>
+            <Stack direction="row" gap={1}>
+              <IconButton
+                aria-label="Im Vollbildmodus anzeigen"
+                onClick={() => setOpenFullScreenChart(true)}
+                variant="outlined"
+                sx={{ background: "none" }}
+                color="primary"
+              >
+                <OpenInFullOutlined />
+              </IconButton>
+              <ActionsMenu
+                variant="outlined"
+                sx={{ background: "none" }}
+                color="primary"
+                actionItems={[
+                  canWrite &&
+                    !props.isReport && {
+                      label: "Anpassen",
+                      startDecorator: <Edit />,
+                      onClick: () => setIsUpdateDiagramSidebarOpen(true),
+                    },
+                  canExportData && {
+                    label: "Als PNG exportieren",
+                    startDecorator: <Download />,
+                    onClick: () => onExportAsImage?.(ImageType.PNG),
+                  },
+                  canExportData && {
+                    label: "Als SVG exportieren",
+                    startDecorator: <Download />,
+                    onClick: () => onExportAsImage?.(ImageType.SVG),
+                  },
+                  canExportData && {
+                    label: "Als XLSX exportieren",
+                    startDecorator: <Download />,
+                    onClick: exportData,
+                  },
+                  canWrite &&
+                    !props.isReport && {
+                      label: "Löschen",
+                      onClick: () =>
+                        openConfirmationDialog({
+                          onConfirm: deleteDiagram,
+                          title: "Diagramm löschen?",
+                          description: `Das Diagramm “${props.evaluationDiagram.title}” wird dann unwiderruflich gelöscht.`,
+                          cancelLabel: "Abbrechen",
+                          confirmLabel: "Löschen",
+                          color: "danger",
+                        }),
+                      startDecorator: <Delete />,
+                    },
+                ].filter(isObjectType)}
+              />
+            </Stack>
+          </Stack>
+        }
+      />
     </>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
index 92cf96811..32af88ce4 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationDiagramBox.tsx
@@ -3,175 +3,66 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  Delete,
-  Download,
-  Edit,
-  OpenInFullOutlined,
-} from "@mui/icons-material";
-import { Box, Divider, IconButton, Stack, Typography } from "@mui/joy";
-import { PropsWithChildren, useState } from "react";
-import { isObjectType } from "remeda";
+import { Divider, Stack, Typography } from "@mui/joy";
+import { ReactNode } from "react";
 
-import { useDeleteDiagram } from "@/lib/businessModules/statistics/api/mutations/useDeleteDiagram";
-import { useExportDiagramData } from "@/lib/businessModules/statistics/api/mutations/useExportDiagramData";
-import { ImageType } from "@/lib/businessModules/statistics/components/shared/charts/types";
-import { UpdateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/UpdateDiagramSidebar/UpdateDiagramSidebar";
-import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
-import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary";
-import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu";
-import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
-
-type EvaluationDiagramProps = PropsWithChildren<{
-  diagramId: string;
-  title: string;
+interface EvaluationDiagramProps {
   description: string | undefined;
   filterLabels: string[];
   evaluatedDataAmount: number;
   evaluatedDataAmountTotal: number;
-  onExportAsImage?: (imageType: ImageType) => void;
-  isReport: boolean;
-  openChartInFullScreenDialog: () => void;
-}>;
+  header?: ReactNode;
+  chart: ReactNode;
+}
 
 export function EvaluationDiagramBox({
-  diagramId,
-  title,
   description,
   filterLabels,
   evaluatedDataAmount,
   evaluatedDataAmountTotal,
-  onExportAsImage,
-  isReport,
-  children,
-  openChartInFullScreenDialog,
+  header,
+  chart,
 }: EvaluationDiagramProps) {
-  const [isUpdateDiagramSidebarOpen, setIsUpdateDiagramSidebarOpen] =
-    useState(false);
-  const exportData = useExportDiagramData(diagramId);
-  const deleteDiagram = useDeleteDiagram(diagramId);
-  const { openConfirmationDialog } = useConfirmationDialog();
-  const canWrite = useStatisticRoleChecks().canWrite();
-
   return (
-    <>
-      {isUpdateDiagramSidebarOpen && (
-        <OverlayBoundary>
-          <UpdateDiagramSidebar
-            open={isUpdateDiagramSidebarOpen}
-            onClose={() => setIsUpdateDiagramSidebarOpen(false)}
-            diagramId={diagramId}
-            title={title}
-            description={description}
-          />
-        </OverlayBoundary>
-      )}
-      <Box
-        flex="1"
-        display="flex"
-        minWidth={0}
-        data-testid="evaluation-diagram"
-        sx={{
-          minHeight: "31rem",
-          borderRadius: "sm",
-          padding: 2,
-          backgroundColor: "background.level1",
-        }}
-      >
-        <Stack flex="1" minWidth={0}>
-          <Stack
-            direction="row"
-            justifyContent="space-between"
-            alignItems="center"
-            minWidth={0}
+    <Stack
+      flex="1"
+      display="flex"
+      minWidth={0}
+      data-testid="evaluation-diagram"
+      sx={{
+        minHeight: "31rem",
+        borderRadius: "sm",
+        padding: 2,
+        backgroundColor: "background.level1",
+      }}
+    >
+      {header}
+      {chart}
+      <Stack gap={2} marginTop={2}>
+        <Divider />
+        <Typography
+          level="body-md"
+          data-testid="evaluation-diagram-description"
+        >
+          {description}
+        </Typography>
+        <Stack gap={0.5}>
+          <Typography
+            level="body-xs"
+            textColor="text.secondary"
+            data-testid="evaluation-diagram-filter"
+          >
+            Filter: {filterLabels.join(" | ")}
+          </Typography>
+          <Typography
+            level="body-xs"
+            textColor="text.secondary"
+            data-testid="evaluation-diagram-evaluated-data"
           >
-            <Typography level="title-md" data-testid="evaluation-diagram-title">
-              {title}
-            </Typography>
-            <Stack direction="row" gap={1}>
-              <IconButton
-                aria-label="Im Vollbildmodus anzeigen"
-                onClick={() => openChartInFullScreenDialog()}
-                variant="outlined"
-                sx={{ background: "none" }}
-                color="primary"
-              >
-                <OpenInFullOutlined />
-              </IconButton>
-              <ActionsMenu
-                variant="outlined"
-                sx={{ background: "none" }}
-                color="primary"
-                actionItems={[
-                  canWrite &&
-                    !isReport && {
-                      label: "Anpassen",
-                      startDecorator: <Edit />,
-                      onClick: () => setIsUpdateDiagramSidebarOpen(true),
-                    },
-                  {
-                    label: "Als PNG exportieren",
-                    startDecorator: <Download />,
-                    onClick: () => onExportAsImage?.(ImageType.PNG),
-                  },
-                  {
-                    label: "Als SVG exportieren",
-                    startDecorator: <Download />,
-                    onClick: () => onExportAsImage?.(ImageType.SVG),
-                  },
-                  {
-                    label: "Als XLSX exportieren",
-                    startDecorator: <Download />,
-                    onClick: exportData,
-                  },
-                  canWrite &&
-                    !isReport && {
-                      label: "Löschen",
-                      onClick: () =>
-                        openConfirmationDialog({
-                          onConfirm: deleteDiagram,
-                          title: "Diagramm löschen?",
-                          description: `Das Diagramm “${title}” wird dann unwiderruflich gelöscht.`,
-                          cancelLabel: "Abbrechen",
-                          confirmLabel: "Löschen",
-                          color: "danger",
-                        }),
-                      startDecorator: <Delete />,
-                    },
-                ].filter(isObjectType)}
-              />
-            </Stack>
-          </Stack>
-          <Stack flex="1" minWidth={0}>
-            {children}
-          </Stack>
-          <Stack gap={2} marginTop={2}>
-            <Divider />
-            <Typography
-              level="body-md"
-              data-testid="evaluation-diagram-description"
-            >
-              {description}
-            </Typography>
-            <Stack gap={0.5}>
-              <Typography
-                level="body-xs"
-                textColor="text.secondary"
-                data-testid="evaluation-diagram-filter"
-              >
-                Filter: {filterLabels.join(" | ")}
-              </Typography>
-              <Typography
-                level="body-xs"
-                textColor="text.secondary"
-                data-testid="evaluation-diagram-evaluated-data"
-              >
-                {`Ausgewertete Daten: ${evaluatedDataAmount} von ${evaluatedDataAmountTotal}`}
-              </Typography>
-            </Stack>
-          </Stack>
+            {`Ausgewertete Daten: ${evaluatedDataAmount} von ${evaluatedDataAmountTotal}`}
+          </Typography>
         </Stack>
-      </Box>
-    </>
+      </Stack>
+    </Stack>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
index dc8ca535e..0ea9c5570 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/EvaluationAccordion/EvaluationSortOrderSelect.tsx
@@ -11,8 +11,6 @@ import { Select } from "@mui/joy";
 import { Evaluation } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 
 export const EvaluationSortOrder = {
-  NewestFirst: "NEWEST_FIRST",
-  OldestFirst: "OLDEST_FIRST",
   NameAscending: "NAME_ASCENDING",
   NameDescending: "NAME_DESCENDING",
 } as const;
@@ -20,8 +18,6 @@ export type EvaluationSortOrder =
   (typeof EvaluationSortOrder)[keyof typeof EvaluationSortOrder];
 
 export const evaluationSortOrderOptions: EnumMap<EvaluationSortOrder> = {
-  [EvaluationSortOrder.NewestFirst]: "Neueste zuerst",
-  [EvaluationSortOrder.OldestFirst]: "Älteste zuerst",
   [EvaluationSortOrder.NameAscending]: "Alphabetisch A-Z",
   [EvaluationSortOrder.NameDescending]: "Alphabetisch Z-A",
 };
@@ -31,14 +27,6 @@ export function sortEvaluations(
   sortOrder: EvaluationSortOrder,
 ): Evaluation[] {
   switch (sortOrder) {
-    case EvaluationSortOrder.NewestFirst:
-      return evaluations.toSorted(
-        (a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
-      );
-    case EvaluationSortOrder.OldestFirst:
-      return evaluations.toSorted(
-        (a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
-      );
     case EvaluationSortOrder.NameAscending:
       return evaluations.toSorted((a, b) => a.name.localeCompare(b.name, "de"));
     case EvaluationSortOrder.NameDescending:
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
index 01bd50b92..37e92ddd5 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ChoroplethMap.tsx
@@ -8,8 +8,8 @@ import { useState } from "react";
 import { isNonNullish, randomString } from "remeda";
 
 import {
+  DiagramCharacteristicParameter,
   DiagramColorScheme,
-  EvaluationChoroplethDiagramConfiguration,
   EvaluationDiagramChoroplethMap,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import {
@@ -20,7 +20,8 @@ import { getChoroplethAggregationMethod } from "@/lib/businessModules/statistics
 
 export interface ChoroplethMapProps {
   diagramData: EvaluationDiagramChoroplethMap["data"];
-  configuration: EvaluationChoroplethDiagramConfiguration;
+  colorScheme: DiagramColorScheme;
+  characteristicParameter?: DiagramCharacteristicParameter;
   geoJson: string;
   eChartApi?: (eChartApi: ChartApi) => void;
 }
@@ -71,10 +72,10 @@ export function ChoroplethMap(props: ChoroplethMapProps) {
       min: min * 0.9999999,
       max: max * 1.0000001, //Otherwise the map breaks if min=max
       inRange: {
-        color: getColor(props.configuration.colorScheme),
+        color: getColor(props.colorScheme),
       },
       text: [
-        `${getChoroplethAggregationMethod(props.configuration.characteristicParameter)}\n\n${max}`,
+        `${getChoroplethAggregationMethod(props.characteristicParameter)}\n\n${max}`,
         min.toString(),
       ],
       textGap: 5,
@@ -83,9 +84,7 @@ export function ChoroplethMap(props: ChoroplethMapProps) {
 
     series: [
       {
-        name: getChoroplethAggregationMethod(
-          props.configuration.characteristicParameter,
-        ),
+        name: getChoroplethAggregationMethod(props.characteristicParameter),
         type: "map",
         map: mapId,
         data: props.diagramData,
diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
index cddea48e9..95b7bae14 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/Histogram.tsx
@@ -4,17 +4,19 @@
  */
 
 import {
+  DiagramGrouping,
+  DiagramScaling,
   DiagramType,
   EvaluationDiagramBarChart,
   EvaluationDiagramHistogram,
-  EvaluationHistogramDiagramConfiguration,
 } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
 import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart";
 import { ChartApi } from "@/lib/businessModules/statistics/components/shared/charts/EChart";
 
 interface HistogramProps {
   diagramData: EvaluationDiagramHistogram["data"];
-  configuration: EvaluationHistogramDiagramConfiguration;
+  grouping?: DiagramGrouping;
+  scaling?: DiagramScaling;
   eChartApi?: (eChartApi: ChartApi) => void;
 }
 
@@ -33,15 +35,13 @@ export function Histogram(props: HistogramProps) {
   const data = mapToBarChartDiagramData(props.diagramData);
   const numAttributes = props.diagramData[0]?.attributes?.length ?? 1;
   const barWidth =
-    props.configuration.grouping === "STACKED"
-      ? "99.8%"
-      : `${99.8 / numAttributes}%`;
+    props.grouping === "STACKED" ? "99.8%" : `${99.8 / numAttributes}%`;
 
   return (
     <BarChart
       filterSetData={data}
-      grouping={props.configuration.grouping}
-      scaling={props.configuration.scaling}
+      grouping={props.grouping}
+      scaling={props.scaling}
       orientation={"VERTICAL"}
       eChartApi={props.eChartApi}
       barGap={0}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
index f21710ff0..4f24e9639 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/SaveStatisticStep.tsx
@@ -11,6 +11,7 @@ import { useState } from "react";
 import { isDefined } from "remeda";
 
 import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { CollapsableList } from "@/lib/businessModules/statistics/components/shared/CollapsableList";
 import { SaveStatisticStepFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/SaveStatisticStep/saveStatisticStepFormModel";
 import { CreateStatisticFromScratchFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/createStatisticFromScratchFormModel";
 import { CreateStatisticFromTemplateFormModel } from "@/lib/businessModules/statistics/components/statistics/CreateStatisticSidebar/createStatisticFromTemplateFormModel";
@@ -89,16 +90,8 @@ export function SaveStatisticStep() {
       <Stack gap={2}>
         <Typography level="title-md">Fachmodule</Typography>
         <Typography level="body-md">{getBusinessModuleName(values)}</Typography>
-      </Stack>
-      <Stack gap={2}>
         <Typography level="title-md">Attribute</Typography>
-        <Stack gap={1}>
-          {getSelectedAttributeNames(values).map((attributeName, index) => (
-            <Typography key={index} level="body-md">
-              {attributeName}
-            </Typography>
-          ))}
-        </Stack>
+        <CollapsableList items={getSelectedAttributeNames(values)} />
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx
new file mode 100644
index 000000000..027bb6e28
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar.tsx
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { useAddEvaluationTemplate } from "@/lib/businessModules/statistics/api/mutations/useAddEvaluationTemplate";
+import { useGetEvaluationDetails } from "@/lib/businessModules/statistics/api/queries/useGetEvaluationDetails";
+import { SaveEvaluationTemplateStep } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep";
+import { SidebarStepper } from "@/lib/shared/components/SidebarStepper/SidebarStepper";
+
+export function SaveAsEvaluationTemplateSidebar({
+  open,
+  onClose,
+  evaluationId,
+}: {
+  open: boolean;
+  onClose: () => void;
+  evaluationId: string;
+}) {
+  const evaluationDetails = useGetEvaluationDetails(evaluationId);
+  const addEvaluationTemplate = useAddEvaluationTemplate(onClose);
+
+  return (
+    <SidebarStepper
+      onClose={onClose}
+      open={open}
+      onSubmit={(model) =>
+        addEvaluationTemplate(evaluationId, model).then(() => void 0)
+      }
+      initialValues={{
+        name: "",
+        description: "",
+      }}
+      steps={[
+        {
+          type: "StandardStep",
+          step: {
+            title: "Vorlage speichern",
+            content: (
+              <SaveEvaluationTemplateStep
+                evaluationDetails={evaluationDetails}
+              />
+            ),
+          },
+        },
+      ]}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx
new file mode 100644
index 000000000..08883d348
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/SaveEvaluationTemplateStep.tsx
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Alert } from "@eshg/lib-portal/components/Alert";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
+import { Divider, Stack, Typography } from "@mui/joy";
+
+import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { EvaluationDetails } from "@/lib/businessModules/statistics/api/models/evaluationDetails";
+import { CollapsableList } from "@/lib/businessModules/statistics/components/shared/CollapsableList";
+import { SaveEvaluationTemplateStepFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel";
+import { SearchableGroups } from "@/lib/shared/components/SearchableGroups";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+import { businessModuleNames } from "@/lib/shared/components/procedures/constants";
+
+export function SaveEvaluationTemplateStep({
+  evaluationDetails,
+}: {
+  evaluationDetails: EvaluationDetails;
+}) {
+  const fieldName =
+    createFieldNameMapper<SaveEvaluationTemplateStepFormModel>();
+
+  const groups = evaluationDetails.analyses.map((evaluation) => ({
+    name: evaluation.name,
+    inAccordion: true,
+    items: evaluation.diagramTitles.map((it, index) => ({
+      key: index.toString(),
+      searchableValue: it,
+    })),
+  }));
+
+  return (
+    <Stack gap={3}>
+      <Stack gap={2}>
+        <InputField
+          name={fieldName("name")}
+          label="Name der Vorlage"
+          required="Bitte Name angeben."
+        />
+        <TextareaField name={fieldName("description")} label="Beschreibung" />
+      </Stack>
+      <Divider />
+      <Typography level="h3" component="h2">
+        Zusammenfassung
+      </Typography>
+      <Stack gap={2}>
+        <Stack gap={1}>
+          <Typography level="title-md">Fachmodule</Typography>
+          <Typography level="body-md">
+            {
+              businessModuleNames[
+                mapToApiBusinessModule(evaluationDetails.businessModule)
+              ]
+            }
+          </Typography>
+        </Stack>
+        <Stack gap={1}>
+          <Typography level="title-md">Attribute</Typography>
+          <CollapsableList items={evaluationDetails.attributeLabels} />
+        </Stack>
+        <Stack gap={2}>
+          <Typography level="title-md">Analysen</Typography>
+          <SearchableGroups
+            groups={groups}
+            renderItem={(item) => item.searchableValue}
+            hideSearch={true}
+          />
+        </Stack>
+        <Alert
+          variant="soft"
+          color="primary"
+          message="Die Analysen und Diagramme der Vorlage werden an die Daten des gewählten Betrachtungszeitraums angepasst."
+        />
+      </Stack>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts
new file mode 100644
index 000000000..c951612e4
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export interface SaveEvaluationTemplateStepFormModel {
+  name: string;
+  description: string;
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts
new file mode 100644
index 000000000..75437739c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/saveAsEvaluationTemplateFormModel.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { SaveEvaluationTemplateStepFormModel } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveEvaluationTemplateStep/saveEvaluationTemplateStepFormModel";
+
+export type SaveAsEvaluationTemplateFormModel =
+  SaveEvaluationTemplateStepFormModel;
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
index 719399e9f..ec6a4f906 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StateChip.tsx
@@ -12,6 +12,7 @@ const statusNames = {
   [ApiStatisticState.Creating]: "Wird erstellt",
   [ApiStatisticState.CopyOngoing]: "Wird kopiert",
   [ApiStatisticState.Updating]: "Wird aktualisiert",
+  [ApiStatisticState.Deleting]: "Wird gelöscht",
 } satisfies Record<ApiStatisticState, string>;
 
 const statusColors = {
@@ -20,6 +21,7 @@ const statusColors = {
   [ApiStatisticState.Creating]: "warning",
   [ApiStatisticState.CopyOngoing]: "warning",
   [ApiStatisticState.Updating]: "warning",
+  [ApiStatisticState.Deleting]: "warning",
 } satisfies Record<ApiStatisticState, ChipProps["color"]>;
 
 export function StateChip({ value }: { value: ApiStatisticState }) {
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
index 037950cff..050998d8c 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsOverview.tsx
@@ -8,10 +8,10 @@
 import {
   ApiAvailableDataSource,
   ApiEvaluationTemplate,
-  ApiGetStatisticsResponse,
 } from "@eshg/employee-portal-api/statistics";
 import { useState } from "react";
 
+import { StatisticOverview } from "@/lib/businessModules/statistics/api/models/statisticOverview";
 import {
   CreateStatisticSidebar,
   OpenSidebarKind,
@@ -20,15 +20,15 @@ import {
 import { StatisticsTable } from "./StatisticsTable";
 
 export interface StatisticsOverviewProps {
-  statisticsResponse: ApiGetStatisticsResponse;
-  isFetchingStatistics: boolean;
+  statisticsOverview: StatisticOverview;
+  isFetchingStatisticsOverview: boolean;
   dataSources: ApiAvailableDataSource[];
   templates: ApiEvaluationTemplate[];
 }
 
 export function StatisticsOverview({
-  statisticsResponse,
-  isFetchingStatistics,
+  statisticsOverview,
+  isFetchingStatisticsOverview,
   dataSources,
   templates,
 }: StatisticsOverviewProps) {
@@ -43,9 +43,8 @@ export function StatisticsOverview({
         setOpenSidebar={setOpenSidebar}
       />
       <StatisticsTable
-        data={statisticsResponse}
-        loading={isFetchingStatistics}
-        onTemplateClick={() => setOpenSidebar("FROM_TEMPLATE")}
+        statisticOverview={statisticsOverview}
+        loading={isFetchingStatisticsOverview}
         onCreateStatisticClick={() => setOpenSidebar("FROM_SCRATCH")}
       />
     </>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
index 359fc4789..38d7643dd 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/StatisticsTable.tsx
@@ -4,26 +4,33 @@
  */
 
 import {
-  ApiGetStatisticsResponse,
   ApiStatisticInfo,
   ApiStatisticState,
   ApiStatisticsFeature,
-  ApiUser,
 } from "@eshg/employee-portal-api/statistics";
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { Add } from "@mui/icons-material";
-import DeleteIcon from "@mui/icons-material/Delete";
-import Edit from "@mui/icons-material/Edit";
-import FileCopyIcon from "@mui/icons-material/FileCopy";
-import FullscreenIcon from "@mui/icons-material/Fullscreen";
-import { Box, Button } from "@mui/joy";
+import {
+  Add,
+  Delete,
+  Download,
+  Edit,
+  FileCopy,
+  Menu,
+} from "@mui/icons-material";
+import { Box, Button, ColorPaletteProp } from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
-import { isDefined } from "remeda";
+import { doNothing, isDefined, isNonNull, isPlainObject } from "remeda";
 
+import {
+  StatisticOverview,
+  StatisticOverviewTableItem,
+} from "@/lib/businessModules/statistics/api/models/statisticOverview";
 import { getStatisticsQueryKey } from "@/lib/businessModules/statistics/api/queries/apiQueryKeys";
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import { DuplicateStatisticSidebar } from "@/lib/businessModules/statistics/components/statistics/DuplicateStatisticSidebar/DuplicateStatisticSidebar";
+import { SaveAsEvaluationTemplateSidebar } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar";
 import { StatisticNameChangeModal } from "@/lib/businessModules/statistics/components/statistics/details/StatisticNameChangeModal";
 import { useDeleteStatisticWithConfirmation } from "@/lib/businessModules/statistics/components/statistics/useDeleteStatisticWithConfirmation";
 import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
@@ -42,19 +49,7 @@ import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl
 
 import { StateChip } from "./StateChip";
 
-type StatisticWithUserInfo = ApiStatisticInfo & {
-  user: ApiUser | undefined;
-};
-
-const columnHelper = createColumnHelper<StatisticWithUserInfo>();
-
-function TemplatesButton({ onClick }: { onClick: () => void }) {
-  return (
-    <Button variant="outlined" size="md" onClick={onClick}>
-      Vorlagen
-    </Button>
-  );
-}
+const columnHelper = createColumnHelper<StatisticOverviewTableItem>();
 
 function CreateStatisticsButton({ onClick }: { onClick: () => void }) {
   return (
@@ -69,9 +64,11 @@ function columns(
   canDelete: (creatorUserId: string) => boolean,
   canWrite: (creatorUserId: string) => boolean,
   canUpdateStatistic: (creatorUserId: string) => boolean,
-  onDuplicate: (item: StatisticWithUserInfo) => void,
+  onDuplicate: (item: StatisticOverviewTableItem) => void,
   duplicateStatisticEnabled: boolean,
   onNameChange: (id: string, name: string) => void,
+  onSaveAsTemplate: (item: StatisticOverviewTableItem) => void,
+  exportDataFeatureToggle: boolean,
 ) {
   return [
     columnHelper.accessor("name", {
@@ -82,6 +79,15 @@ function columns(
         },
       },
     }),
+    columnHelper.accessor("dataSourceName", {
+      header: "Datenquelle",
+      meta: {
+        canNavigate: {
+          parentRow: true,
+        },
+        width: "8rem",
+      },
+    }),
     columnHelper.accessor("timeRangeStart", {
       header: "Zeitraum Start",
       cell: (props) => formatDate(props.getValue(), "DE"),
@@ -125,58 +131,54 @@ function columns(
       cell: (props) => (
         <ActionsMenu
           actionItems={[
-            {
-              label: "Anzeigen",
-              onClick: routes.statistics.details(props.row.original.id).index,
+            canUpdateStatistic(props.row.original.userId) && {
+              label: "Name ändern",
+              onClick: () =>
+                onNameChange(props.row.original.id, props.row.original.name),
+              disabled:
+                props.row.original.state !== ApiStatisticState.Completed,
+              startDecorator: <Edit />,
+            },
+            canWrite(props.row.original.userId) &&
+              duplicateStatisticEnabled && {
+                label: "Duplizieren",
+                onClick: () => {
+                  onDuplicate(props.row.original);
+                },
+                disabled:
+                  props.row.original.state !== ApiStatisticState.Completed,
+                startDecorator: <FileCopy />,
+              },
+            props.row.original.anonymized &&
+              exportDataFeatureToggle && {
+                label: "Daten exportieren",
+                onClick: doNothing,
+                disabled:
+                  props.row.original.state !== ApiStatisticState.Completed,
+                startDecorator: <Download />,
+              },
+            canWrite(props.row.original.userId) && {
+              label: "Als Vorlage speichern",
+              onClick: () => {
+                onSaveAsTemplate(props.row.original);
+              },
               disabled:
                 props.row.original.state !== ApiStatisticState.Completed,
-              startDecorator: <FullscreenIcon />,
+              startDecorator: <Menu />,
+            },
+            canDelete(props.row.original.userId) && {
+              label: "Löschen",
+              onClick: () =>
+                deleteStatisticWithConfirmation(
+                  props.row.original.id,
+                  props.row.original.name,
+                ),
+              disabled:
+                props.row.original.state === ApiStatisticState.CopyOngoing,
+              startDecorator: <Delete />,
+              color: "danger" as ColorPaletteProp,
             },
-            ...(canUpdateStatistic(props.row.original.userId)
-              ? [
-                  {
-                    label: "Name ändern",
-                    onClick: () =>
-                      onNameChange(
-                        props.row.original.id,
-                        props.row.original.name,
-                      ),
-                    disabled:
-                      props.row.original.state !== ApiStatisticState.Completed,
-                    startDecorator: <Edit />,
-                  },
-                ]
-              : []),
-            ...(canWrite(props.row.original.userId) && duplicateStatisticEnabled
-              ? [
-                  {
-                    label: "Duplizieren",
-                    onClick: () => {
-                      onDuplicate(props.row.original);
-                    },
-                    disabled:
-                      props.row.original.state !== ApiStatisticState.Completed,
-                    startDecorator: <FileCopyIcon />,
-                  },
-                ]
-              : []),
-            ...(canDelete(props.row.original.userId)
-              ? [
-                  {
-                    label: "Löschen",
-                    onClick: () =>
-                      deleteStatisticWithConfirmation(
-                        props.row.original.id,
-                        props.row.original.name,
-                      ),
-                    disabled:
-                      props.row.original.state ===
-                      ApiStatisticState.CopyOngoing,
-                    startDecorator: <DeleteIcon />,
-                  },
-                ]
-              : []),
-          ]}
+          ].filter(isPlainObject)}
         />
       ),
       meta: {
@@ -188,16 +190,15 @@ function columns(
 }
 
 export interface StatisticsTableProps {
-  data: ApiGetStatisticsResponse;
+  statisticOverview: StatisticOverview;
   loading: boolean;
-  onTemplateClick: () => void;
   onCreateStatisticClick: () => void;
 }
 
 export function StatisticsTable({
-  data,
+  statisticOverview,
   loading,
-  onTemplateClick,
+
   onCreateStatisticClick,
 }: StatisticsTableProps) {
   const tableControl = useTableControl({
@@ -211,20 +212,21 @@ export function StatisticsTable({
   const duplicateStatisticEnabled = useIsNewFeatureEnabled(
     ApiStatisticsFeature.CloneStatistic,
   );
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+
   const [duplicateStatisticAction, setDuplicateStatisticAction] =
-    useState<StatisticWithUserInfo>();
+    useState<StatisticOverviewTableItem>();
   const [nameChangeAction, setNameChangeAction] =
     useState<Pick<ApiStatisticInfo, "id" | "name">>();
+  const [
+    saveAsEvaluationTemplateSidebarEvaluationId,
+    setSaveAsEvaluationTemplateSidebarEvaluationId,
+  ] = useState<string | null>(null);
 
   const userPermissions = useStatisticRoleChecks();
 
-  const tableData: StatisticWithUserInfo[] = data.statistics.map(
-    (statistic) => ({
-      ...statistic,
-      user: data.resolvedUsers[statistic.userId],
-    }),
-  );
-
   const deleteStatisticsWithConfirmation = useDeleteStatisticWithConfirmation();
 
   return (
@@ -240,12 +242,13 @@ export function StatisticsTable({
                 loading={loading}
                 queryKey={getStatisticsQueryKey([])}
               />,
-              userPermissions.canWrite() && (
-                <TemplatesButton
-                  key="displayTemplates"
-                  onClick={onTemplateClick}
-                />
-              ),
+              <InternalLinkButton
+                key="evaluationTemplatesOverview"
+                variant="outlined"
+                href={routes.statistics.evaluationTemplates.index}
+              >
+                Auswertungsvorlagen
+              </InternalLinkButton>,
               userPermissions.canWrite() && (
                 <CreateStatisticsButton
                   key="createStatistic"
@@ -259,7 +262,7 @@ export function StatisticsTable({
         <TableSheet
           footer={
             <Pagination
-              totalCount={data.totalNumberOfElements}
+              totalCount={statisticOverview.totalNumberOfElements}
               {...tableControl.paginationProps}
             />
           }
@@ -267,7 +270,7 @@ export function StatisticsTable({
           <DataTable
             wrapContent
             minWidth="58rem"
-            data={tableData}
+            data={statisticOverview.data}
             columns={columns(
               deleteStatisticsWithConfirmation,
               userPermissions.canDelete,
@@ -276,14 +279,17 @@ export function StatisticsTable({
               setDuplicateStatisticAction,
               duplicateStatisticEnabled,
               (id, name) => setNameChangeAction({ id, name }),
+              (item) => setSaveAsEvaluationTemplateSidebarEvaluationId(item.id),
+              exportDataFeatureToggle,
             )}
             sorting={tableControl.tableSorting}
-            rowNavRoute={(row) =>
-              row.original.state === ApiStatisticState.Completed
-                ? routes.statistics.details(row.original.id).index
-                : undefined
-            }
-            focusColumnHeader="Name"
+            rowNavigation={{
+              route: (row) =>
+                row.original.state === ApiStatisticState.Completed
+                  ? routes.statistics.details(row.original.id).index
+                  : undefined,
+              focusColumnAccessorKey: "name",
+            }}
             enableSortingRemoval={false}
             noDataComponent={() => (
               <Box flex={1} alignContent="center">
@@ -307,6 +313,15 @@ export function StatisticsTable({
           />
         </OverlayBoundary>
       )}
+      {isNonNull(saveAsEvaluationTemplateSidebarEvaluationId) && (
+        <OverlayBoundary>
+          <SaveAsEvaluationTemplateSidebar
+            open={true}
+            onClose={() => setSaveAsEvaluationTemplateSidebarEvaluationId(null)}
+            evaluationId={saveAsEvaluationTemplateSidebarEvaluationId}
+          />
+        </OverlayBoundary>
+      )}
 
       {isDefined(nameChangeAction) && (
         <OverlayBoundary>
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
index a464d47de..cc7af1baa 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard.tsx
@@ -13,6 +13,7 @@ export interface BusinessModuleInformationCardProps {
   dataSource: string;
   datasetAmount: number;
   attributeLabels: string[];
+  anonymized: boolean;
 }
 
 export function BusinessModuleInformationCard(
@@ -23,6 +24,10 @@ export function BusinessModuleInformationCard(
       label: "Datenquelle",
       value: props.dataSource,
     },
+    {
+      label: "Anonymisierung der Daten",
+      value: props.anonymized === true ? "Ja" : "Nein",
+    },
     {
       label: "Datensätze",
       value: props.datasetAmount.toString(),
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
index c4229b4ed..ab8ce10fc 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx
@@ -98,7 +98,14 @@ export function ConfigureHistogramChartStep({
           name={fieldName("binning")}
           label="Bins"
         />
-        {showBins && <SliderField min={1} max={50} name={fieldName("bins")} />}
+        {showBins && (
+          <SliderField
+            min={1}
+            max={50}
+            name={fieldName("bins")}
+            ariaLabel="Anzahl Bins"
+          />
+        )}
       </Stack>
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
index bb431da92..e125cfe49 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/DetailsInformationCard.tsx
@@ -3,9 +3,17 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { AddchartOutlined, Delete, Edit, FileCopy } from "@mui/icons-material";
-import { Button, Stack } from "@mui/joy";
+import {
+  AddchartOutlined,
+  Delete,
+  Download,
+  Edit,
+  FileCopy,
+  Menu,
+} from "@mui/icons-material";
+import { Button, ColorPaletteProp, Stack } from "@mui/joy";
 import { isPlainObject } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
@@ -21,6 +29,7 @@ export interface DetailsInformationCardProps {
   canWrite: boolean;
   canDelete: boolean;
   canUpdateStatistic: boolean;
+  canExportData: boolean;
   start: Date;
   end: Date;
   createdAt: Date;
@@ -30,20 +39,29 @@ export interface DetailsInformationCardProps {
   onNameChangeClicked: () => void;
   onStatisticDeleteClicked: () => void;
   onStatisticDuplicateClicked: () => void;
+  onSaveEvaluationTemplateClicked: () => void;
+  onDataExport: () => Promise<void>;
 }
 
 export function DetailsInformationCard(props: DetailsInformationCardProps) {
-  const cloneStatisticFeatureToggle = useIsNewFeatureEnabled("CLONE_STATISTIC");
-
+  const cloneStatisticFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.CloneStatistic,
+  );
   const canDuplicateStatistic = props.canWrite && cloneStatisticFeatureToggle;
-  const { canDelete, canUpdateStatistic } = props;
+
+  const exportDataFeatureToggle = useIsNewFeatureEnabled(
+    ApiStatisticsFeature.FakeAnonymization,
+  );
+  const canExportData = props.canExportData && exportDataFeatureToggle;
+
+  const { canDelete, canUpdateStatistic, canWrite } = props;
 
   return (
     <InfoTile
       name="aggregation-details"
       title="Details"
       footer={
-        props.canWrite && (
+        canWrite && (
           <Stack
             alignItems={{ md: "start" }}
             marginTop={2}
@@ -69,7 +87,10 @@ export function DetailsInformationCard(props: DetailsInformationCardProps) {
         )
       }
       controls={
-        (canUpdateStatistic || canDuplicateStatistic || canDelete) && (
+        (canUpdateStatistic ||
+          canDuplicateStatistic ||
+          canDelete ||
+          canWrite) && (
           <ActionsMenu
             actionItems={[
               canUpdateStatistic && {
@@ -77,15 +98,26 @@ export function DetailsInformationCard(props: DetailsInformationCardProps) {
                 onClick: () => props.onNameChangeClicked(),
                 startDecorator: <Edit />,
               },
+              canWrite && {
+                label: "Als Vorlage speichern",
+                onClick: () => props.onSaveEvaluationTemplateClicked(),
+                startDecorator: <Menu />,
+              },
               canDuplicateStatistic && {
                 label: "Duplizieren",
                 onClick: () => props.onStatisticDuplicateClicked(),
                 startDecorator: <FileCopy />,
               },
+              canExportData && {
+                label: "Daten exportieren",
+                onClick: () => props.onDataExport(),
+                startDecorator: <Download />,
+              },
               canDelete && {
                 label: "Löschen",
                 onClick: () => props.onStatisticDeleteClicked(),
                 startDecorator: <Delete />,
+                color: "danger" as ColorPaletteProp,
               },
             ].filter(isPlainObject)}
           />
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
index 222379974..8cd7dcec3 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/StatisticDetails.tsx
@@ -9,7 +9,7 @@ import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
 import { Stack } from "@mui/joy";
 import { useRouter } from "next/navigation";
 import { useState } from "react";
-import { isDefined } from "remeda";
+import { isDefined, isNonNull } from "remeda";
 
 import { GeoShapeInfo } from "@/lib/businessModules/statistics/api/models/geoShapesTableView";
 import { StatisticDetailsView } from "@/lib/businessModules/statistics/api/models/statisticDetailsViewTypes";
@@ -18,6 +18,7 @@ import {
   DuplicateStatisticSidebar,
   OriginalStatistic,
 } from "@/lib/businessModules/statistics/components/statistics/DuplicateStatisticSidebar/DuplicateStatisticSidebar";
+import { SaveAsEvaluationTemplateSidebar } from "@/lib/businessModules/statistics/components/statistics/SaveAsEvaluationTemplateSidebar/SaveAsEvaluationTemplateSidebar";
 import { BusinessModuleInformationCardProps } from "@/lib/businessModules/statistics/components/statistics/details/BusinessModuleInformationCard";
 import { CreateDiagramSidebar } from "@/lib/businessModules/statistics/components/statistics/details/CreateDiagramSidebar/CreateDiagramSidebar";
 import { CreateEvaluationSidebar } from "@/lib/businessModules/statistics/components/statistics/details/CreateEvaluationSidebar/CreateEvaluationSidebar";
@@ -44,6 +45,10 @@ export function StatisticDetails(
   const [createDiagramSidebarState, setCreateDiagramSidebarState] = useState<
     SidebarState<{ evaluationId: string }>
   >({ open: false });
+  const [
+    saveAsEvaluationTemplateSidebarEvaluationId,
+    setSaveAsEvaluationTemplateSidebarEvaluationId,
+  ] = useState<string | null>(null);
 
   const [duplicateStatisticAction, setDuplicateStatisticAction] =
     useState<OriginalStatistic>();
@@ -60,6 +65,7 @@ export function StatisticDetails(
     canDelete: canDelete(props.userId),
     canUpdateStatistic: canUpdateStatistic(props.userId),
     canWrite: canWrite(),
+    canExportData: props.anonymized,
     start: props.start,
     end: props.end,
     createdAt: props.createdAt,
@@ -79,6 +85,9 @@ export function StatisticDetails(
         timeRangeStart: props.start,
         timeRangeEnd: props.end,
       }),
+    onSaveEvaluationTemplateClicked: () =>
+      setSaveAsEvaluationTemplateSidebarEvaluationId(props.statisticId),
+    onDataExport: () => Promise.resolve(),
   };
 
   const businessModuleInformationCardsProps: BusinessModuleInformationCardProps[] =
@@ -88,11 +97,21 @@ export function StatisticDetails(
         dataSource: props.dataSource.name,
         datasetAmount: props.dataSource.datasetAmount,
         attributeLabels: props.dataSource.attributeLabels,
+        anonymized: props.anonymized,
       },
     ];
 
   return (
     <Stack gap={6}>
+      {isNonNull(saveAsEvaluationTemplateSidebarEvaluationId) && (
+        <OverlayBoundary>
+          <SaveAsEvaluationTemplateSidebar
+            open={true}
+            onClose={() => setSaveAsEvaluationTemplateSidebarEvaluationId(null)}
+            evaluationId={saveAsEvaluationTemplateSidebarEvaluationId}
+          />
+        </OverlayBoundary>
+      )}
       {isCreateEvaluationSidebarOpen && (
         <OverlayBoundary>
           <CreateEvaluationSidebar
@@ -162,6 +181,7 @@ export function StatisticDetails(
         onDiagramCreateClicked={(evaluationId) =>
           setCreateDiagramSidebarState({ open: true, data: { evaluationId } })
         }
+        anonymized={props.anonymized}
       />
     </Stack>
   );
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
index ce01c5519..14227ad87 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/details/reports/StatisticReports.tsx
@@ -9,9 +9,18 @@ import {
   ApiReportState,
   ApiStatisticState,
 } from "@eshg/employee-portal-api/statistics";
+import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
-import { Add } from "@mui/icons-material";
-import { Box, Button, Stack } from "@mui/joy";
+import { Add, NotInterestedOutlined } from "@mui/icons-material";
+import {
+  Box,
+  Button,
+  Card,
+  CardActions,
+  CardContent,
+  Stack,
+  Typography,
+} from "@mui/joy";
 import { createColumnHelper } from "@tanstack/react-table";
 import { useState } from "react";
 
@@ -211,7 +220,7 @@ export function StatisticReports({
     });
   }
 
-  return (
+  return data.anonymized ? (
     <>
       {openCreateReportSidebar && (
         <OverlayBoundary>
@@ -286,12 +295,14 @@ export function StatisticReports({
                     <NoSearchResults info="Keine Reports vorhanden" />
                   </Box>
                 )}
-                rowNavRoute={(row) =>
-                  row.original.type !== "SERIES" &&
-                  row.original.status === ApiStatisticState.Completed
-                    ? routes.reports.details(row.original.reportId).index
-                    : undefined
-                }
+                rowNavigation={{
+                  route: (row) =>
+                    row.original.type !== "SERIES" &&
+                    row.original.status === ApiStatisticState.Completed
+                      ? routes.reports.details(row.original.reportId).index
+                      : undefined,
+                  focusColumnAccessorKey: "name",
+                }}
                 enableSortingRemoval={false}
                 sorting={{
                   manualSorting: false,
@@ -317,5 +328,32 @@ export function StatisticReports({
         </Stack>
       </Stack>
     </>
+  ) : (
+    <Card
+      variant="plain"
+      sx={{
+        alignSelf: "center",
+        borderRadius: "lg",
+        padding: 3,
+        gap: 3,
+        alignItems: "center",
+      }}
+    >
+      <CardContent sx={{ alignItems: "center", gap: 2 }}>
+        <NotInterestedOutlined sx={{ width: 130, height: 130 }} />
+        <Typography level="h1">Reports nicht verfügbar</Typography>
+        <Typography level="body-md">
+          Reports für Auswertungen mit nicht anonymisierten Daten stehen nicht
+          zur Verfügung.
+        </Typography>
+      </CardContent>
+      <CardActions sx={{ padding: 0 }}>
+        <InternalLinkButton
+          href={routes.statistics.details(data.statisticId).index}
+        >
+          Zu den Analysen
+        </InternalLinkButton>
+      </CardActions>
+    </Card>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx b/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx
new file mode 100644
index 000000000..3a83105ed
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/evaluationTemplates/EvaluationTemplatesOverview.tsx
@@ -0,0 +1,189 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { Add, Delete, Edit } from "@mui/icons-material";
+import { Box } from "@mui/joy";
+import { createColumnHelper } from "@tanstack/react-table";
+import { doNothing, isPlainObject } from "remeda";
+
+import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule";
+import { EvaluationTemplateWithUserInfo } from "@/lib/businessModules/statistics/api/models/evaluationTemplatesOverview";
+import { useDeleteEvaluationTemplate } from "@/lib/businessModules/statistics/api/mutations/useDeleteEvaluationTemplate";
+import { useGetEvaluationTemplatesOverview } from "@/lib/businessModules/statistics/api/queries/useGetEvaluationTemplatesOverview";
+import { useStatisticRoleChecks } from "@/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks";
+import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
+import {
+  ActionsItem,
+  ActionsMenu,
+} from "@/lib/shared/components/buttons/ActionsMenu";
+import { useConfirmationDialog } from "@/lib/shared/components/confirmationDialog/ConfirmationDialogProvider";
+import { Pagination } from "@/lib/shared/components/pagination/Pagination";
+import { businessModuleNames } from "@/lib/shared/components/procedures/constants";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+import { UserLink } from "@/lib/shared/components/users/UserLink";
+import { usePagination } from "@/lib/shared/hooks/table/usePagination";
+import { useTableSorting } from "@/lib/shared/hooks/table/useTableSorting";
+import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
+
+export function EvaluationTemplatesOverview() {
+  const userPermissions = useStatisticRoleChecks();
+  const { resetPageNumber, page, pageSize, getPaginationProps } =
+    usePagination();
+  const { sortKey, sortDirection, manualSortingProps } = useTableSorting({
+    onSortingChange: () => resetPageNumber(),
+    initialSorting: {
+      id: "createdAt",
+      desc: true,
+    },
+  });
+
+  const evaluationTemplatesOverview = useGetEvaluationTemplatesOverview({
+    page,
+    pageSize,
+    sortDirection,
+    sortKey,
+  });
+  const writePermission = useHasUserRoleCheck(
+    ApiUserRole.StatisticsStatisticsWrite,
+  );
+  const { openConfirmationDialog } = useConfirmationDialog();
+  const deleteEvaluationTemplate = useDeleteEvaluationTemplate();
+
+  function deleteTemplateWithConfirmation(templateId: string) {
+    openConfirmationDialog({
+      title: "Vorlage löschen?",
+      description: "Möchten Sie die Vorlage wirklich löschen?",
+      confirmLabel: "Löschen",
+      onConfirm: () => deleteEvaluationTemplate(templateId),
+      color: "danger",
+    });
+  }
+
+  return (
+    <TablePage data-testid="evaluation-templates-overview-table" fullHeight>
+      <TableSheet
+        footer={
+          <Pagination
+            {...getPaginationProps({
+              totalCount: evaluationTemplatesOverview.totalNumberOfElements,
+            })}
+          />
+        }
+      >
+        <DataTable
+          data={evaluationTemplatesOverview.evaluationTemplates}
+          columns={evaluationTemplatesColumns(
+            writePermission,
+            userPermissions.canWrite,
+            userPermissions.canUpdateEvaluationTemplate,
+            userPermissions.canDeleteEvaluationTemplate,
+            deleteTemplateWithConfirmation,
+          )}
+          wrapHeader
+          wrapContent
+          noDataComponent={() => (
+            <Box flex={1} alignContent="center">
+              <NoSearchResults info="Keine Vorlagen vorhanden" />
+              {/* TODO: comment in and open sidebar on click 
+              <Button
+                size="md"
+                startDecorator={<Add />}
+                onClick={() => doNothing()}
+              >
+                Vorlage erstellen
+              </Button> */}
+            </Box>
+          )}
+          sorting={manualSortingProps}
+        ></DataTable>
+      </TableSheet>
+    </TablePage>
+  );
+}
+
+const columnHelper = createColumnHelper<EvaluationTemplateWithUserInfo>();
+
+function evaluationTemplatesColumns(
+  writePermission: boolean,
+  canWrite: (creatorUserId: string) => boolean,
+  canEdit: (creatorUserId: string) => boolean,
+  canDelete: (creatorUserId: string) => boolean,
+  onDelete: (id: string) => void,
+) {
+  const staticColumns = [
+    columnHelper.accessor("name", {
+      header: "Name",
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("businessModuleName", {
+      header: "Datenquelle",
+      enableSorting: false,
+      cell: (props) =>
+        businessModuleNames[mapToApiBusinessModule(props.getValue())],
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("analysisCount", {
+      header: "Analysen",
+      meta: { canNavigate: { parentRow: true }, width: "8rem" },
+    }),
+    columnHelper.accessor("createdAt", {
+      header: "Erstellt am",
+      cell: (props) => formatDate(props.getValue(), "DE"),
+      meta: { canNavigate: { parentRow: true } },
+    }),
+    columnHelper.accessor("user", {
+      header: "Erstellt von",
+      enableSorting: false,
+      cell: (props) => <UserLink user={props.getValue()} />,
+      meta: { canNavigate: { parentRow: true } },
+    }),
+  ];
+
+  if (!writePermission) {
+    return staticColumns;
+  }
+
+  return [
+    ...staticColumns,
+    columnHelper.display({
+      id: "actions",
+      header: "Aktionen",
+      enableSorting: false,
+      cell: (props) => (
+        <ActionsMenu
+          actionItems={[
+            canWrite(props.row.original.userId) && {
+              label: "Auswertung erstellen",
+              onClick: () => doNothing(),
+              startDecorator: <Add />,
+            },
+            canEdit(props.row.original.userId) && {
+              label: "Bearbeiten",
+              onClick: () => doNothing(),
+              startDecorator: <Edit />,
+            },
+            canDelete(props.row.original.userId) &&
+              ({
+                label: "Löschen",
+                onClick: () => onDelete(props.row.original.id),
+                color: "danger",
+                startDecorator: <Delete />,
+              } satisfies ActionsItem),
+          ].filter(isPlainObject)}
+        />
+      ),
+      meta: {
+        width: "6rem",
+        cellStyle: "button",
+      },
+    }),
+  ];
+}
diff --git a/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts b/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
index cb48d2c29..05bca4b84 100644
--- a/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
+++ b/employee-portal/src/lib/businessModules/statistics/components/statistics/useStatisticRoleChecks.ts
@@ -22,6 +22,8 @@ export function useStatisticRoleChecks() {
   return {
     canDelete: isAdminOrOwner,
     canUpdateStatistic: isAdminOrOwner,
+    canUpdateEvaluationTemplate: isAdminOrOwner,
+    canDeleteEvaluationTemplate: isAdminOrOwner,
     canWrite: () => canWrite,
   };
 }
diff --git a/employee-portal/src/lib/businessModules/statistics/shared/routes.ts b/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
index 8930ab35c..2caa74c7c 100644
--- a/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/statistics/shared/routes.ts
@@ -16,6 +16,9 @@ export const routes = {
       reports: `${statisticsPath}/${id}/reports`,
       dataQuality: `${statisticsPath}/${id}/data-quality`,
     }),
+    evaluationTemplates: {
+      index: `${statisticsPath}/evaluation-templates`,
+    },
   },
   reports: {
     index: reportsPath,
diff --git a/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
index 5f8608296..ce51be3a1 100644
--- a/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/statistics/shared/sideNavigationItem.tsx
@@ -8,7 +8,7 @@ import { ApiStatisticsFeature } from "@eshg/employee-portal-api/statistics";
 import { Leaderboard } from "@mui/icons-material";
 import { isPlainObject } from "remeda";
 
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { UseSideNavigationItemsResult } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/statistics/api/queries/useStatisticsFeatureToggle";
 import {
   hasAnyUserRoles,
@@ -17,42 +17,48 @@ import {
 
 import { routes } from "./routes";
 
-export function useSideNavigationItems(): SideNavigationItem[] {
-  const { data: statisticsReportsEnabled, isError } =
-    useIsNewFeatureEnabledUnsuspended(ApiStatisticsFeature.Reports);
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
+  const {
+    data: statisticsReportsEnabled,
+    isError,
+    isLoading,
+  } = useIsNewFeatureEnabledUnsuspended(ApiStatisticsFeature.Reports);
 
-  return [
-    {
-      name: "Statistik",
-      decorator: <Leaderboard />,
-      error: isError
-        ? "Bei der Verbindung zum Statistikmodul ist ein Fehler aufgetreten."
-        : undefined,
-      subItems: [
-        {
-          name: "Auswertungen",
-          href: routes.statistics.index,
-          accessCheck: hasAnyUserRoles([
-            ApiUserRole.StatisticsStatisticsRead,
-            ApiUserRole.StatisticsStatisticsWrite,
-            ApiUserRole.StatisticsStatisticsAdmin,
-          ]),
-        },
-        statisticsReportsEnabled && {
-          name: "Reports",
-          href: routes.reports.index,
-          accessCheck: hasAnyUserRoles([
-            ApiUserRole.StatisticsStatisticsRead,
-            ApiUserRole.StatisticsStatisticsWrite,
-            ApiUserRole.StatisticsStatisticsAdmin,
-          ]),
-        },
-        {
-          name: "Geo-Shapes",
-          href: routes.geoShapes.index,
-          accessCheck: hasUserRole(ApiUserRole.StatisticsStatisticsAdmin),
-        },
-      ].filter(isPlainObject),
-    },
-  ];
+  return {
+    isLoading,
+    items: [
+      {
+        name: "Statistik",
+        decorator: <Leaderboard />,
+        error: isError
+          ? "Bei der Verbindung zum Statistikmodul ist ein Fehler aufgetreten."
+          : undefined,
+        subItems: [
+          {
+            name: "Auswertungen",
+            href: routes.statistics.index,
+            accessCheck: hasAnyUserRoles([
+              ApiUserRole.StatisticsStatisticsRead,
+              ApiUserRole.StatisticsStatisticsWrite,
+              ApiUserRole.StatisticsStatisticsAdmin,
+            ]),
+          },
+          statisticsReportsEnabled && {
+            name: "Reports",
+            href: routes.reports.index,
+            accessCheck: hasAnyUserRoles([
+              ApiUserRole.StatisticsStatisticsRead,
+              ApiUserRole.StatisticsStatisticsWrite,
+              ApiUserRole.StatisticsStatisticsAdmin,
+            ]),
+          },
+          {
+            name: "Geo-Shapes",
+            href: routes.geoShapes.index,
+            accessCheck: hasUserRole(ApiUserRole.StatisticsStatisticsAdmin),
+          },
+        ].filter(isPlainObject),
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts
new file mode 100644
index 000000000..1f0972bcd
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistory.ts
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
+
+import { useMedicalHistoryApi } from "@/lib/businessModules/stiProtection/api/clients";
+
+import { stiProtectionApiQueryKey } from "./apiQueryKeys";
+
+export function useGetMedicalHistoryQueryOptions(procedureId: string) {
+  const medicalHistoryApi = useMedicalHistoryApi();
+
+  return queryOptions({
+    queryFn: ({ signal }) =>
+      medicalHistoryApi
+        .getMedicalHistory(procedureId, {
+          signal,
+        })
+        .then((response) => {
+          return response;
+        })
+        .catch((_error: Error) => {
+          return null;
+        }),
+    queryKey: stiProtectionApiQueryKey(["medicalHistory", procedureId]),
+  });
+}
+
+export function useMedicalHistoryQuery(procedureId: string) {
+  return useSuspenseQuery(useGetMedicalHistoryQueryOptions(procedureId));
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
index 14a5acdf7..a7a4160f7 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx
@@ -135,13 +135,14 @@ export function CreateAppointmentBlockGroupForm() {
 
   async function handleSubmit(values: AppointmentBlockGroupValues) {
     const appointmentBlockGroupValues = mapFormValues(values);
-    await createDailyAppointmentBlocksForGroup
-      .mutateAsync(appointmentBlockGroupValues, {
+    await createDailyAppointmentBlocksForGroup.mutateAsync(
+      appointmentBlockGroupValues,
+      {
         onSuccess: () => {
           router.push(routes.appointmentBlockGroups.index);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
index 0f3f62dcb..eb158106f 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/components/procedures/proceduresTable/StiProtectionProceduresTable.tsx
@@ -207,10 +207,11 @@ export function StiProtectionProceduresTable() {
           sorting={tableControl.tableSorting}
           enableSortingRemoval={false}
           columns={getProceduresColumns({ reopenDialog })}
-          rowNavRoute={({ original: { id: procedureId } }) =>
-            routes.procedures.byId(procedureId).details
-          }
-          focusColumnHeader="id"
+          rowNavigation={{
+            route: ({ original: { id: procedureId } }) =>
+              routes.procedures.byId(procedureId).details,
+            focusColumnAccessorKey: "createdAt",
+          }}
         />
         <ReopenConfirmationDialog
           open={reopenDialog.isRequestingFinalize}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
index 7b407f9c6..dddb3736e 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx
@@ -100,7 +100,6 @@ export function AppointmentForm() {
               ApiAppointmentBookingType.AppointmentBlock,
             )
           }
-          // eslint-disable-next-line jsx-a11y/aria-props
           aria-description="Termin aus Terminblock wählen"
         >
           <Grid container spacing={3} direction="row">
@@ -139,7 +138,6 @@ export function AppointmentForm() {
               ApiAppointmentBookingType.UserDefined,
             )
           }
-          // eslint-disable-next-line jsx-a11y/aria-props
           aria-description="Frei wählbarer Zeitraum für den Termin"
         >
           <Row>
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
index 15ed30d96..7688e75e1 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AdditionalDataSection.tsx
@@ -4,15 +4,14 @@
  */
 
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { Sheet } from "@mui/joy";
 
 import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants";
 import { createOnlyIfProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 const dateFormater = new Intl.DateTimeFormat("de-DE", { dateStyle: "medium" });
 const timeFormater = new Intl.DateTimeFormat("de-DE", { timeStyle: "short" });
@@ -28,19 +27,19 @@ export function AdditionalDataSection({
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
   const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <DetailsCard
-      title="Zusatzinfos"
-      actionButton={onlyIfOpen(
-        <EditButton aria-label="Zusatzinfos bearbeiten" />,
-      )}
-    >
-      <ValueList>
-        <LabeledValue label="Art" value={CONCERN_VALUES[procedure.concern]} />
-        <LabeledValue
-          label="Nächster Termin"
-          value={formatAppointmentTime(procedure?.appointment?.start)}
-        />
-      </ValueList>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection
+        title="Zusatzinfos"
+        buttons={onlyIfOpen(<EditButton aria-label="Zusatzinfos bearbeiten" />)}
+      >
+        <DetailsColumn>
+          <DetailsCell label="Art" value={CONCERN_VALUES[procedure.concern]} />
+          <DetailsCell
+            label="Nächster Termin"
+            value={formatAppointmentTime(procedure?.appointment?.start)}
+          />
+        </DetailsColumn>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx
new file mode 100644
index 000000000..b9c710086
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { DownloadLink } from "@eshg/lib-portal/api/files/DownloadLink";
+import { Sheet, Stack } from "@mui/joy";
+import { useRef } from "react";
+
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
+
+export function AnonIdentityDocumentCard() {
+  const downloadContainerRef = useRef<HTMLDivElement>(null);
+
+  return (
+    <Sheet>
+      <DetailsSection title="Dokument zur anonymen Identifizierung">
+        <DetailsColumn>
+          <DetailsCell label="Anmeldecode" value="ABCDEFG1234567890" />
+          <DetailsCell
+            label="Identifizierungs-Dokument als PDF"
+            valueIsDiv
+            value={
+              <Stack direction="row" gap={1}>
+                <DownloadLink
+                  downloadContainerRef={downloadContainerRef}
+                  onDownload={() => Promise.resolve()}
+                >
+                  PDF auf Deutsch
+                </DownloadLink>
+                <DownloadLink
+                  downloadContainerRef={downloadContainerRef}
+                  onDownload={() => Promise.resolve()}
+                >
+                  PDF auf Englisch
+                </DownloadLink>
+              </Stack>
+            }
+          />
+        </DetailsColumn>
+      </DetailsSection>
+    </Sheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
index 5c95852de..d5a866221 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx
@@ -4,51 +4,60 @@
  */
 
 import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { Sheet, Stack } from "@mui/joy";
 
 import { GENDER_VALUES } from "@/lib/businessModules/stiProtection/shared/constants";
 import { COUNTRY_CODE_LABELS } from "@/lib/businessModules/stiProtection/shared/countryCodes";
 import { createOnlyIfProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers";
+import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider";
 import { EditButton } from "@/lib/shared/components/buttons/EditButton";
-import { DetailsCard } from "@/lib/shared/components/detailsCard/DetailsCard";
-import {
-  LabeledValue,
-  ValueList,
-} from "@/lib/shared/components/detailsCard/LabeledValue";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn";
+import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection";
 
 export function PersonDetails({
   procedure,
 }: Readonly<{ procedure: ApiStiProtectionProcedure }>) {
   const onlyIfOpen = createOnlyIfProcedureOpen(procedure);
   return (
-    <DetailsCard
-      title="Person"
-      actionButton={onlyIfOpen(<EditButton aria-label="Person bearbeiten" />)}
-    >
-      <ValueList>
-        <LabeledValue label="Aktenzeichen" value="-" />
-        <LabeledValue
-          label="Geburtsjahr"
-          value={procedure.person.yearOfBirth.toString()}
-        />
-        <LabeledValue
-          label="Geschlecht"
-          value={GENDER_VALUES[procedure.person.gender]}
-        />
-      </ValueList>
-      <ValueList>
-        <LabeledValue
-          label="Geburtsland"
-          value={
-            procedure.person.countryOfBirth
-              ? COUNTRY_CODE_LABELS[procedure.person.countryOfBirth]
-              : undefined
-          }
-        />
-        <LabeledValue
-          label="In Deutschland seit"
-          value={procedure.person.inGermanySince?.toString()}
-        />
-      </ValueList>
-    </DetailsCard>
+    <Sheet>
+      <DetailsSection
+        title="Person"
+        buttons={onlyIfOpen(<EditButton aria-label="Person bearbeiten" />)}
+      >
+        <Stack
+          direction={{ md: "row" }}
+          gap={3}
+          divider={<ResponsiveDivider breakpoint="md" />}
+          width="100%"
+        >
+          <DetailsColumn>
+            <DetailsCell label="Aktenzeichen" value="-" />
+            <DetailsCell
+              label="Geburtsjahr"
+              value={procedure.person.yearOfBirth.toString()}
+            />
+            <DetailsCell
+              label="Geschlecht"
+              value={GENDER_VALUES[procedure.person.gender]}
+            />
+          </DetailsColumn>
+          <DetailsColumn>
+            <DetailsCell
+              label="Geburtsland"
+              value={
+                procedure.person.countryOfBirth
+                  ? COUNTRY_CODE_LABELS[procedure.person.countryOfBirth]
+                  : undefined
+              }
+            />
+            <DetailsCell
+              label="In Deutschland seit"
+              value={procedure.person.inGermanySince?.toString()}
+            />
+          </DetailsColumn>
+        </Stack>
+      </DetailsSection>
+    </Sheet>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
index 1657b7e80..01e6daed2 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/ProcedureDetails.tsx
@@ -10,23 +10,27 @@ import { Grid, Stack } from "@mui/joy";
 import { useStiProcedureQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
 
 import { AdditionalDataSection } from "./AdditionalDataSection";
+import { AnonIdentityDocumentCard } from "./AnonIdentityDocumentCard";
 import { CloseAndReopenProcedurePanel } from "./CloseProcedurePanel";
 import { PersonDetails } from "./PersonDetails";
 
-const SPACING = { sm: 2, md: 3, xxl: 4 };
-
 export function ProcedureDetails({
   procedureId,
 }: Readonly<{ procedureId: string }>) {
   const procedure = useStiProcedureQuery(procedureId).data;
 
   return (
-    <Grid container spacing={SPACING}>
-      <Grid xs={8}>
-        <PersonDetails procedure={procedure} />
+    <Grid container spacing={2}>
+      <Grid container spacing={2} xs={12} lg={8}>
+        <Grid xs={12}>
+          <PersonDetails procedure={procedure} />
+        </Grid>
+        <Grid xs={12}>
+          <AnonIdentityDocumentCard />
+        </Grid>
       </Grid>
-      <Grid xs={4}>
-        <Stack spacing={SPACING}>
+      <Grid xs={12} lg={4}>
+        <Stack spacing={2}>
           <AdditionalDataSection procedure={procedure} />
           <CloseAndReopenProcedurePanel procedure={procedure} />
         </Stack>
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx
new file mode 100644
index 000000000..068e9b04c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/BooleanSelectDate.tsx
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
+import {
+  MonthAndYear,
+  MonthAndYearFields,
+} from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
+import { Grid, GridProps } from "@mui/joy";
+import { SxProps } from "@mui/joy/styles/types";
+
+import {
+  DiseaseType,
+  diseaseTypeNames,
+} from "@/lib/businessModules/stiProtection/shared/constants";
+
+import { AutoWidthHorizontalField } from "./MedicalHistoryForm";
+
+export const booleanSelectGroupGridProps: GridProps = {
+  container: true,
+  direction: "row",
+  xxs: 12,
+  lg: 6,
+  rowSpacing: 2,
+  rowGap: 1,
+};
+
+export function BooleanSelectDate({
+  date,
+  diseaseType,
+  fieldNameDate,
+  fieldNameSelect,
+  showDateField = false,
+}: {
+  date: MonthAndYear;
+  diseaseType: DiseaseType;
+  fieldNameDate: string;
+  fieldNameSelect: string;
+  showDateField?: boolean;
+}) {
+  return (
+    <Grid
+      {...booleanSelectGroupGridProps}
+      mb={1}
+      component="section"
+      aria-label={diseaseTypeNames[diseaseType]}
+    >
+      <Grid xxs={12} md={6}>
+        <BooleanSelectField
+          name={fieldNameSelect}
+          label={diseaseTypeNames[diseaseType]}
+          component={AutoWidthHorizontalField}
+          sx={{ mr: 1 }}
+        />
+      </Grid>
+      <Grid
+        xxs={12}
+        md={6}
+        sx={{
+          ml: {
+            xxs: 3,
+            md: "inherit",
+          },
+          ...fadeInOut(showDateField),
+        }}
+      >
+        <MonthAndYearFields fieldName={fieldNameDate} date={date} />
+      </Grid>
+    </Grid>
+  );
+}
+
+export function fadeInOut(shouldFadeIn: boolean): SxProps {
+  return {
+    visibility: shouldFadeIn ? "visible" : "hidden",
+    opacity: shouldFadeIn ? 1 : 0,
+    height: shouldFadeIn ? "100%" : 0,
+    transition: "all ease-in-out 0.4s",
+    "@media (prefers-reduced-motion)": {
+      transition: "none",
+    },
+  };
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
index ecbfc0b75..52713ddfb 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config.ts
@@ -4,10 +4,15 @@
  */
 
 import {
+  ApiConcern,
+  ApiCreateMedicalHistoryRequest,
   ApiExamination,
   ApiGender,
+  ApiPreviousIllness,
+  ApiRelationshipModel,
   ApiRiskFactors,
   ApiSexualOrientation,
+  ApiStiProtectionProcedure,
   ApiVaccination,
   CreateMedicalHistoryRequest,
 } from "@eshg/employee-portal-api/stiProtection";
@@ -17,17 +22,23 @@ import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
 type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
 
-type ExaminationData = Merge<
+export type ExaminationData = Merge<
   ApiExamination,
   {
-    [K in keyof ApiExamination]: MonthAndYear;
+    [K in keyof ApiExamination]: {
+      hadExamination: boolean;
+      examinationDate: MonthAndYear;
+    };
   }
 >;
 
 export type VaccinationData = Merge<
   ApiVaccination,
   {
-    [K in keyof ApiVaccination]: MonthAndYear;
+    [K in keyof ApiVaccination]: {
+      hadVaccination: boolean;
+      vaccinationDate: MonthAndYear;
+    };
   }
 >;
 
@@ -38,58 +49,137 @@ type RiskFactors = Merge<
   }
 >;
 
+export const defaultExaminations: ExaminationData = {
+  chlamydia: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+  gonorrhea: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+  hepA: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hepB: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hepC: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  hiv: { hadExamination: false, examinationDate: { month: null, year: "" } },
+  syphilis: {
+    hadExamination: false,
+    examinationDate: { month: null, year: "" },
+  },
+};
+
+export const defaultPreviousIllnesses: ApiPreviousIllness = {
+  chlamydia: false,
+  gonorrhea: false,
+  hepA: false,
+  hepB: false,
+  hepC: false,
+  hiv: false,
+  syphilis: false,
+};
+
+export const defaultVaccinations: VaccinationData = {
+  hepA: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+  hepB: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+  hpv: { hadVaccination: false, vaccinationDate: { month: null, year: "" } },
+};
+
 export interface MedicalHistoryFormData
   extends Omit<
     CreateMedicalHistoryRequest["apiCreateMedicalHistoryRequest"]["medicalHistory"],
-    "examinations" | "riskFactors"
+    | "contactToClarifyDuration"
+    | "examinations"
+    | "riskFactors"
+    | "relationshipModel"
   > {
+  contactToClarifyDuration: OptionalFieldValue<string>;
+  currentSymptoms: string;
   examinations: ExaminationData;
-  lastMenstruation: OptionalFieldValue<string>;
   lastCancerScreening: OptionalFieldValue<string>;
+  lastMenstruation: OptionalFieldValue<string>;
   hasBeenPregnant: boolean | null;
-  numberOfSexualPartners: number;
-  numberOfPregnancies: number;
+  knownOperationsOrIllnesses: string;
+  medications: string;
   numberOfBirthsOrAbortions: number;
+  numberOfPregnancies: number;
+  numberOfSexualPartnersLast12Months: number;
+  relationshipModel: OptionalFieldValue<ApiRelationshipModel>;
+  remarks: string;
+  riskFactors: RiskFactors;
   sexualContact: ApiGender;
   sexualOrientation: ApiSexualOrientation;
-  riskFactors: RiskFactors;
 }
 
-export const initialValues: MedicalHistoryFormData = {
-  type: "SexWorkMedicalHistory",
-  examinationReason: "",
-  sexualContact: "NOT_SPECIFIED",
-  sexualOrientation: "NOT_SPECIFIED",
-  examinations: {
-    chlamydia: { month: null, year: "" },
-    gonorrhea: { month: null, year: "" },
-    hepA: { month: null, year: "" },
-    hepB: { month: null, year: "" },
-    hepC: { month: null, year: "" },
-    hiv: { month: null, year: "" },
-    syphilis: { month: null, year: "" },
-  },
-  hasBeenPregnant: null,
-  lastMenstruation: "",
-  lastCancerScreening: "",
-  numberOfSexualPartners: 0,
-  numberOfPregnancies: 0,
-  numberOfBirthsOrAbortions: 0,
-  previousIllnesses: {
-    chlamydia: false,
-    gonorrhea: false,
-    hepA: false,
-    hepB: false,
-    hepC: false,
-    hiv: false,
-    syphilis: false,
-  },
-  riskFactors: {
-    prepInfoProvided: false,
-    vaccinations: {
-      hepA: { month: null, year: "" },
-      hepB: { month: null, year: "" },
-      hpv: { month: null, year: "" },
+type MedicalHistoryType =
+  ApiCreateMedicalHistoryRequest["medicalHistory"]["type"];
+
+export const medicalHistoryTypeByConcern: Record<
+  ApiConcern,
+  MedicalHistoryType
+> = {
+  [ApiConcern.SexWork]: "SexWorkMedicalHistory",
+  [ApiConcern.HivStiConsultation]: "StiConsultationMedicalHistory",
+} satisfies Record<ApiConcern, MedicalHistoryType>;
+
+export function defaultMedicalHistoryFormValues({
+  concern,
+}: ApiStiProtectionProcedure): MedicalHistoryFormData {
+  return {
+    type: medicalHistoryTypeByConcern[concern],
+    contactToClarifyDuration: "",
+    currentSymptoms: "",
+    examinationReason: "",
+    examinations: defaultExaminations,
+    hasBeenPregnant: null,
+    knownOperationsOrIllnesses: "",
+    lastCancerScreening: "",
+    lastMenstruation: "",
+    medications: "",
+    numberOfBirthsOrAbortions: 0,
+    numberOfPregnancies: 0,
+    numberOfSexualPartnersLast12Months: 0,
+    previousIllnesses: defaultPreviousIllnesses,
+    relationshipModel: "",
+    remarks: "",
+    riskFactors: {
+      prepInfoProvided: false,
+      vaccinations: defaultVaccinations,
     },
-  },
+    sexualContact: "NOT_SPECIFIED",
+    sexualOrientation: "NOT_SPECIFIED",
+  };
+}
+
+export const medicalHistoryFormFields = {
+  additionalComments: "",
+  contactToClarifyDuration: "Abzuklärender Kontakt vor",
+  currentSymptoms: "Aktuelle Beschwerden",
+  examinationReason: "Grund für die heutige Beratung",
+  hasBeenPregnant: "Bereits schwanger?",
+  knownOperationsOrIllnesses: "Bekannte Operationen oder Erkrankungen",
+  lastCancerScreening: "Letzte Krebsvorsorge vor",
+  lastMenstruation: "Letzte Menstruation vor",
+  medications: "Medikamente",
+  numberOfBirthsOrAbortions: "Anzahl Geburten/Aborte",
+  numberOfPregnancies: "Wenn ja, wie oft?",
+  numberOfSexualPartnersLast12Months:
+    "Anzahl der Sexpartner:innen in den letzten 12 Monaten",
+  previousIllnesses: "Bisherige Krankheiten",
+  relationshipModel: "Beziehungsmodell",
+  remarks: "Bemerkungen",
+  riskContacts: "",
+  sexualContact: "Sexueller Kontakt",
+  sexualOrientation: "Sexuelle Orientierung",
+} as const satisfies Record<
+  keyof Omit<MedicalHistoryFormData, "type" | "examinations" | "riskFactors">,
+  string
+>;
+
+export const medicalHistoryFormSections = {
+  common: "Allgemein",
+  examinations: "Untersuchungen",
+  previousIllnesses: "Bisherige Krankheiten",
+  riskAndPrevention: "Risiko und Prävention",
+  sexualOrientationAndContact: "Sexuelle Orientierung / Kontakte",
+  vaccinations: "Impfungen",
 };
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
index db162470d..55ea810f0 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.tsx
@@ -6,29 +6,19 @@
 "use client";
 
 import {
-  ApiStiProtectionProcedure,
+  ApiGetMedicalHistory200Response,
   CreateMedicalHistoryRequest,
 } from "@eshg/employee-portal-api/stiProtection";
 import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
 import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
-import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
 import { HorizontalField } from "@eshg/lib-portal/components/formFields/HorizontalField";
-import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
-import {
-  MonthAndYearFields,
-  mapMonthAndYear,
-} from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
-import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
+import { mapMonthAndYear } from "@eshg/lib-portal/components/formFields/MonthAndYearFields";
 import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton";
 import { toDateString, toUtcDate } from "@eshg/lib-portal/helpers/dateTime";
-import { Divider, FormLabel, Grid, Typography, styled } from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
-import { subDays } from "date-fns";
-import { FieldArray, Formik, useFormikContext } from "formik";
+import { Divider, Grid, Typography, styled } from "@mui/joy";
+import { FieldArray, Formik } from "formik";
 import { useRouter } from "next/navigation";
 
-import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
 import { useCreateMedicalHistory } from "@/lib/businessModules/stiProtection/api/mutations/medicalHistory";
 import { useStiProcedureQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures";
 import {
@@ -38,123 +28,36 @@ import {
 } from "@/lib/businessModules/stiProtection/shared/constants";
 import { routes } from "@/lib/businessModules/stiProtection/shared/routes";
 import { StickyBottomButtonBar } from "@/lib/shared/components/buttons/StickyBottomButtonBar";
-import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
 import { FormSheet } from "@/lib/shared/components/form/FormSheet";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
 
+import {
+  BooleanSelectDate,
+  booleanSelectGroupGridProps,
+} from "./BooleanSelectDate";
 import {
   MedicalHistoryFormData,
-  initialValues,
+  defaultMedicalHistoryFormValues,
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
 } from "./MedicalHistoryForm.config";
-import { sexualContactOptions, sexualOrientationOptions } from "./options";
+import { firstDayOfCurrentMonth, mapToFormValues } from "./helpers";
+import { MedicalHistoryCommonFields } from "./sections/MedicalHistoryCommonFields";
+import { SexualOrientationAndContact } from "./sections/SexualOrientationAndContact";
 
-const AutoWidthHorizontalField = styled(HorizontalField)({
+export const AutoWidthHorizontalField = styled(HorizontalField)({
   ".MuiStack-root": {
     justifyContent: "space-between",
   },
 });
 
-function MedicalHistoryCommonFields({
-  procedure,
-}: {
-  procedure: ApiStiProtectionProcedure;
-}) {
-  const { values } = useFormikContext<MedicalHistoryFormData>();
-
-  return (
-    <>
-      <Typography level="title-md" mt={1}>
-        Allgemein
-      </Typography>
-      <FormGroupGrid>
-        <Grid container xxs={12}>
-          <Grid xxs={12} md={6} xxl={3}>
-            <InputField
-              name="examinationReason"
-              label={
-                <FormLabel title="Grund für die heutige Beratung">
-                  Grund für die heutige Beratung
-                </FormLabel>
-              }
-              component={AutoWidthHorizontalField}
-            />
-          </Grid>
-        </Grid>
-        {procedure.concern === "SEX_WORK" && (
-          <Grid container xxs={12}>
-            <Grid xxs={12} md={6} xxl={3} direction="column" container>
-              <Grid>
-                <DateField
-                  name="lastMenstruation"
-                  label="Letzte Menstruation vor"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-              <Grid>
-                <DateField
-                  name="lastCancerScreening"
-                  label="Letzte Krebsvorsorge vor"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-            </Grid>
-            <Grid xxs={12} md={6} xxl={3} direction="column" container>
-              <Grid>
-                <BooleanSelectField
-                  name="hasBeenPregnant"
-                  label="Bereits schwanger?"
-                  component={AutoWidthHorizontalField}
-                />
-              </Grid>
-              {!!values.hasBeenPregnant && (
-                <>
-                  <Grid ml={3}>
-                    <NumberField
-                      name="numberOfPregnancies"
-                      label="Wenn ja, wie oft?"
-                      component={AutoWidthHorizontalField}
-                      required={"Bitte eine Zahl angeben"}
-                    />
-                  </Grid>
-                  <Grid ml={3}>
-                    <NumberField
-                      name="numberOfBirthsOrAbortions"
-                      label="Anzahl Geburten/Aborte"
-                      component={AutoWidthHorizontalField}
-                    />
-                  </Grid>
-                </>
-              )}
-            </Grid>
-
-            <Grid xxs={12} md={6} xxl={3}>
-              <TextareaField
-                name="knownOperationsOrIllnesses"
-                label="Bekannte Operationen oder Erkrankungen"
-                sx={{
-                  fontWeight: 500,
-                }}
-              />
-            </Grid>
-            <Grid xxs={12} md={6} xxl={3}>
-              <TextareaField
-                name="medications"
-                label="Medikamente"
-                sx={{
-                  fontWeight: 500,
-                }}
-              />
-            </Grid>
-          </Grid>
-        )}
-      </FormGroupGrid>
-    </>
-  );
-}
-
 export function MedicalHistoryForm({
   procedureId,
-}: Readonly<{ procedureId: string }>) {
+  medicalHistory,
+}: Readonly<{
+  procedureId: string;
+  medicalHistory?: ApiGetMedicalHistory200Response | null;
+}>) {
   const { data: stiProcedure } = useStiProcedureQuery(procedureId);
 
   const createMedicalHistory = useCreateMedicalHistory();
@@ -164,26 +67,28 @@ export function MedicalHistoryForm({
 
   async function onSubmit(values: MedicalHistoryFormData) {
     const examinationsToReport = Object.entries(values.examinations).filter(
-      ([_diseaseType, vaccinationDate]) => !!vaccinationDate,
+      ([_diseaseType, { hadExamination }]) => !!hadExamination,
     );
-
     const vaccinationsToReport = Object.entries(
       values.riskFactors.vaccinations,
-    ).filter(([_diseaseType, vaccinationDate]) => !!vaccinationDate);
+    ).filter(([_diseaseType, { hadVaccination }]) => !!hadVaccination);
 
     const medicalHistoryRequest: CreateMedicalHistoryRequest["apiCreateMedicalHistoryRequest"] =
       {
         medicalHistory: {
-          type: "StiConsultationMedicalHistory",
+          type:
+            stiProcedure.concern === "SEX_WORK"
+              ? "SexWorkMedicalHistory"
+              : "StiConsultationMedicalHistory",
           ...(values.examinationReason && {
             examinationReason: values.examinationReason,
           }),
           ...(examinationsToReport && {
             examinations: Object.fromEntries(
-              examinationsToReport.map(([diseaseType, vaccinationDate]) => [
+              examinationsToReport.map(([diseaseType, { examinationDate }]) => [
                 diseaseType as DiseaseType,
-                mapMonthAndYear(vaccinationDate) ??
-                  toUtcDate(toDateString(subDays(new Date(), 7))),
+                mapMonthAndYear(examinationDate) ??
+                  toUtcDate(toDateString(firstDayOfCurrentMonth())),
               ]),
             ),
           }),
@@ -198,34 +103,41 @@ export function MedicalHistoryForm({
             prepInfoProvided: values.riskFactors.prepInfoProvided,
             ...(vaccinationsToReport && {
               vaccinations: Object.fromEntries(
-                vaccinationsToReport.map(([diseaseType, vaccinationDate]) => [
-                  diseaseType as DiseaseType,
-                  mapMonthAndYear(vaccinationDate) ??
-                    toUtcDate(toDateString(subDays(new Date(), 7))),
-                ]),
+                vaccinationsToReport.map(
+                  ([diseaseType, { vaccinationDate }]) => [
+                    diseaseType as DiseaseType,
+                    mapMonthAndYear(vaccinationDate) ??
+                      toUtcDate(toDateString(firstDayOfCurrentMonth())),
+                  ],
+                ),
               ),
             }),
           },
         },
       };
 
-    await createMedicalHistory
-      .mutateAsync(
-        {
-          id: procedureId,
-          medicalHistory: medicalHistoryRequest,
-        },
-        {
-          onSuccess: () => {
-            router.push(routes.procedures.byId(procedureId).details);
-          },
+    await createMedicalHistory.mutateAsync(
+      {
+        id: procedureId,
+        medicalHistory: medicalHistoryRequest,
+      },
+      {
+        onSuccess: () => {
+          router.push(routes.procedures.byId(procedureId).details);
         },
-      )
-      .catch();
+      },
+    );
   }
 
   return (
-    <Formik initialValues={initialValues} onSubmit={onSubmit}>
+    <Formik
+      initialValues={
+        medicalHistory
+          ? mapToFormValues(medicalHistory)
+          : defaultMedicalHistoryFormValues(stiProcedure)
+      }
+      onSubmit={onSubmit}
+    >
       {({ values, isSubmitting }) => (
         <>
           <FormSheet sx={{ overflow: "auto" }}>
@@ -235,55 +147,33 @@ export function MedicalHistoryForm({
             <Divider />
             <MedicalHistoryCommonFields procedure={stiProcedure} />
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Untersuchungen
+            <SexualOrientationAndContact />
+            <Divider />
+            <Typography level="title-md" mt={1} id="examinations-section-title">
+              {sections.examinations}
             </Typography>
-            <Grid xxs={12} md={6}>
+            <Grid
+              xxs={12}
+              md={6}
+              component="section"
+              aria-labelledby="examinations-section-title"
+            >
               <FieldArray name={"examinations"}>
                 {() => (
                   <>
                     {Object.entries(values.examinations).map(
-                      ([diseaseType, examinationDate]) => {
-                        const showDateField = !!examinationDate;
+                      ([diseaseType, { examinationDate, hadExamination }]) => {
+                        const showDateField = !!hadExamination;
 
                         return (
-                          <Grid
+                          <BooleanSelectDate
                             key={diseaseType}
-                            container
-                            direction="row"
-                            xxs={12}
-                            lg={6}
-                            mb={1}
-                            rowSpacing={2}
-                            rowGap={1}
-                          >
-                            <Grid xxs={12} md={4}>
-                              <BooleanSelectField
-                                name={`examinations.${diseaseType}.hadExamination`}
-                                label={
-                                  diseaseTypeNames[diseaseType as DiseaseType]
-                                }
-                                component={AutoWidthHorizontalField}
-                                sx={{ mr: 1 }}
-                              />
-                            </Grid>
-                            <Grid
-                              xxs={12}
-                              md={8}
-                              sx={{
-                                ml: {
-                                  xxs: 3,
-                                  md: "inherit",
-                                },
-                                ...fadeInOut(showDateField),
-                              }}
-                            >
-                              <MonthAndYearFields
-                                fieldName={`examinations.${diseaseType}.examinationDate`}
-                                date={examinationDate}
-                              />
-                            </Grid>
-                          </Grid>
+                            date={examinationDate}
+                            diseaseType={diseaseType as DiseaseType}
+                            fieldNameDate={`examinations.${diseaseType}.examinationDate`}
+                            fieldNameSelect={`examinations.${diseaseType}.hadExamination`}
+                            showDateField={showDateField}
+                          />
                         );
                       },
                     )}
@@ -293,96 +183,77 @@ export function MedicalHistoryForm({
             </Grid>
             <Divider />
             <Typography level="title-md" mt={1}>
-              Bisherige Krankheiten
+              {sections.riskAndPrevention}
             </Typography>
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Sexuelle Orientierung / Kontakte
+            <Typography
+              level="title-md"
+              mt={1}
+              id="previous-illnesses-section-title"
+            >
+              {sections.previousIllnesses}
             </Typography>
-            <FormGroupGrid>
-              <Grid xxs={12} md={4}>
-                <SelectField
-                  name="sexualOrientation"
-                  label="Sexuelle Orientierung"
-                  options={sexualOrientationOptions}
-                />
-              </Grid>
-              <Grid xxs={12} md={4}>
-                <NumberField
-                  name="numberOfSexualPartners"
-                  label={
-                    <FormLabel
-                      sx={multiLineEllipsis(1)}
-                      title="Anzahl der Sexpartner:innen in den letzten 12 Monaten"
-                    >
-                      Anzahl der Sexpartner:innen in den letzten 12 Monaten
-                    </FormLabel>
-                  }
-                  required="Bitte eine Zahl eingeben"
-                />
-              </Grid>
-              <Grid xxs={12} md={4}>
-                <SelectField
-                  name="sexualContact"
-                  label="Sexueller Kontakt"
-                  options={sexualContactOptions}
+            <Grid
+              component="section"
+              xxs={12}
+              md={6}
+              aria-labelledby="previous-illnesses-section-title"
+            >
+              <FieldArray name={"previousIllnesses"}>
+                {() => (
+                  <Grid {...booleanSelectGroupGridProps}>
+                    {Object.entries(values.previousIllnesses).map(
+                      ([diseaseType, _hadPreviousIllness]) => (
+                        <Grid key={diseaseType} mb={1}>
+                          <Grid xxs={12} md={6}>
+                            <BooleanSelectField
+                              name={`previousIllnesses.${diseaseType}`}
+                              label={
+                                diseaseTypeNames[diseaseType as DiseaseType]
+                              }
+                              component={AutoWidthHorizontalField}
+                              sx={{ mr: 1 }}
+                            />
+                          </Grid>
+                        </Grid>
+                      ),
+                    )}
+                  </Grid>
+                )}
+              </FieldArray>
+              <Grid xxs={12} lg={3}>
+                <TextareaField
+                  name="contactToClarifyDuration"
+                  label={fields.contactToClarifyDuration}
                 />
               </Grid>
-            </FormGroupGrid>
-            <Divider />
-            <Typography level="title-md" mt={1}>
-              Risiko und Prävention
-            </Typography>
+            </Grid>
             <Divider />
-            <Typography level="title-md" mt={1}>
-              Impfungen
+            <Typography level="title-md" mt={1} id="vaccinations-section-title">
+              {sections.vaccinations}
             </Typography>
-            <Grid xxs={12} md={6}>
+            <Grid
+              component="section"
+              aria-labelledby="vaccinations-section-title"
+              xxs={12}
+              md={6}
+            >
               <FieldArray name={"vaccinations"}>
                 {() => (
                   <>
                     {Object.entries(values.riskFactors.vaccinations).map(
-                      ([diseaseType, vaccinationDate]) => {
-                        const showDateField = !!vaccinationDate;
+                      ([diseaseType, { vaccinationDate, hadVaccination }]) => {
+                        const showDateField = !!hadVaccination;
 
                         return (
-                          <Grid
+                          <BooleanSelectDate
                             key={diseaseType}
-                            container
-                            direction="row"
-                            xxs={12}
-                            lg={6}
-                            mb={1}
-                            rowSpacing={2}
-                            rowGap={1}
-                          >
-                            <Grid xxs={12} md={4}>
-                              <BooleanSelectField
-                                name={`vaccinations.${diseaseType}.hadVaccination`}
-                                label={
-                                  diseaseTypeNames[diseaseType as DiseaseType]
-                                }
-                                component={AutoWidthHorizontalField}
-                                sx={{ mr: 1 }}
-                              />
-                            </Grid>
-                            <Grid
-                              xxs={12}
-                              md={8}
-                              sx={{
-                                ml: {
-                                  xxs: 3,
-                                  md: "inherit",
-                                },
-                                ...fadeInOut(showDateField),
-                              }}
-                            >
-                              <MonthAndYearFields
-                                fieldName={`vaccinations.${diseaseType}.vaccinationDate`}
-                                date={vaccinationDate}
-                              />
-                            </Grid>
-                          </Grid>
+                            date={vaccinationDate}
+                            diseaseType={diseaseType as DiseaseType}
+                            fieldNameDate={`riskFactors.vaccinations.${diseaseType}.vaccinationDate`}
+                            fieldNameSelect={`riskFactors.vaccinations.${diseaseType}.hadVaccination`}
+                            showDateField={showDateField}
+                          />
                         );
                       },
                     )}
@@ -392,7 +263,7 @@ export function MedicalHistoryForm({
             </Grid>
             <Divider />
             <Grid xxs={12}>
-              <TextareaField name="notes" label="Bemerkungen" />
+              <TextareaField name="remarks" label={fields.remarks} />
             </Grid>
             <StickyBottomButtonBar
               right={
@@ -415,15 +286,3 @@ export function MedicalHistoryForm({
     </Formik>
   );
 }
-
-function fadeInOut(shouldFadeIn: boolean): SxProps {
-  return {
-    visibility: shouldFadeIn ? "visible" : "hidden",
-    opacity: shouldFadeIn ? 1 : 0,
-    height: shouldFadeIn ? "100%" : 0,
-    transition: "all ease-in-out 0.4s",
-    "@media (prefers-reduced-motion)": {
-      transition: "none",
-    },
-  };
-}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts
new file mode 100644
index 000000000..66bddb6e9
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts
@@ -0,0 +1,106 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiExamination,
+  ApiGetMedicalHistory200Response,
+  ApiVaccination,
+} from "@eshg/employee-portal-api/stiProtection";
+
+import {
+  ExaminationData,
+  MedicalHistoryFormData,
+  VaccinationData,
+  defaultExaminations,
+  defaultPreviousIllnesses,
+  defaultVaccinations,
+} from "./MedicalHistoryForm.config";
+
+function mapApiExaminationToForm(
+  examinations?: ApiExamination,
+): ExaminationData {
+  const examinationData: ExaminationData = { ...defaultExaminations };
+
+  if (examinations) {
+    for (const exam of Object.keys(examinationData)) {
+      const key = exam as keyof ApiExamination;
+      const examinationDate = examinations[key];
+
+      if (examinationDate) {
+        examinationData[key] = {
+          hadExamination: true,
+          examinationDate: {
+            month: examinationDate.getMonth(),
+            year: examinationDate.getFullYear(),
+          },
+        };
+      }
+    }
+  }
+
+  return examinationData;
+}
+
+function mapApiVaccinationToForm(
+  vaccinations?: ApiVaccination,
+): VaccinationData {
+  const vaccinationData: VaccinationData = { ...defaultVaccinations };
+
+  if (vaccinations) {
+    for (const vax of Object.keys(vaccinationData)) {
+      const key = vax as keyof ApiVaccination;
+      const vaccinationDate = vaccinations[key];
+
+      if (vaccinationDate) {
+        vaccinationData[key] = {
+          hadVaccination: true,
+          vaccinationDate: {
+            month: vaccinationDate.getMonth(),
+            year: vaccinationDate.getFullYear(),
+          },
+        };
+      }
+    }
+  }
+
+  return vaccinationData;
+}
+
+export function mapToFormValues(
+  apiMedicalHistory: ApiGetMedicalHistory200Response,
+): MedicalHistoryFormData {
+  return {
+    type: apiMedicalHistory.type,
+    contactToClarifyDuration: "",
+    currentSymptoms: "",
+    examinationReason: apiMedicalHistory.examinationReason,
+    examinations: mapApiExaminationToForm(apiMedicalHistory.examinations),
+    hasBeenPregnant: null,
+    knownOperationsOrIllnesses: "",
+    lastCancerScreening: "",
+    lastMenstruation: "",
+    medications: "",
+    numberOfBirthsOrAbortions: 0,
+    numberOfPregnancies: 0,
+    numberOfSexualPartnersLast12Months: 0,
+    previousIllnesses: defaultPreviousIllnesses,
+    relationshipModel: "",
+    remarks: "",
+    sexualContact: "NOT_SPECIFIED",
+    sexualOrientation: "NOT_SPECIFIED",
+    riskFactors: {
+      prepInfoProvided: apiMedicalHistory.riskFactors.prepInfoProvided,
+      vaccinations: mapApiVaccinationToForm(
+        apiMedicalHistory.riskFactors.vaccinations,
+      ),
+    },
+  };
+}
+
+export function firstDayOfCurrentMonth(): Date {
+  const { getMonth, getFullYear } = new Date();
+
+  return new Date(getFullYear(), getMonth(), 1);
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx
new file mode 100644
index 000000000..fb058cb8a
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/MedicalHistoryCommonFields.tsx
@@ -0,0 +1,142 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection";
+import { BooleanSelectField } from "@eshg/lib-portal/components/formFields/BooleanSelectField";
+import { DateField } from "@eshg/lib-portal/components/formFields/DateField";
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
+import { FormLabel, Grid, Typography } from "@mui/joy";
+import { useFormikContext } from "formik";
+
+import { AutoWidthHorizontalField } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
+import {
+  MedicalHistoryFormData,
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config";
+import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+
+export function MedicalHistoryCommonFields({
+  procedure,
+}: {
+  procedure: ApiStiProtectionProcedure;
+}) {
+  const { values } = useFormikContext<MedicalHistoryFormData>();
+
+  return (
+    <>
+      <Typography level="title-md" mt={1} id="general-section-title">
+        {sections.common}
+      </Typography>
+      <FormGroupGrid
+        component="section"
+        aria-labelledby="general-section-title"
+      >
+        <Grid container xxs={12}>
+          <Grid xxs={12} md={6} xxl={3}>
+            <InputField
+              name="examinationReason"
+              label={
+                <FormLabel title={fields.examinationReason}>
+                  {fields.examinationReason}
+                </FormLabel>
+              }
+              component={AutoWidthHorizontalField}
+            />
+          </Grid>
+          <Grid xxs={12} md={6} xxl={3}>
+            <InputField
+              name="currentSymptoms"
+              label={
+                <FormLabel title={fields.currentSymptoms}>
+                  {fields.currentSymptoms}
+                </FormLabel>
+              }
+              component={AutoWidthHorizontalField}
+            />
+          </Grid>
+        </Grid>
+        <Grid xxs={12} md={6} xxl={3}>
+          <DateField
+            name="contactToClarifyDuration"
+            label={fields.contactToClarifyDuration}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        {procedure.concern === "SEX_WORK" && (
+          <Grid container xxs={12}>
+            <Grid xxs={12} md={6} xxl={3} direction="column" container>
+              <Grid>
+                <DateField
+                  name="lastMenstruation"
+                  label={fields.lastMenstruation}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+              <Grid>
+                <DateField
+                  name="lastCancerScreening"
+                  label={fields.lastCancerScreening}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+            </Grid>
+            <Grid xxs={12} md={6} xxl={3} direction="column" container>
+              <Grid>
+                <BooleanSelectField
+                  name="hasBeenPregnant"
+                  label={fields.hasBeenPregnant}
+                  component={AutoWidthHorizontalField}
+                />
+              </Grid>
+              {!!values.hasBeenPregnant && (
+                <>
+                  <Grid ml={3}>
+                    <NumberField
+                      name="numberOfPregnancies"
+                      label={fields.numberOfPregnancies}
+                      component={AutoWidthHorizontalField}
+                      required={"Bitte eine Zahl angeben"}
+                      min={0}
+                    />
+                  </Grid>
+                  <Grid ml={3}>
+                    <NumberField
+                      name="numberOfBirthsOrAbortions"
+                      label={fields.numberOfBirthsOrAbortions}
+                      component={AutoWidthHorizontalField}
+                      min={0}
+                    />
+                  </Grid>
+                </>
+              )}
+            </Grid>
+
+            <Grid xxs={12} md={6} xxl={3}>
+              <TextareaField
+                name="knownOperationsOrIllnesses"
+                label={fields.knownOperationsOrIllnesses}
+                sx={{
+                  fontWeight: 500,
+                }}
+              />
+            </Grid>
+            <Grid xxs={12} md={6} xxl={3}>
+              <TextareaField
+                name="medications"
+                label="Medikamente"
+                sx={{
+                  fontWeight: 500,
+                }}
+              />
+            </Grid>
+          </Grid>
+        )}
+      </FormGroupGrid>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx
new file mode 100644
index 000000000..f2be7499c
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/sections/SexualOrientationAndContact.tsx
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
+import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
+import { FormLabel, Grid, Typography } from "@mui/joy";
+
+import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
+import { AutoWidthHorizontalField } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm";
+import {
+  medicalHistoryFormFields as fields,
+  medicalHistoryFormSections as sections,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/MedicalHistoryForm.config";
+import {
+  sexualContactOptions,
+  sexualOrientationOptions,
+} from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/options";
+import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid";
+
+export function SexualOrientationAndContact() {
+  return (
+    <>
+      <Typography
+        level="title-md"
+        mt={1}
+        id="sexual-orientation-and-contact-title"
+      >
+        {sections.sexualOrientationAndContact}
+      </Typography>
+      <FormGroupGrid
+        component="section"
+        aria-labelledby="sexual-orientation-and-contact-title"
+      >
+        <Grid xxs={12} md={4}>
+          <SelectField
+            name="sexualOrientation"
+            label={fields.sexualOrientation}
+            options={sexualOrientationOptions}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        <Grid xxs={12} md={4}>
+          <NumberField
+            name="numberOfSexualPartnersLast12Months"
+            label={
+              <FormLabel
+                sx={multiLineEllipsis(1)}
+                title={fields.numberOfSexualPartnersLast12Months}
+              >
+                {fields.numberOfSexualPartnersLast12Months}
+              </FormLabel>
+            }
+            required="Bitte eine Zahl eingeben"
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+        <Grid xxs={12} md={4}>
+          <SelectField
+            name="sexualContact"
+            label={fields.sexualContact}
+            options={sexualContactOptions}
+            component={AutoWidthHorizontalField}
+          />
+        </Grid>
+      </FormGroupGrid>
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
index 25dbbd463..948e0bc8f 100644
--- a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx
@@ -7,8 +7,8 @@ import { ApiBaseFeature, ApiUserRole } from "@eshg/employee-portal-api/base";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
 import {
-  SideNavigationItem,
   SideNavigationSubItem,
+  UseSideNavigationItemsResult,
 } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { HivOutlined } from "@/lib/shared/components/icons/HivOutlined";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
@@ -38,8 +38,11 @@ const defaultSubItems: SideNavigationSubItem[] = [
   },
 ];
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   const isModuleEnabled = useIsNewFeatureEnabled(ApiBaseFeature.StiProtection);
   const subItems = defaultSubItems;
-  return isModuleEnabled ? [{ ...sideNavigationItem, subItems }] : [];
+  return {
+    isLoading: false,
+    items: isModuleEnabled ? [{ ...sideNavigationItem, subItems }] : [],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
index 488aad6e8..48976bb5f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/appointmentTypes.ts
@@ -19,9 +19,7 @@ export function useUpdateAppointmentType() {
   const appointmentTypeApi = useAppointmentTypeApi();
   return useHandledMutation({
     mutationFn: (wrapper: ApiUpdateAppointmentTypeRequestWrapper) =>
-      appointmentTypeApi
-        .updateAppointmentType(wrapper.id, wrapper.request)
-        .then(),
+      appointmentTypeApi.updateAppointmentType(wrapper.id, wrapper.request),
     onSuccess: () => {
       snackbar.confirmation("Der Termintyp wurde aktualisiert.");
     },
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
index b5b48ee41..1d470abbd 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation.ts
@@ -4,6 +4,8 @@
  */
 
 import {
+  AbortDraftVaccinationConsultationRequest,
+  AcceptDraftVaccinationConsultationRequest,
   AddProcedureStepRequest,
   ApiPatchVaccinationConsultationPatientRequest,
   ApiPatchVaccinationConsultationTravelDetailsRequest,
@@ -287,3 +289,29 @@ export function useSyncPerson(procedureId: string) {
     onSuccess: () => snackbar.confirmation("Die Änderungen wurden übernommen."),
   });
 }
+
+export function useAboardDraftVaccinationConsultation() {
+  const snackbar = useSnackbar();
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+
+  return useHandledMutation({
+    mutationFn: (request: AbortDraftVaccinationConsultationRequest) =>
+      vaccinationConsultationApi.abortDraftVaccinationConsultationRaw(request),
+    onSuccess: () => {
+      snackbar.confirmation("Vorgang erfolgreich abgebrochen.");
+    },
+  });
+}
+
+export function useAcceptDraftVaccinationConsultation() {
+  const snackbar = useSnackbar();
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+
+  return useHandledMutation({
+    mutationFn: (request: AcceptDraftVaccinationConsultationRequest) =>
+      vaccinationConsultationApi.acceptDraftVaccinationConsultationRaw(request),
+    onSuccess: () => {
+      snackbar.confirmation("Vorgang erfolgreich gestartet.");
+    },
+  });
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
index c6016e152..5da72809c 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation.ts
@@ -11,22 +11,15 @@ import { mapAppointment } from "@/lib/businessModules/travelMedicine/api/models/
 import { mapAssignableService } from "@/lib/businessModules/travelMedicine/api/models/AssignableService";
 import { vaccinationConsultationApiQueryKey } from "@/lib/businessModules/travelMedicine/api/queries/queryKeys";
 
-export function useGetAllProcedureAppointmentSummaries(
-  dateRangeStart: Date,
-  dateRangeEnd: Date,
-) {
+export function useGetAllProcedureAppointmentSummaries(date: Date) {
   const vaccinationConsultationApi = useVaccinationConsultationApi();
   return useQuery({
     queryKey: vaccinationConsultationApiQueryKey([
       "getAllProcedureAppointmentSummaries",
-      dateRangeStart,
-      dateRangeEnd,
+      date,
     ]),
     queryFn: () =>
-      vaccinationConsultationApi.getAllProcedureAppointmentSummaries(
-        dateRangeStart,
-        dateRangeEnd,
-      ),
+      vaccinationConsultationApi.getAllProcedureAppointmentSummaries(date),
   });
 }
 
@@ -79,6 +72,18 @@ export function useGetAllMedicalHistoriesQuery(procedureId: string) {
   });
 }
 
+export function useGetAllInformationStatementsQuery(procedureId: string) {
+  const vaccinationConsultationApi = useVaccinationConsultationApi();
+  return queryOptions({
+    queryKey: vaccinationConsultationApiQueryKey([
+      "getInformationStatements",
+      procedureId,
+    ]),
+    queryFn: () =>
+      vaccinationConsultationApi.getInformationStatements(procedureId),
+  });
+}
+
 export function useGetVaccinationConsultationCertificatesQuery(
   procedureId: string,
 ) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
index 7a95d4355..273c045a4 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/appointment/InitialAppointmentForm.tsx
@@ -36,7 +36,7 @@ export interface InitialAppointmentFormValuesProps {
   selectedPerson?: LegacyPerson;
   initialStepAppointmentType: ApiAppointmentType;
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   appointmentBlockDateOption?: SelectOption;
   userDefinedAppointmentDate?: string;
   appointmentTypeStandardDuration: number;
@@ -66,7 +66,7 @@ export function InitialAppointmentForm({
 
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
@@ -87,7 +87,7 @@ export function InitialAppointmentForm({
     <Formik
       initialValues={{
         ...initialValues,
-        appointmentBlockDate: initialValues.appointmentBlockDate ?? "",
+        appointmentBlockDate: initialValues.appointmentBlockDate ?? undefined,
         userDefinedAppointmentDate:
           initialValues.userDefinedAppointmentDate ??
           format(new Date(), "yyyy-MM-dd'T'HH:mm"),
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
index e2ece3878..6ca81fd17 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/personSidebar/personSidebarHelper.ts
@@ -10,6 +10,7 @@ import {
   ApiPostVaccinationConsultationRequest,
   ApiTravelType,
 } from "@eshg/employee-portal-api/travelMedicine";
+import { durationBetweenDatesInMinutes } from "@eshg/lib-portal/helpers/dateTime";
 import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
 import { mapOptionalValue } from "@eshg/lib-portal/helpers/form";
 
@@ -77,14 +78,15 @@ export function mapToApiPostVaccinationConsultationRequest(
 ): ApiPostVaccinationConsultationRequest {
   let appointmentStart;
   let durationInMinutes;
-  // todo needs to be extended when working with citizen portal
   if (data.bookingType == ApiAppointmentBookingType.UserDefined) {
     appointmentStart = new Date(data.userDefinedAppointmentDate!);
     durationInMinutes = data.appointmentTypeStandardDuration;
   } else {
-    const split = data.appointmentBlockDate!.split(",");
-    appointmentStart = new Date(split.at(0)!);
-    durationInMinutes = Number.parseInt(split.at(1)!);
+    appointmentStart = data.appointmentBlockDate!.start;
+    durationInMinutes = durationBetweenDatesInMinutes(
+      data.appointmentBlockDate!.start,
+      data.appointmentBlockDate!.end,
+    );
   }
   return {
     ...data,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
index 32766c142..98babe500 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateEditor.tsx
@@ -131,12 +131,13 @@ export function InformationStatementTemplateEditor(
   }
 
   async function createNewTemplate(values: TemplateValues) {
-    await createInformationStatementTemplate
-      .mutateAsync(createTemplateRequest(values, newTemplateState), {
+    await createInformationStatementTemplate.mutateAsync(
+      createTemplateRequest(values, newTemplateState),
+      {
         onSuccess: () =>
           router.push(routes.informationStatementTemplates.index),
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
index ff0f420dc..09ee6d44f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/informationStatement/InformationStatementTemplateOverviewTable.tsx
@@ -29,7 +29,7 @@ export function InformationStatementTemplateOverviewTable() {
     useDeleteInformationStatementTemplate();
 
   async function deleteEntry(entryId: string) {
-    await deleteInformationStatementTemplate.mutateAsync(entryId).catch();
+    await deleteInformationStatementTemplate.mutateAsync(entryId);
   }
 
   return (
@@ -54,10 +54,11 @@ export function InformationStatementTemplateOverviewTable() {
         <DataTable
           data={allInformationStatementTemplates}
           columns={informationStatementColumns(deleteEntry)}
-          rowNavRoute={(row) =>
-            routes.informationStatementTemplates.details(row.original.id)
-          }
-          focusColumnHeader="name"
+          rowNavigation={{
+            route: (row) =>
+              routes.informationStatementTemplates.details(row.original.id),
+            focusColumnAccessorKey: "name",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
index f1ba1d586..5be00ffd5 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/templates/medicalHistory/MedicalHistoryTemplateOverviewTable.tsx
@@ -86,10 +86,11 @@ export function MedicalHistoryTemplateOverviewTable() {
             updateFollowUpFlag,
             deleteEntry,
           )}
-          rowNavRoute={(row) =>
-            routes.medicalHistoryTemplates.details(row.original.id)
-          }
-          focusColumnHeader="title"
+          rowNavigation={{
+            route: (row) =>
+              routes.medicalHistoryTemplates.details(row.original.id),
+            focusColumnAccessorKey: "title",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
index d99559bc7..f90ae83fa 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultationSearch/VaccinationConsultationsSearchTable.tsx
@@ -103,9 +103,11 @@ export function VaccinationConsultationsSearchTable() {
           }
           columns={searchColumns()}
           sorting={tableControl.tableSorting}
-          rowNavRoute={(row) =>
-            routes.procedures.baseData(row.original.procedureId)
-          }
+          rowNavigation={{
+            route: (row) =>
+              routes.procedures.baseData(row.original.procedureId),
+            focusColumnAccessorKey: "lastName",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
index 253a6139d..becbd742d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabNavigationToolbar.tsx
@@ -6,7 +6,9 @@
 "use client";
 
 import { ApiUserRole } from "@eshg/employee-portal-api/base";
+import { ApiTravelMedicineFeature } from "@eshg/employee-portal-api/travelMedicine";
 import {
+  DocumentScannerOutlined,
   FormatListBulletedOutlined,
   ReceiptOutlined,
   TextSnippetOutlined,
@@ -14,8 +16,10 @@ import {
 } from "@mui/icons-material";
 import { Chip } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
+import { isPlainObject } from "remeda";
 
 import { procedureStatusNames } from "@/lib/baseModule/api/procedures/enums";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
 import { useGetStatusQuery } from "@/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation";
 import { VaccinationConsultationTabHeader } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationTabHeader";
 import { routes as businessRoutes } from "@/lib/businessModules/travelMedicine/shared/routes";
@@ -32,10 +36,13 @@ export function VaccinationConsultationTabNavigationToolbar({
   const hasTravelMedicineAdminRole = useHasUserRoleCheck(
     ApiUserRole.TravelMedicineAdmin,
   );
-  const tabItems = createTabItems(id);
+  const isInformationStatementsEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
+  );
   const [{ data: status }] = useSuspenseQueries({
     queries: [useGetStatusQuery(id)],
   });
+  const tabItems = createTabItems(id, isInformationStatementsEnabled);
 
   return (
     <TabNavigationToolbar
@@ -53,7 +60,10 @@ export function VaccinationConsultationTabNavigationToolbar({
   );
 }
 
-function createTabItems(procedureId: string): TabNavigationItem[] {
+function createTabItems(
+  procedureId: string,
+  isInformationStatementsEnabled: boolean,
+): TabNavigationItem[] {
   return [
     {
       tabButtonName: "Vorgangsdaten",
@@ -65,6 +75,11 @@ function createTabItems(procedureId: string): TabNavigationItem[] {
       href: `${businessRoutes.procedures.medicalHistories(procedureId)}`,
       decorator: <FormatListBulletedOutlined />,
     },
+    isInformationStatementsEnabled && {
+      tabButtonName: "Aufklärungsbögen",
+      href: `${businessRoutes.procedures.informationStatements(procedureId)}`,
+      decorator: <DocumentScannerOutlined />,
+    },
     {
       tabButtonName: "Bescheinigungen",
       href: `${businessRoutes.procedures.certificates(procedureId)}`,
@@ -75,5 +90,5 @@ function createTabItems(procedureId: string): TabNavigationItem[] {
       href: `${businessRoutes.procedures.progressEntries(procedureId).index}`,
       decorator: <TimelineOutlined />,
     },
-  ];
+  ].filter(isPlainObject);
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
index b4c7676e5..b771dba9a 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx
@@ -34,18 +34,21 @@ import { TableSheet } from "@/lib/shared/components/table/TableSheet";
 import { TextInputClientFilter } from "@/lib/shared/components/tableFilters/TextInputClientFilter";
 import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl";
 
-export function VaccinationConsultationsOverviewTable() {
+export function VaccinationConsultationsOverviewTable(
+  props: Readonly<{
+    date?: string;
+  }>,
+) {
   const tableControl = useTableControl({});
   const [lastName, setLastName] = useState("");
   const [firstName, setFirstName] = useState("");
   const [dateOfBirth, setDateOfBirth] = useState("");
   const [dayOfAppointmentFilter, setDayOfAppointmentFilter] = useState<Date>(
-    new Date(),
+    props.date ? new Date(props.date) : new Date(),
   );
   const [status, setStatus] = useState<ApiProcedureStatus[]>([]);
   const queryResult = useGetAllProcedureAppointmentSummaries(
     dayOfAppointmentFilter,
-    dayOfAppointmentFilter,
   );
   const allAppointmentOverviewEntries = useMemo(() => {
     return queryResult.data ?? { appointmentOverviewEntries: [] };
@@ -71,11 +74,7 @@ export function VaccinationConsultationsOverviewTable() {
   function updateTimeRange(newDate: Date) {
     tableControl.setFilter([
       {
-        name: "dateRangeStart",
-        value: toDateString(newDate),
-      },
-      {
-        name: "dateRangeEnd",
+        name: "date",
         value: toDateString(newDate),
       },
     ]);
@@ -235,10 +234,11 @@ export function VaccinationConsultationsOverviewTable() {
             manualSorting: false,
             initialSorting,
           }}
-          rowNavRoute={(row) =>
-            routes.procedures.baseData(row.original.procedureId)
-          }
-          focusColumnHeader="lastName"
+          rowNavigation={{
+            route: (row) =>
+              routes.procedures.baseData(row.original.procedureId),
+            focusColumnAccessorKey: "lastName",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx
new file mode 100644
index 000000000..4a8d0e16e
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal.tsx
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  AbortDraftVaccinationConsultationRequest,
+  ApiGetVaccinationConsultationDetailsResponse,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { Button, Stack, Typography } from "@mui/joy";
+import { useRouter } from "next/navigation";
+
+import { useAboardDraftVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { routes } from "@/lib/businessModules/travelMedicine/shared/routes";
+import { BaseModal, BaseModalProps } from "@/lib/shared/components/BaseModal";
+
+interface AbortProcedureModalProps extends Omit<BaseModalProps, "children"> {
+  procedure: ApiGetVaccinationConsultationDetailsResponse;
+}
+
+export function AbortProcedureModal(props: AbortProcedureModalProps) {
+  const abortProcedure = useAboardDraftVaccinationConsultation();
+  const router = useRouter();
+
+  async function handleSubmit() {
+    const request: AbortDraftVaccinationConsultationRequest = {
+      procedureId: props.procedure.procedureId,
+    };
+    await abortProcedure.mutateAsync(request).then(() => {
+      router.push(routes.procedures.index);
+    });
+  }
+
+  return (
+    <BaseModal modalTitle="Vorgang löschen?" {...props}>
+      <Typography level="body-md" marginBottom={3}>
+        Der Vorgang wird gelöscht und kann nicht mehr wiederhergestellt werden.
+      </Typography>
+      <Stack
+        direction="row"
+        gap={2}
+        alignItems="center"
+        justifyContent="flex-end"
+      >
+        <Button variant="outlined" color="neutral" onClick={props.onClose}>
+          Abbrechen
+        </Button>
+
+        <Button color="danger" onClick={handleSubmit}>
+          Löschen
+        </Button>
+      </Stack>
+    </BaseModal>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx
new file mode 100644
index 000000000..5786ab8c4
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm.tsx
@@ -0,0 +1,150 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import { ApiPatient } from "@eshg/employee-portal-api/travelMedicine";
+import { ComponentType, Ref, useState } from "react";
+import { isDefined } from "remeda";
+
+import { PatientDetails } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails";
+import { SidebarFormHandle } from "@/lib/shared/components/form/SidebarForm";
+import {
+  DefaultPersonFormValues,
+  defaultPersonFormValues,
+} from "@/lib/shared/components/personSidebar/form/DefaultPersonForm";
+import { PersonFormValues } from "@/lib/shared/components/personSidebar/form/PersonSidebarForm";
+import { PersonSearchResults } from "@/lib/shared/components/personSidebar/search/PersonSearchResults";
+import {
+  SearchPersonFormProps,
+  SearchPersonFormValues,
+} from "@/lib/shared/components/personSidebar/search/SearchPersonSidebar";
+
+type CreatePersonStateMapper<TSearchValues, TCreateValues> = (props: {
+  inputs: TSearchValues;
+  addressRequired?: boolean;
+}) => TCreateValues;
+
+interface SearchFormProps<TSearchValues> {
+  initialSearchState: TSearchValues;
+}
+
+type CreateFormProps<TSearchValues, TCreateValues> =
+  | {
+      initialCreateState: CreatePersonStateMapper<TSearchValues, TCreateValues>;
+      createFormComponent: ComponentType<SearchPersonFormProps<TCreateValues>>;
+    }
+  | {
+      initialCreateState?: never;
+      createFormComponent?: never;
+    };
+
+export type PersonSidebarProps<
+  TSearchValues extends SearchPersonFormValues = SearchPersonFormValues,
+  TCreateValues extends PersonFormValues = DefaultPersonFormValues,
+> = SearchFormProps<TSearchValues> &
+  CreateFormProps<TSearchValues, TCreateValues> & {
+    onCancel: () => void;
+    onSelect: (props: {
+      person: ApiGetReferencePersonResponse | ApiPatient;
+    }) => Promise<void>;
+    sidebarFormRef: Ref<SidebarFormHandle>;
+    title: string;
+    submitLabel: string;
+    addressRequired?: boolean;
+    queryResults?: ApiGetReferencePersonResponse[];
+    initialPatient: ApiPatient;
+  };
+
+type SidebarMode = "create" | "search_results" | "display";
+
+interface SidebarState<TSearchValues, TCreateValues> {
+  mode: SidebarMode;
+  createState: TCreateValues;
+  searchState: TSearchValues;
+  searchResult: ApiGetReferencePersonResponse[];
+  selectedPerson: ApiGetReferencePersonResponse | ApiPatient | undefined;
+}
+
+export function AcceptProcedureForm<
+  TSearchValues extends SearchPersonFormValues = SearchPersonFormValues,
+  TCreateValues extends PersonFormValues = DefaultPersonFormValues,
+>(props: PersonSidebarProps<TSearchValues, TCreateValues>) {
+  const initialSearchState = props.initialSearchState;
+  const mapCreateState = (props.initialCreateState ??
+    defaultPersonFormValues) as CreatePersonStateMapper<
+    TSearchValues,
+    TCreateValues
+  >;
+
+  function getInitialState(): SidebarState<TSearchValues, TCreateValues> {
+    return {
+      mode: "search_results",
+      createState: mapCreateState({
+        inputs: initialSearchState,
+        addressRequired: props.addressRequired,
+      }),
+      searchState: initialSearchState,
+      searchResult: props.queryResults ?? [],
+      selectedPerson: props.initialPatient,
+    };
+  }
+
+  const [state, setState] = useState(getInitialState);
+
+  let activeMode: SidebarMode;
+  if (state.mode === "display" && isDefined(state.selectedPerson)) {
+    activeMode = "display";
+  } else {
+    activeMode = "search_results";
+  }
+
+  return (
+    <>
+      {activeMode === "search_results" && (
+        <PersonSearchResults
+          title={props.title}
+          sidebarFormRef={props.sidebarFormRef}
+          onCancel={props.onCancel}
+          inputs={state.searchState}
+          persons={state.searchResult}
+          onSelectPerson={(person) =>
+            setState((previous) => ({
+              ...previous,
+              mode: "display",
+              selectedPerson: person,
+            }))
+          }
+          onCreatePerson={() =>
+            setState((previous) => ({
+              ...previous,
+              mode: "display",
+              selectedPerson: props.initialPatient,
+            }))
+          }
+        />
+      )}
+      {activeMode === "display" && isDefined(state.selectedPerson) && (
+        <PatientDetails
+          title={props.title}
+          person={state.selectedPerson}
+          initialPatient={props.initialPatient}
+          submitLabel={props.submitLabel}
+          onCancel={props.onCancel}
+          onBack={() =>
+            setState((previous) => ({
+              ...previous,
+              mode: "search_results",
+            }))
+          }
+          onSubmit={(person) =>
+            props.onSelect({
+              person: person,
+            })
+          }
+        />
+      )}
+    </>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx
new file mode 100644
index 000000000..eeaa92090
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar.tsx
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import {
+  ApiCountryCode,
+  ApiGender,
+  ApiGetVaccinationConsultationDetailsResponse,
+  ApiPatient,
+  ApiPersonAddress,
+  ApiSalutation,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { toDateString } from "@eshg/lib-portal/helpers/dateTime";
+import { OptionalFieldValue } from "@eshg/lib-portal/types/form";
+
+import { useAcceptDraftVaccinationConsultation } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { AcceptProcedureForm } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureForm";
+import { SearchPersonFormValues } from "@/lib/shared/components/personSidebar/search/SearchPersonSidebar";
+import {
+  SidebarWithFormRefProps,
+  UseSidebarWithFormRefResult,
+  useSidebarWithFormRef,
+} from "@/lib/shared/hooks/useSidebarWithFormRef";
+
+export function useAcceptProcedureSidebar(): UseSidebarWithFormRefResult<AcceptProcedureSidebarProps> {
+  return useSidebarWithFormRef({
+    component: AcceptProcedureSidebar,
+  });
+}
+
+interface AcceptProcedureSidebarProps extends SidebarWithFormRefProps {
+  procedure: ApiGetVaccinationConsultationDetailsResponse;
+  queryResults?: ApiGetReferencePersonResponse[];
+}
+
+export interface PatientFormValues extends SearchPersonFormValues {
+  emailAddresses: OptionalFieldValue<string>[];
+  phoneNumbers: OptionalFieldValue<string>[];
+  address: OptionalFieldValue<ApiPersonAddress>;
+  countryOfBirth: OptionalFieldValue<ApiCountryCode>;
+  gender: OptionalFieldValue<ApiGender>;
+  nameAtBirth: OptionalFieldValue<string>;
+  placeOfBirth: OptionalFieldValue<string>;
+  salutation: OptionalFieldValue<ApiSalutation>;
+  title: OptionalFieldValue<string>;
+}
+
+export function instanceOfApiGetReferencePersonResponse(
+  data: ApiGetReferencePersonResponse | ApiPatient,
+): data is ApiGetReferencePersonResponse {
+  return "id" in data;
+}
+
+function AcceptProcedureSidebar(props: Readonly<AcceptProcedureSidebarProps>) {
+  const acceptDraftVaccinationConsultation =
+    useAcceptDraftVaccinationConsultation();
+
+  async function handleCreate(procedureId: string, referencePersonId?: string) {
+    const request = {
+      procedureId: procedureId,
+      apiPatchAcceptDraftRequest: {
+        referencePersonId: referencePersonId ?? undefined,
+      },
+    };
+    await acceptDraftVaccinationConsultation.mutateAsync(request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
+  }
+
+  const personSearchFormInitialValues: PatientFormValues = {
+    firstName: props.procedure.patient.firstName,
+    lastName: props.procedure.patient.lastName,
+    dateOfBirth: toDateString(props.procedure.patient.dateOfBirth),
+    emailAddresses: props.procedure.patient.emailAddresses ?? [],
+    phoneNumbers: props.procedure.patient.phoneNumbers ?? [],
+    address: props.procedure.patient.address ?? "",
+    countryOfBirth: props.procedure.patient.countryOfBirth ?? "",
+    gender: props.procedure.patient.gender ?? ApiGender.NotSpecified,
+    nameAtBirth: props.procedure.patient.nameAtBirth ?? "",
+    placeOfBirth: props.procedure.patient.placeOfBirth ?? "",
+    salutation:
+      props.procedure.patient.salutation ?? ApiSalutation.NotSpecified,
+    title: props.procedure.patient.title ?? "",
+  };
+
+  return (
+    <AcceptProcedureForm
+      title={"Vorgang starten"}
+      onSelect={async ({ person }) => {
+        let referencePersonId;
+        if (instanceOfApiGetReferencePersonResponse(person)) {
+          referencePersonId = person.id;
+        }
+
+        await handleCreate(props.procedure.procedureId, referencePersonId);
+      }}
+      submitLabel={"Vorgang starten"}
+      sidebarFormRef={props.formRef}
+      initialSearchState={personSearchFormInitialValues}
+      addressRequired={false}
+      onCancel={props.onClose}
+      queryResults={props.queryResults ?? undefined}
+      initialPatient={props.procedure.patient}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx
deleted file mode 100644
index e349b9179..000000000
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import {
-  ApiProcedureStatus,
-  ApiServiceStatus,
-} from "@eshg/employee-portal-api/travelMedicine";
-import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { Button, Stack } from "@mui/joy";
-import { Dispatch, SetStateAction } from "react";
-
-import {
-  UsePatchStatusRequest,
-  usePatchStatus,
-} from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
-import { CreateProcedureValues } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails";
-import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
-
-export function CloseProcedurePanel(
-  props: Readonly<{
-    procedure: CreateProcedureValues;
-    dataTestid: string;
-    setIsProcedureClosed: Dispatch<SetStateAction<boolean>>;
-  }>,
-) {
-  const snackbar = useSnackbar();
-  const patchStatus = usePatchStatus();
-
-  function procedureHasPlannedServices() {
-    return props.procedure.services.some(
-      (s) => s.status === ApiServiceStatus.Planned,
-    );
-  }
-  async function handleCloseProcedure() {
-    if (procedureHasPlannedServices()) {
-      snackbar.error(
-        "Es befinden sich noch geplante Leistungen im Vorgang, diese müssen zunächst durchgeführt oder aus dem Termin entfernt werden, um den Vorgang schließen zu können.",
-      );
-    } else {
-      const request: UsePatchStatusRequest = {
-        procedureId: props.procedure.externalId,
-        apiProcedureStatus: ApiProcedureStatus.Closed,
-      };
-      await patchStatus
-        .mutateAsync(request)
-        .then(() => props.setIsProcedureClosed(true));
-    }
-  }
-
-  async function handleReopenProcedure() {
-    const request: UsePatchStatusRequest = {
-      procedureId: props.procedure.externalId,
-      apiProcedureStatus: ApiProcedureStatus.Open,
-    };
-    await patchStatus
-      .mutateAsync(request)
-      .then(() => props.setIsProcedureClosed(false));
-  }
-
-  return (
-    <InformationSheet>
-      {props.procedure.status === ApiProcedureStatus.Closed ? (
-        <Button
-          color="danger"
-          onClick={handleReopenProcedure}
-          fullWidth
-          data-testid={props.dataTestid}
-        >
-          Vorgang wiedereröffnen
-        </Button>
-      ) : (
-        <Stack direction={{ xxs: "column", md: "row" }} gap={2}>
-          <Button
-            onClick={handleCloseProcedure}
-            fullWidth
-            data-testid={props.dataTestid}
-          >
-            Vorgang schließen
-          </Button>
-        </Stack>
-      )}
-    </InformationSheet>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
index df8d061f3..c0afb336a 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid.tsx
@@ -7,9 +7,8 @@ import { Grid } from "@mui/joy";
 import { ReactNode } from "react";
 
 export function DetailsGrid({ children }: Readonly<{ children: ReactNode }>) {
-  const SPACING = { xxs: 2, sm: 3, md: 3, xxl: 3 };
   return (
-    <Grid container columnSpacing={2} rowSpacing={SPACING}>
+    <Grid container columnSpacing={2} rowSpacing={2}>
       {children}
     </Grid>
   );
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
deleted file mode 100644
index 97de7c8d5..000000000
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright 2024 SCOOP Software GmbH, cronn GmbH
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-"use client";
-
-import {
-  ApiInformationStatement,
-  ApiTravelMedicineFeature,
-} from "@eshg/employee-portal-api/travelMedicine";
-import { AddOutlined } from "@mui/icons-material";
-import { Button, Grid } from "@mui/joy";
-
-import { useDeleteInformationStatement } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
-import { TableTitle } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TableTitle";
-import { useInformationStatementSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar";
-import { DataTable } from "@/lib/shared/components/table/DataTable";
-import { TablePage } from "@/lib/shared/components/table/TablePage";
-import { TableSheet } from "@/lib/shared/components/table/TableSheet";
-
-import { informationStatementsColumns } from "./InformationStatementsColumns";
-
-export function InformationStatementsTable({
-  procedureId,
-  isProcedureClosed,
-  data,
-}: Readonly<{
-  procedureId: string;
-  isProcedureClosed: boolean;
-  data: ApiInformationStatement[];
-}>) {
-  const deleteInformationStatementApi = useDeleteInformationStatement();
-  const isInformationStatementEnabled = useIsNewFeatureEnabled(
-    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
-  );
-
-  const informationStatementSidebar = useInformationStatementSidebar();
-
-  function deleteInformationStatement(informationStatementId: string) {
-    return deleteInformationStatementApi.mutate({
-      procedureId,
-      informationStatementId,
-    });
-  }
-
-  return (
-    <TablePage data-testid="vc-information-statements">
-      <TableSheet
-        title={<TableTitle title="Aufklärungsbögen" />}
-        footer={
-          !isProcedureClosed &&
-          isInformationStatementEnabled && (
-            <Grid xs={12}>
-              <Button
-                color="primary"
-                variant="plain"
-                startDecorator={<AddOutlined />}
-                onClick={() =>
-                  informationStatementSidebar.open({
-                    procedureId: procedureId,
-                  })
-                }
-                disabled={isProcedureClosed}
-              >
-                Bogen hinzufügen
-              </Button>
-            </Grid>
-          )
-        }
-        hideTable={data.length === 0}
-      >
-        <DataTable
-          data={data}
-          columns={informationStatementsColumns({
-            isProcedureClosed,
-            onDeleteInformationStatement: (informationStatementId: string) =>
-              deleteInformationStatement(informationStatementId),
-          })}
-        />
-      </TableSheet>
-    </TablePage>
-  );
-}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
index afdf720c0..be3d9a29b 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InitialAppointmentTile.tsx
@@ -125,7 +125,7 @@ function EditInitialAppointmentSidebar({
         false,
       ),
       isEditInitialAppointmentMode: true,
-      appointmentBlockDate: appointmentBlockDateOption?.value,
+      appointmentBlockDate: undefined,
       appointmentBlockDateOption: appointmentBlockDateOption,
     };
   }
@@ -136,7 +136,7 @@ function EditInitialAppointmentSidebar({
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
 
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx
new file mode 100644
index 000000000..e9ba392c1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx
@@ -0,0 +1,234 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiGetReferencePersonResponse } from "@eshg/employee-portal-api/base";
+import {
+  ApiGender,
+  ApiPatient,
+  ApiSalutation,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { formatDate } from "@eshg/lib-portal/formatters/dateTime";
+import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form";
+import { Divider, Stack, Typography } from "@mui/joy";
+import { Formik } from "formik";
+import { isDefined } from "remeda";
+
+import { instanceOfApiGetReferencePersonResponse } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar";
+import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails";
+import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell";
+import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow";
+import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar";
+import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
+import {
+  GENDER_VALUES,
+  SALUTATION_VALUES,
+  getOptionalTitle,
+} from "@/lib/shared/components/personSidebar/constants";
+import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
+import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
+import { BaseAddress } from "@/lib/shared/helpers/address";
+import { translateCountry } from "@/lib/shared/helpers/i18n";
+
+export interface PatientDetailsProps {
+  title: string;
+  submitLabel: string;
+  person: ApiGetReferencePersonResponse | ApiPatient;
+  initialPatient: ApiPatient;
+  onSubmit: (
+    values: ApiGetReferencePersonResponse | ApiPatient,
+  ) => Promise<void>;
+  onBack?: () => void;
+  onCancel: () => void;
+}
+
+export function PatientDetails(props: Readonly<PatientDetailsProps>) {
+  const fieldName = createFieldNameMapper<
+    ApiGetReferencePersonResponse | ApiPatient
+  >();
+  const person = instanceOfApiGetReferencePersonResponse(props.person)
+    ? props.person
+    : {
+        ...props.person,
+        contactAddress: isDefined(props.person.address)
+          ? ({
+              type: "DomesticAddress",
+              ...props.person.address,
+            } satisfies BaseAddress)
+          : undefined,
+        salutation: isDefined(props.person.salutation)
+          ? props.person.salutation
+          : ApiSalutation.NotSpecified,
+        gender: isDefined(props.person.gender)
+          ? props.person.gender
+          : ApiGender.NotSpecified,
+        emailAddresses: isDefined(props.person.emailAddresses)
+          ? props.person.emailAddresses
+          : [],
+        phoneNumbers: isDefined(props.person.phoneNumbers)
+          ? props.person.phoneNumbers
+          : [],
+      };
+
+  const showEmailPhoneSection =
+    isDefined(person.phoneNumbers) && isDefined(person.emailAddresses)
+      ? person.phoneNumbers.length + person.emailAddresses.length > 0
+      : false;
+
+  const showInitialPatientEmailPhoneSection =
+    instanceOfApiGetReferencePersonResponse(props.person) &&
+    isDefined(props.initialPatient.phoneNumbers) &&
+    isDefined(props.initialPatient.emailAddresses)
+      ? props.initialPatient.phoneNumbers.length +
+          props.initialPatient.emailAddresses.length >
+        0
+      : false;
+
+  return (
+    <Formik
+      initialValues={props.person}
+      onSubmit={props.onSubmit}
+      enableReinitialize
+    >
+      {({ isSubmitting }) => (
+        <SidebarForm>
+          <SidebarContent title={props.title} subtitle="Ausgewählte Person">
+            <Stack gap={2}>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("salutation")}
+                  label={"Anrede"}
+                  value={SALUTATION_VALUES[person.salutation]}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("title")}
+                  label={"Titel"}
+                  value={getOptionalTitle(person.title)}
+                  flexGrow
+                  avoidWrap
+                />
+              </DetailsRow>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("firstName")}
+                  label={"Vorname"}
+                  value={person.firstName}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("lastName")}
+                  label={"Name"}
+                  value={person.lastName}
+                  flexGrow
+                  avoidWrap
+                />
+              </DetailsRow>
+              <DetailsRow>
+                <DetailsCell
+                  name={fieldName("dateOfBirth")}
+                  label={"Geburtsdatum"}
+                  value={formatDate(person.dateOfBirth)}
+                  flexGrow
+                />
+                <DetailsCell
+                  name={fieldName("gender")}
+                  label={"Geschlecht"}
+                  value={GENDER_VALUES[person.gender]}
+                  flexGrow
+                />
+              </DetailsRow>
+              <DetailsCell
+                name={fieldName("nameAtBirth")}
+                label={"Geburtsname"}
+                value={props.person.nameAtBirth}
+              />
+              <DetailsCell
+                name={fieldName("placeOfBirth")}
+                label={"Geburtsort"}
+                value={props.person.placeOfBirth}
+              />
+              <DetailsCell
+                name={fieldName("countryOfBirth")}
+                label={"Geburtsland"}
+                value={
+                  isDefined(props.person.countryOfBirth)
+                    ? translateCountry(props.person.countryOfBirth)
+                    : undefined
+                }
+              />
+
+              {isDefined(person.contactAddress) && (
+                <>
+                  <Divider />
+                  <BaseAddressDetails address={person.contactAddress} />
+                </>
+              )}
+
+              {showEmailPhoneSection && (
+                <>
+                  <Divider />
+                  {person.emailAddresses.map((email, index) => (
+                    <DetailsCell
+                      key={`${email}-${index}`}
+                      name={fieldName("emailAddresses") + "." + index}
+                      label={"E-Mail-Adresse"}
+                      value={email}
+                    />
+                  ))}
+                  {person.phoneNumbers.map((phoneNumber, index) => (
+                    <DetailsCell
+                      key={`${phoneNumber}-${index}`}
+                      name={fieldName("phoneNumbers") + "." + index}
+                      label={"Telefonnummer"}
+                      value={phoneNumber}
+                    />
+                  ))}
+                </>
+              )}
+
+              {showInitialPatientEmailPhoneSection && (
+                <>
+                  <Divider component="div" role="presentation">
+                    <Typography level={"title-sm"} color={"neutral"}>
+                      Gemeldete Kontaktdaten
+                    </Typography>
+                  </Divider>
+                  {isDefined(props.initialPatient.emailAddresses) &&
+                    props.initialPatient.emailAddresses.map((email, index) => (
+                      <DetailsCell
+                        key={`initialPatient-${email}-${index}`}
+                        name={fieldName("emailAddresses") + "." + index}
+                        label={"E-Mail-Adresse"}
+                        value={email}
+                      />
+                    ))}
+                  {isDefined(props.initialPatient.phoneNumbers) &&
+                    props.initialPatient.phoneNumbers.map(
+                      (phoneNumber, index) => (
+                        <DetailsCell
+                          key={`initialPatient-${phoneNumber}-${index}`}
+                          name={fieldName("phoneNumbers") + "." + index}
+                          label={"Telefonnummer"}
+                          value={phoneNumber}
+                        />
+                      ),
+                    )}
+                </>
+              )}
+            </Stack>
+          </SidebarContent>
+          <SidebarActions>
+            <MultiFormButtonBar
+              submitting={isSubmitting}
+              submitLabel={props.submitLabel}
+              onBack={props.onBack}
+              onCancel={props.onCancel}
+            />
+          </SidebarActions>
+        </SidebarForm>
+      )}
+    </Formik>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
index 09fe71314..ae95a9132 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel.tsx
@@ -41,6 +41,7 @@ interface PatientPanelProps {
   patient: ApiPatient;
   person: ApiPersonSync;
   isProcedureClosed: boolean;
+  isProcedureDraft: boolean;
 }
 
 export function PatientPanel({
@@ -48,6 +49,7 @@ export function PatientPanel({
   patient,
   person,
   isProcedureClosed,
+  isProcedureDraft,
 }: Readonly<PatientPanelProps>) {
   const [open, setOpen] = useSearchParam("edit-patient", "boolean");
 
@@ -94,7 +96,7 @@ export function PatientPanel({
     if (resetAndClose) {
       options = { onSuccess: resetAndClose };
     }
-    await updatePatientApi.mutateAsync(request, options).catch();
+    await updatePatientApi.mutateAsync(request, options);
   }
 
   return (
@@ -104,6 +106,7 @@ export function PatientPanel({
           name="patient-card-tile"
           title="Patient"
           buttons={
+            !isProcedureDraft &&
             !isProcedureClosed && (
               <SyncBarrier outdated={person.outdated} syncHref={syncRoute}>
                 <EditButton
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx
new file mode 100644
index 000000000..1c353855a
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel.tsx
@@ -0,0 +1,169 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import {
+  ApiGetVaccinationConsultationDetailsResponse,
+  ApiProcedureStatus,
+  ApiServiceStatus,
+  ApiTravelMedicineFeature,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
+import { Button, Grid } from "@mui/joy";
+import { ReactNode } from "react";
+
+import { useSearchReferencePersonsQuery } from "@/lib/baseModule/api/queries/persons";
+import {
+  UsePatchStatusRequest,
+  useAcceptDraftVaccinationConsultation,
+  usePatchStatus,
+} from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
+import { AbortProcedureModal } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AbortProcedureModal";
+import { useAcceptProcedureSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/AcceptProcedureSidebar";
+import { OpenModalButton } from "@/lib/shared/components/buttons/OpenModalButton";
+import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet";
+
+export function ProcedureActionsPanel(
+  props: Readonly<{
+    procedure: ApiGetVaccinationConsultationDetailsResponse;
+    dataTestid: string;
+  }>,
+) {
+  const citizenPortalProcedureEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalProcedure,
+  );
+  const snackbar = useSnackbar();
+
+  const patchStatus = usePatchStatus();
+  const acceptProcedureSidebar = useAcceptProcedureSidebar();
+  const acceptDraftVaccinationConsultation =
+    useAcceptDraftVaccinationConsultation();
+
+  function procedureHasPlannedServices() {
+    return props.procedure.servicePlanList.some(
+      (s) => s.status === ApiServiceStatus.Planned,
+    );
+  }
+  async function handleCloseProcedure() {
+    if (procedureHasPlannedServices()) {
+      snackbar.error(
+        "Es befinden sich noch geplante Leistungen im Vorgang, diese müssen zunächst durchgeführt oder aus dem Termin entfernt werden, um den Vorgang schließen zu können.",
+      );
+    } else {
+      const request: UsePatchStatusRequest = {
+        procedureId: props.procedure.procedureId,
+        apiProcedureStatus: ApiProcedureStatus.Closed,
+      };
+      await patchStatus.mutateAsync(request);
+    }
+  }
+
+  async function handleReopenProcedure() {
+    const request: UsePatchStatusRequest = {
+      procedureId: props.procedure.procedureId,
+      apiProcedureStatus: ApiProcedureStatus.Open,
+    };
+    await patchStatus.mutateAsync(request);
+  }
+
+  async function handleCreate(procedureId: string) {
+    const request = {
+      procedureId: procedureId,
+      apiPatchAcceptDraftRequest: {
+        referencePersonId: undefined,
+      },
+    };
+    await acceptDraftVaccinationConsultation.mutateAsync(request);
+  }
+
+  const query = useSearchReferencePersonsQuery(
+    {
+      firstName: props.procedure.patient.firstName.trim(),
+      lastName: props.procedure.patient.lastName.trim(),
+      dateOfBirth: new Date(props.procedure.patient.dateOfBirth),
+    },
+    {
+      enabled: true,
+    },
+  );
+
+  const buttons: ReactNode[] = [];
+
+  if (props.procedure.status === ApiProcedureStatus.Open) {
+    buttons.push(
+      <Button key="closeProcedure" onClick={handleCloseProcedure} fullWidth>
+        Vorgang schließen
+      </Button>,
+    );
+  }
+
+  if (props.procedure.status === ApiProcedureStatus.Closed) {
+    buttons.push(
+      <Button
+        key="reopenProcedure"
+        color="danger"
+        onClick={handleReopenProcedure}
+        fullWidth
+      >
+        Vorgang wiedereröffnen
+      </Button>,
+    );
+  }
+
+  if (
+    citizenPortalProcedureEnabled &&
+    props.procedure.status === ApiProcedureStatus.Draft
+  ) {
+    buttons.push(
+      <Grid container spacing={2}>
+        <Grid xs={6} display={"flex"}>
+          <OpenModalButton
+            key="reopenProcedure"
+            renderModal={(modalProps) => (
+              <AbortProcedureModal
+                procedure={props.procedure}
+                {...modalProps}
+              />
+            )}
+            fullWidth
+            color="neutral"
+            variant="soft"
+          >
+            Vorgang abbrechen
+          </OpenModalButton>
+        </Grid>
+        <Grid xs={6} display={"flex"}>
+          <Button
+            key="startProcedure"
+            color="primary"
+            onClick={async () => {
+              if (query.isSuccess && query.data.persons.length > 0) {
+                acceptProcedureSidebar.open({
+                  procedure: props.procedure,
+                  queryResults: query.isSuccess
+                    ? query.data.persons
+                    : undefined,
+                });
+              } else if (query.isSuccess && query.data.persons.length === 0) {
+                await handleCreate(props.procedure.procedureId);
+              }
+            }}
+            fullWidth
+          >
+            Vorgang starten
+          </Button>
+        </Grid>
+      </Grid>,
+    );
+  }
+
+  if (buttons.length === 0) {
+    return null;
+  }
+
+  return (
+    <InformationSheet dataTestId={props.dataTestid}>{buttons}</InformationSheet>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
index 0c940e239..39e63edf1 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/VaccinationConsultationDetails.tsx
@@ -10,23 +10,19 @@ import {
   ApiAppointmentSummary,
   ApiCreatedByUserType,
   ApiGetVaccinationConsultationDetailsResponse,
-  ApiInformationStatement,
   ApiPatient,
   ApiPersonSync,
   ApiProcedureStatus,
   ApiServicePlanEntry,
-  ApiTravelMedicineFeature,
   ApiTravelTimeUnit,
   ApiTravelType,
 } from "@eshg/employee-portal-api/travelMedicine";
+import { Alert } from "@eshg/lib-portal/components/Alert";
 import { Grid, Stack } from "@mui/joy";
-import { useState } from "react";
 
-import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
-import { CloseProcedurePanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/CloseProcedurePanel";
 import { DetailsGrid } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/DetailsGrid";
-import { InformationStatementsTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsTable";
 import { PatientPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientPanel";
+import { ProcedureActionsPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureActionsPanel";
 import { ProcedureDetailsPanel } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureDetailsPanel";
 import { ServicePlanTable } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ServicePlanTable";
 
@@ -41,7 +37,6 @@ export interface CreateProcedureValues {
   travelTimeAmount?: number;
   travelTimeUnit?: ApiTravelTimeUnit;
   services: ApiServicePlanEntry[];
-  informationStatements: ApiInformationStatement[];
   templateId?: string;
   initialAppointment: ApiAppointmentSummary;
   createdByUserType: ApiCreatedByUserType;
@@ -57,13 +52,8 @@ export function VaccinationConsultationDetails(
 ) {
   const initialValues = createInitialFormValues(props.procedure);
 
-  const [isProcedureClosed, setIsProcedureClosed] = useState<boolean>(
-    props.procedure.status === ApiProcedureStatus.Closed,
-  );
-
-  const isInformationStatementEnabled = useIsNewFeatureEnabled(
-    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
-  );
+  const isProcedureClosed: boolean =
+    props.procedure.status === ApiProcedureStatus.Closed;
 
   function createInitialFormValues(
     newData: ApiGetVaccinationConsultationDetailsResponse,
@@ -81,7 +71,6 @@ export function VaccinationConsultationDetails(
       travelTimeAmount:
         newData.travelInformation.travelTimeAmount ?? ("" as unknown as number),
       services: newData.servicePlanList,
-      informationStatements: newData.informationStatements,
       initialAppointment: newData.initialAppointment,
       createdByUserType: newData.createdByUserType,
     };
@@ -89,12 +78,23 @@ export function VaccinationConsultationDetails(
 
   return (
     <DetailsGrid>
+      <Grid xs={12}>
+        {props.procedure.createdByUserType ===
+          ApiCreatedByUserType.CitizenPortal &&
+          props.procedure.status === ApiProcedureStatus.Draft && (
+            <Alert
+              color="warning"
+              message="Dieser Entwurf kommt aus einer externen Quelle. Bitte kontrollieren Sie die Daten, bevor Sie den Vorgang starten."
+            />
+          )}
+      </Grid>
       <Grid xs={9} display={"flex"} data-testid={"patient"}>
         <PatientPanel
           procedureId={initialValues.externalId}
           patient={initialValues.patient}
           person={initialValues.personSync}
           isProcedureClosed={isProcedureClosed}
+          isProcedureDraft={props.procedure.status === ApiProcedureStatus.Draft}
         />
       </Grid>
       <Grid xs={3}>
@@ -103,10 +103,9 @@ export function VaccinationConsultationDetails(
             initialValues={initialValues}
             procedureClosed={isProcedureClosed}
           />
-          <CloseProcedurePanel
-            procedure={initialValues}
-            dataTestid="button-close-reopen"
-            setIsProcedureClosed={setIsProcedureClosed}
+          <ProcedureActionsPanel
+            procedure={props.procedure}
+            dataTestid="procedure-actions"
           />
         </Stack>
       </Grid>
@@ -122,16 +121,6 @@ export function VaccinationConsultationDetails(
           createdByUserType={initialValues.createdByUserType}
         ></ServicePlanTable>
       </Grid>
-
-      <Grid xs={12}>
-        {isInformationStatementEnabled && (
-          <InformationStatementsTable
-            data={initialValues.informationStatements}
-            procedureId={initialValues.externalId ?? ""}
-            isProcedureClosed={isProcedureClosed}
-          ></InformationStatementsTable>
-        )}
-      </Grid>
     </DetailsGrid>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
index 1fefe9102..632f1c712 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServiceAppointmentSidebar.tsx
@@ -74,7 +74,7 @@ function AddServiceAppointmentSidebar(
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
 
@@ -103,20 +103,18 @@ function AddServiceAppointmentSidebar(
 
   async function handleSubmit(values: AddServiceAppointmentFormValues) {
     const useAddProcedureRequest = createUseAddProcedureRequest(values);
-    await addProcedure
-      .mutateAsync(useAddProcedureRequest.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await addProcedure.mutateAsync(useAddProcedureRequest.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   const initialServiceAppointmentFormValues: AddServiceAppointmentFormValues = {
     procedureId: props.procedureId,
     serviceChecks: [],
     bookingType: "" as ApiAppointmentBookingType,
-    appointmentBlockDate: "",
+    appointmentBlockDate: undefined,
     userDefinedAppointmentDate: format(new Date(), "yyyy-MM-dd'T'HH:mm"),
     appointmentTypeStandardDuration: vaccinationStandardDuration as number,
     appointmentType: "" as ApiAppointmentType,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
index 8328d80c4..2b9e6327b 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AddServicePlanSidebar.tsx
@@ -90,13 +90,11 @@ function AddServicePlanSidebar(props: Readonly<AddServicePlanSidebarProps>) {
   }
 
   async function handleSubmit(values: AddServicePlanFormValues) {
-    await postServicesApi
-      .mutateAsync(createPostServicesRequest(values), {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await postServicesApi.mutateAsync(createPostServicesRequest(values), {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   const initServicesValues: ServicesRequest = {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
index 13c8833c4..e8905d842 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/AssignServiceSidebar.tsx
@@ -55,13 +55,14 @@ function AssignServiceSidebar(props: Readonly<AssignServiceSidebarProps>) {
   }
 
   async function handleSubmit(values: AssignServiceFormValues) {
-    await assignStepToServiceApi
-      .mutateAsync(createAssignsStepToServiceRequest(values), {
+    await assignStepToServiceApi.mutateAsync(
+      createAssignsStepToServiceRequest(values),
+      {
         onSuccess: () => {
           props.onClose(true);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
index 037e099ee..77423ddf1 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditEarliestDateSidebar.tsx
@@ -57,13 +57,11 @@ function EditEarliestDateSidebar(
   async function handleSubmit(values: EditEarliestDateFormValues) {
     const patchAppointmentRequest = createPatchEarliestDateRequest(values);
 
-    await patchEarliestDate
-      .mutateAsync(patchAppointmentRequest.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await patchEarliestDate.mutateAsync(patchAppointmentRequest.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   function mapProcedureStepToEditEarliestDateValues(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
index 73ed034a0..1343de443 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/EditServiceAppointmentSidebar.tsx
@@ -72,7 +72,7 @@ function EditServiceAppointmentSidebar(
     const { appointmentStart, durationInMinutes } = determineStartAndDuration(
       values.bookingType,
       values.userDefinedAppointmentDate!,
-      values.appointmentBlockDate!,
+      values.appointmentBlockDate,
       values.appointmentTypeStandardDuration,
     );
     const request: PatchAppointmentRequest = {
@@ -92,13 +92,11 @@ function EditServiceAppointmentSidebar(
   async function handleSubmit(values: EditServiceAppointmentFormValues) {
     const usePatchAppointment = createUsePatchAppointmentRequest(values);
 
-    await patchProcedure
-      .mutateAsync(usePatchAppointment.request, {
-        onSuccess: () => {
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await patchProcedure.mutateAsync(usePatchAppointment.request, {
+      onSuccess: () => {
+        props.onClose(true);
+      },
+    });
   }
 
   function mapProcedureStepToEditAppointmentValues(
@@ -108,7 +106,7 @@ function EditServiceAppointmentSidebar(
       procedureId: props.procedureId,
       procedureStepId: procedureStep.procedureStepId ?? "",
       bookingType: "" as ApiAppointmentBookingType,
-      appointmentBlockDate: "",
+      appointmentBlockDate: undefined,
       appointmentType: procedureStep.appointmentType,
       userDefinedAppointmentDate: mapDateTimeToInput(new Date(), false),
       appointmentTypeStandardDuration: vaccinationStandardDuration as number,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
index c5c2cfe26..2d3c5ce2f 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/OtherServiceAppliedSidebar.tsx
@@ -61,17 +61,15 @@ function OtherServiceAppliedSidebar(
         mfa: values.medicalAssistant,
       },
     };
-    await updateOtherServiceApi
-      .mutateAsync(request, {
-        onSuccess: () => {
-          props.setStoredUsers({
-            physician: values.physician,
-            medicalAssistant: values.medicalAssistant ?? "",
-          });
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await updateOtherServiceApi.mutateAsync(request, {
+      onSuccess: () => {
+        props.setStoredUsers({
+          physician: values.physician,
+          medicalAssistant: values.medicalAssistant ?? "",
+        });
+        props.onClose(true);
+      },
+    });
   }
 
   function mapOtherServiceAppliedValues(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
index 2fdd25d1e..8a3a78646 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/ServiceAppliedSidebar.tsx
@@ -61,17 +61,15 @@ function ServiceAppliedSidebar(props: Readonly<ServiceAppliedSidebarProps>) {
       },
     };
 
-    await updateVaccination
-      .mutateAsync(request, {
-        onSuccess: () => {
-          props.setStoredUsers({
-            physician: values.physician,
-            medicalAssistant: values.medicalAssistant ?? "",
-          });
-          props.onClose(true);
-        },
-      })
-      .catch();
+    await updateVaccination.mutateAsync(request, {
+      onSuccess: () => {
+        props.setStoredUsers({
+          physician: values.physician,
+          medicalAssistant: values.medicalAssistant ?? "",
+        });
+        props.onClose(true);
+      },
+    });
   }
 
   function formatVaccinationInfo(
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
index 1f1913666..df5260d51 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServiceAppointmentForm.tsx
@@ -33,7 +33,7 @@ export interface AddServiceAppointmentFormValues {
   procedureId: string;
   serviceChecks?: ApiAssignableService[];
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   userDefinedAppointmentDate?: string;
   appointmentTypeStandardDuration: number;
   appointmentType?: ApiAppointmentType;
@@ -60,7 +60,7 @@ export function AddServiceAppointmentForm(
     const errors: FormikErrors<AddServiceAppointmentFormValues> = {};
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
index 4397d7b42..daba65bf7 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/EditServiceAppointmentForm.tsx
@@ -26,7 +26,7 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 export interface EditServiceAppointmentFormValues {
   procedureId: string;
   bookingType?: ApiAppointmentBookingType;
-  appointmentBlockDate?: string;
+  appointmentBlockDate?: { start: Date; end: Date };
   userDefinedAppointmentDate?: string;
   procedureStepId: string;
   appointmentType?: ApiAppointmentType;
@@ -53,7 +53,7 @@ export function EditServiceAppointmentForm(
     const errors: FormikErrors<EditServiceAppointmentFormValues> = {};
     if (
       values.bookingType === ApiAppointmentBookingType.AppointmentBlock &&
-      values.appointmentBlockDate === ""
+      values.appointmentBlockDate?.start === undefined
     ) {
       errors.appointmentBlockDate = "Bitte einen Termin auswählen";
     } else if (values.bookingType === ApiAppointmentBookingType.UserDefined) {
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
index ce7049ee9..e25199562 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/certificates/VaccinationConsultationCertificatesTable.tsx
@@ -11,7 +11,7 @@ import {
 } from "@eshg/employee-portal-api/travelMedicine";
 import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { CalendarTodayOutlined } from "@mui/icons-material";
+import { ReceiptOutlined } from "@mui/icons-material";
 import AddOutlined from "@mui/icons-material/AddOutlined";
 import { Button, Stack, Typography } from "@mui/joy";
 import { useSuspenseQueries } from "@tanstack/react-query";
@@ -133,7 +133,7 @@ export function VaccinationConsultationCertificatesTable({
           flex: 1,
         }}
       >
-        <CalendarTodayOutlined sx={{ height: "40px", width: "40px" }} />
+        <ReceiptOutlined sx={{ height: "40px", width: "40px" }} />
         <Typography sx={{ mt: 2, mb: 3 }}>
           Aktuell keine Bescheinigungen vorhanden
         </Typography>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
similarity index 95%
rename from employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
index 1aeb9702c..d72406936 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/InformationStatementSidebar.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar.tsx
@@ -61,13 +61,14 @@ function InformationStatementSidebar(
   }
 
   async function handleSubmit(values: InformationStatementFormValues) {
-    await createInformationStatements
-      .mutateAsync(createPostInformationStatementsRequest(values), {
+    await createInformationStatements.mutateAsync(
+      createPostInformationStatementsRequest(values),
+      {
         onSuccess: () => {
           props.onClose(true);
         },
-      })
-      .catch();
+      },
+    );
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
similarity index 85%
rename from employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
index 078ddc253..d1cedb570 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/InformationStatementsColumns.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsColumns.tsx
@@ -4,7 +4,7 @@
  */
 
 import { ApiInformationStatement } from "@eshg/employee-portal-api/travelMedicine";
-import { DeleteOutlined } from "@mui/icons-material";
+import { DeleteOutlined, TextSnippetOutlined } from "@mui/icons-material";
 import { ColumnHelper, createColumnHelper } from "@tanstack/react-table";
 
 import { CitizenHasAnsweredStatusChip } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/CitizenHasAnsweredChip";
@@ -20,7 +20,6 @@ export function informationStatementsColumns({
   isProcedureClosed: boolean;
   onDeleteInformationStatement: (informationStatementId: string) => void;
 }>) {
-  // todo switch back to old structure when having more than one entry in actions menu
   const columns = [
     columnHelper.accessor("title", {
       header: "Titel",
@@ -44,6 +43,13 @@ export function informationStatementsColumns({
         cell: (props) => (
           <ActionsMenu
             actionItems={[
+              {
+                label: "PDF anzeigen",
+                startDecorator: <TextSnippetOutlined />,
+                onClick: () => {
+                  //TODO implementation in PDF story
+                },
+              },
               {
                 label: "Löschen",
                 disabled: isProcedureClosed,
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx
new file mode 100644
index 000000000..89e8750fe
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementsTable.tsx
@@ -0,0 +1,130 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+"use client";
+
+import {
+  ApiProcedureStatus,
+  ApiTravelMedicineFeature,
+} from "@eshg/employee-portal-api/travelMedicine";
+import { AddOutlined, DocumentScannerOutlined } from "@mui/icons-material";
+import { Button, Stack, Typography } from "@mui/joy";
+import { useSuspenseQueries } from "@tanstack/react-query";
+
+import { useDeleteInformationStatement } from "@/lib/businessModules/travelMedicine/api/mutations/vaccinationConsultation";
+import { useIsNewFeatureEnabled } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
+import {
+  useGetAllInformationStatementsQuery,
+  useGetStatusQuery,
+} from "@/lib/businessModules/travelMedicine/api/queries/vaccinationConsultation";
+import { useInformationStatementSidebar } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/informationStatements/InformationStatementSidebar";
+import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
+import { DataTable } from "@/lib/shared/components/table/DataTable";
+import { TablePage } from "@/lib/shared/components/table/TablePage";
+import { TableSheet } from "@/lib/shared/components/table/TableSheet";
+
+import { informationStatementsColumns } from "./InformationStatementsColumns";
+
+export function InformationStatementsTable({
+  procedureId,
+}: Readonly<{
+  procedureId: string;
+}>) {
+  const deleteInformationStatementApi = useDeleteInformationStatement();
+  const isInformationStatementEnabled = useIsNewFeatureEnabled(
+    ApiTravelMedicineFeature.CitizenPortalInformationStatement,
+  );
+
+  const [{ data: allInformationStatements }, { data: status }] =
+    useSuspenseQueries({
+      queries: [
+        useGetAllInformationStatementsQuery(procedureId),
+        useGetStatusQuery(procedureId),
+      ],
+    });
+
+  const informationStatementSidebar = useInformationStatementSidebar();
+
+  const isProcedureClosed = status === ApiProcedureStatus.Closed;
+
+  function deleteInformationStatement(informationStatementId: string) {
+    return deleteInformationStatementApi.mutate({
+      procedureId,
+      informationStatementId,
+    });
+  }
+
+  return (
+    <TablePage
+      data-testid="vc-information-statements"
+      fullHeight
+      controls={
+        !isProcedureClosed &&
+        isInformationStatementEnabled && (
+          <ButtonBar
+            right={
+              <Button
+                sx={{ py: 1 / 2 }}
+                startDecorator={<AddOutlined />}
+                onClick={() =>
+                  informationStatementSidebar.open({
+                    procedureId: procedureId,
+                  })
+                }
+                data-testid="add-information-statement"
+                disabled={isProcedureClosed}
+              >
+                Bogen hinzufügen
+              </Button>
+            }
+          />
+        )
+      }
+    >
+      <TableSheet>
+        <DataTable
+          data={allInformationStatements.informationStatements}
+          columns={informationStatementsColumns({
+            isProcedureClosed,
+            onDeleteInformationStatement: (informationStatementId: string) =>
+              deleteInformationStatement(informationStatementId),
+          })}
+          noDataComponent={() => <NoInformationStatementsAvailable />}
+        />
+      </TableSheet>
+    </TablePage>
+  );
+
+  function NoInformationStatementsAvailable() {
+    return (
+      <Stack
+        sx={{
+          alignItems: "center",
+          justifyContent: "center",
+          flex: 1,
+        }}
+      >
+        <DocumentScannerOutlined sx={{ height: "40px", width: "40px" }} />
+        <Typography sx={{ mt: 2, mb: 3 }}>
+          Aktuell keine Aufklärungsbögen vorhanden
+        </Typography>
+        {!isProcedureClosed ? (
+          <Button
+            sx={{ py: 1 / 2 }}
+            startDecorator={<AddOutlined />}
+            onClick={() =>
+              informationStatementSidebar.open({
+                procedureId: procedureId,
+              })
+            }
+            data-testid="add-information-statement-empty-table"
+          >
+            Bogen hinzufügen
+          </Button>
+        ) : null}
+      </Stack>
+    );
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx
new file mode 100644
index 000000000..96281d8e1
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement.tsx
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiDocumentConfirmation } from "@eshg/employee-portal-api/travelMedicine";
+import { SetStateAction } from "react";
+
+import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField";
+
+interface ConfirmationElementProps {
+  sectionIndex: number;
+  elementIndex: number;
+  confirmation: ApiDocumentConfirmation;
+  readOnly: boolean;
+  // eslint-disable-next-line  @typescript-eslint/no-explicit-any
+  setFieldValue: (field: string, value: SetStateAction<any>) => void;
+}
+
+export function ConfirmationElement({
+  sectionIndex,
+  elementIndex,
+  confirmation,
+  readOnly,
+  setFieldValue,
+}: Readonly<ConfirmationElementProps>) {
+  const name = `medicalHistoryContent.sections[${sectionIndex}].sectionElements[${elementIndex}].confirmation.answer`;
+
+  return (
+    <CheckboxField
+      label={confirmation.confirmationTextField}
+      name={name}
+      onChange={(event) => setFieldValue(name, event.target.checked)}
+      disabled={readOnly}
+    />
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
index ef19a4649..7cdc95850 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistory.tsx
@@ -8,7 +8,7 @@
 import { ApiMedicalHistory } from "@eshg/employee-portal-api/travelMedicine";
 import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus";
 import { validateLength } from "@eshg/lib-portal/helpers/validators";
-import { Stack } from "@mui/joy";
+import { Box, Stack } from "@mui/joy";
 import { Formik } from "formik";
 import { useEffect } from "react";
 
@@ -16,6 +16,7 @@ import {
   PatchMedicalHistoryRequest,
   usePatchMedicalHistory,
 } from "@/lib/businessModules/travelMedicine/api/mutations/medicalHistory";
+import { ConfirmationElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/ConfirmationElement";
 import { MedicalHistoryMultiSelectElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement";
 import { MedicalHistoryRadioButtonElement } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement";
 import { MedicalHistorySection } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistorySection";
@@ -40,7 +41,7 @@ export function MedicalHistory({
   const patchMedicalHistory = usePatchMedicalHistory();
 
   async function sendUpdateRequest(request: PatchMedicalHistoryRequest) {
-    await patchMedicalHistory.mutateAsync(request).catch();
+    await patchMedicalHistory.mutateAsync(request);
   }
 
   async function handleSubmit(changedContent: ApiMedicalHistory) {
@@ -91,71 +92,94 @@ export function MedicalHistory({
                           dataTestId={"document-element-" + elementIndex}
                         >
                           <>
-                            <MedicalHistoryRadioButtonElement
-                              name={
-                                "medicalHistoryContent.sections[" +
-                                sectionIndex +
-                                "].sectionElements[" +
-                                elementIndex +
-                                "].elementData.answer"
-                              }
-                              label={element.elementData.questionText}
-                              setFieldValue={setFieldValue}
-                              element={element}
-                              elementIndex={elementIndex}
-                              sectionIndex={sectionIndex}
-                              readOnly={readOnly}
-                            ></MedicalHistoryRadioButtonElement>
-                            {(
-                              getFieldProps(
-                                "medicalHistoryContent.sections[" +
-                                  sectionIndex +
-                                  "].sectionElements[" +
-                                  elementIndex +
-                                  "].elementData.answer",
-                              ).value as string
-                            )?.toString() === "true" && (
+                            {element.anamnesisQuestion && (
                               <>
-                                {element.elementData.subElementMultiSelect
-                                  .length > 0 && (
-                                  <MedicalHistoryMultiSelectElement
-                                    element={element}
-                                    elementIndex={elementIndex}
-                                    sectionIndex={sectionIndex}
-                                    name={
-                                      "medicalHistoryContent.sections[" +
+                                <MedicalHistoryRadioButtonElement
+                                  name={
+                                    "medicalHistoryContent.sections[" +
+                                    sectionIndex +
+                                    "].sectionElements[" +
+                                    elementIndex +
+                                    "].anamnesisQuestion.answer"
+                                  }
+                                  label={element.anamnesisQuestion.questionText}
+                                  setFieldValue={setFieldValue}
+                                  element={element}
+                                  elementIndex={elementIndex}
+                                  sectionIndex={sectionIndex}
+                                  readOnly={readOnly}
+                                ></MedicalHistoryRadioButtonElement>
+
+                                {(
+                                  getFieldProps(
+                                    "medicalHistoryContent.sections[" +
                                       sectionIndex +
                                       "].sectionElements[" +
                                       elementIndex +
-                                      "].elementData.subElementMultiSelect"
-                                    }
-                                    readOnly={readOnly}
-                                  />
-                                )}
-                                {element.elementData.subElementText && (
-                                  <Stack
-                                    style={{
-                                      marginLeft: 16,
-                                    }}
-                                  >
-                                    <MedicalHistoryTextareaElement
-                                      name={
-                                        "medicalHistoryContent.sections[" +
-                                        sectionIndex +
-                                        "].sectionElements[" +
-                                        elementIndex +
-                                        "].elementData.subElementText.answer"
-                                      }
-                                      label={
-                                        element.elementData.subElementText
-                                          .questionText
-                                      }
-                                      readOnly={readOnly}
-                                    ></MedicalHistoryTextareaElement>
-                                  </Stack>
+                                      "].anamnesisQuestion.answer",
+                                  ).value as string
+                                )?.toString() === "true" && (
+                                  <>
+                                    {element.anamnesisQuestion
+                                      .subElementMultiSelect.length > 0 && (
+                                      <MedicalHistoryMultiSelectElement
+                                        element={element}
+                                        elementIndex={elementIndex}
+                                        sectionIndex={sectionIndex}
+                                        name={
+                                          "medicalHistoryContent.sections[" +
+                                          sectionIndex +
+                                          "].sectionElements[" +
+                                          elementIndex +
+                                          "].anamnesisQuestion.subElementMultiSelect"
+                                        }
+                                        readOnly={readOnly}
+                                      />
+                                    )}
+
+                                    {element.anamnesisQuestion
+                                      .subElementText && (
+                                      <Stack
+                                        style={{
+                                          marginLeft: 16,
+                                        }}
+                                      >
+                                        <MedicalHistoryTextareaElement
+                                          name={
+                                            "medicalHistoryContent.sections[" +
+                                            sectionIndex +
+                                            "].sectionElements[" +
+                                            elementIndex +
+                                            "].anamnesisQuestion.subElementText.answer"
+                                          }
+                                          label={
+                                            element.anamnesisQuestion
+                                              .subElementText.questionText
+                                          }
+                                          readOnly={readOnly}
+                                        ></MedicalHistoryTextareaElement>
+                                      </Stack>
+                                    )}
+                                  </>
                                 )}
                               </>
                             )}
+
+                            {element.textBlock && (
+                              <Box sx={{ whiteSpace: "pre-wrap" }}>
+                                {element.textBlock.textField}
+                              </Box>
+                            )}
+
+                            {element.confirmation && (
+                              <ConfirmationElement
+                                confirmation={element.confirmation}
+                                elementIndex={elementIndex}
+                                sectionIndex={sectionIndex}
+                                readOnly={readOnly}
+                                setFieldValue={setFieldValue}
+                              />
+                            )}
                           </>
                         </MedicalHistorySectionElement>
                       ))}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
index 7fc225621..2f6a3b304 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryMultiSelectElement.tsx
@@ -4,8 +4,8 @@
  */
 
 import {
-  ApiMedicalHistorySectionElement,
-  ApiMedicalHistorySubElementMultiSelect,
+  ApiDocumentSectionElement,
+  ApiDocumentSubElementMultiSelect,
 } from "@eshg/employee-portal-api/travelMedicine";
 import {
   BaseFieldProps,
@@ -16,7 +16,7 @@ import { ChangeEvent } from "react";
 
 interface MedicalHistoryMultiSelectElementProps
   extends Omit<BaseFieldProps, "required" | "children"> {
-  element: ApiMedicalHistorySectionElement;
+  element: ApiDocumentSectionElement;
   sectionIndex: number;
   elementIndex: number;
   name: string;
@@ -30,9 +30,9 @@ export function MedicalHistoryMultiSelectElement({
   readOnly = false,
   ...restProps
 }: Readonly<MedicalHistoryMultiSelectElementProps>) {
-  const { input, helpers } = useBaseField<
-    ApiMedicalHistorySubElementMultiSelect[]
-  >({ ...restProps });
+  const { input, helpers } = useBaseField<ApiDocumentSubElementMultiSelect[]>({
+    ...restProps,
+  });
 
   async function handleCheckboxChange(
     event: ChangeEvent<HTMLInputElement>,
@@ -72,10 +72,10 @@ export function MedicalHistoryMultiSelectElement({
         sx={{ rowGap: 1, marginBlock: "0.5rem" }}
       >
         {(readOnly
-          ? element.elementData.subElementMultiSelect.filter(
+          ? element.anamnesisQuestion!.subElementMultiSelect.filter(
               (element) => element.answer,
             )
-          : element.elementData.subElementMultiSelect
+          : element.anamnesisQuestion!.subElementMultiSelect
         ).map(({ questionText }, index) => (
           <ListItem
             key={"multiselect" + elementIndex + "-" + index}
@@ -92,7 +92,7 @@ export function MedicalHistoryMultiSelectElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 index +
                 "].answer"
               }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
index 7d2c37e85..2e5f93208 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/medicalHistory/MedicalHistoryRadioButtonElement.tsx
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { ApiMedicalHistorySectionElement } from "@eshg/employee-portal-api/travelMedicine";
+import { ApiDocumentSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 import { SetFieldValueHelper } from "@eshg/lib-portal/types/form";
 import { FormLabel, styled } from "@mui/joy";
 
@@ -13,7 +13,7 @@ interface MedicalHistoryRadioButtonElementProps {
   name: string;
   label: string;
   setFieldValue: SetFieldValueHelper;
-  element: ApiMedicalHistorySectionElement;
+  element: ApiDocumentSectionElement;
   sectionIndex: number;
   elementIndex: number;
   readOnly?: boolean;
@@ -42,19 +42,19 @@ export function MedicalHistoryRadioButtonElement({
       ]}
       onChange={async (event) => {
         if (event.target.value === "false" || !event.target.value) {
-          if (element.elementData.subElementText) {
+          if (element.anamnesisQuestion!.subElementText) {
             await setFieldValue(
               "medicalHistoryContent.sections[" +
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementText.answer",
+                "].anamnesisQuestion.subElementText.answer",
               "",
             );
           }
           for (
             let i = 0;
-            i < element.elementData.subElementMultiSelect.length;
+            i < element.anamnesisQuestion!.subElementMultiSelect.length;
             i++
           ) {
             await setFieldValue(
@@ -62,7 +62,7 @@ export function MedicalHistoryRadioButtonElement({
                 sectionIndex +
                 "].sectionElements[" +
                 elementIndex +
-                "].elementData.subElementMultiSelect[" +
+                "].anamnesisQuestion.subElementMultiSelect[" +
                 i +
                 "].answer",
               false,
@@ -73,7 +73,7 @@ export function MedicalHistoryRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             false,
           );
         } else {
@@ -82,7 +82,7 @@ export function MedicalHistoryRadioButtonElement({
               sectionIndex +
               "].sectionElements[" +
               elementIndex +
-              "].elementData.answer",
+              "].anamnesisQuestion.answer",
             true,
           );
         }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
index e3cf6685b..c916a2c67 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/new/NewPerson.tsx
@@ -37,16 +37,14 @@ export function NewPerson() {
       data as InitialAppointmentFormValuesProps,
     );
 
-    await saveVaccinationConsultation
-      .mutateAsync(request, {
-        onSuccess: (response) => {
-          if (response) {
-            router.push(routes.procedures.baseData(response));
-          }
-          handleClose();
-        },
-      })
-      .catch();
+    await saveVaccinationConsultation.mutateAsync(request, {
+      onSuccess: (response) => {
+        if (response) {
+          router.push(routes.procedures.baseData(response));
+        }
+        handleClose();
+      },
+    });
   }
 
   return (
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
index d8c94d284..9b7176856 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/AppointmentRadioGroup.tsx
@@ -8,18 +8,20 @@ import {
   ApiAppointmentType,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions";
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
+import {
+  AppointmentPickerField,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
 import { Stack, Typography } from "@mui/joy";
 import { useField } from "formik";
+import { useState } from "react";
 import { isEmpty } from "remeda";
 
 import { Appointment } from "@/lib/businessModules/travelMedicine/api/models/Appointment";
 import { SelectableCard } from "@/lib/shared/components/cards/SelectableCard";
 import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField";
 import { RadioGroupField } from "@/lib/shared/components/formFields/RadioGroupField";
-import { durationBetweenDatesInMinutes } from "@/lib/shared/helpers/dateTime";
 
 interface AppointmentRadioGroupProps {
   type?: ApiAppointmentType;
@@ -37,37 +39,8 @@ export function AppointmentRadioGroup({
   freeVaccinationBlockAppointments,
   ...props
 }: Readonly<AppointmentRadioGroupProps>) {
-  function createAppointmentOptions(availableBlockAppointments: Appointment[]) {
-    if (availableBlockAppointments) {
-      let needToAddOption = true;
-      const labelOptions: SelectOption[] = availableBlockAppointments.map(
-        (blockAppointment) => {
-          const label = formatDateTime(blockAppointment.start) + " Uhr";
-          if (label == props.appointmentBlockDateOption?.label) {
-            needToAddOption = false;
-          }
-          return {
-            label: label,
-            value:
-              blockAppointment.start.toISOString() +
-              "," +
-              durationBetweenDatesInMinutes(
-                blockAppointment.start,
-                blockAppointment.end,
-              ),
-          };
-        },
-      );
-      if (props.appointmentBlockDateOption && needToAddOption) {
-        labelOptions.push(props.appointmentBlockDateOption);
-      }
-
-      return labelOptions;
-    } else {
-      return [];
-    }
-  }
   const [bookingTypeFieldProps] = useField("bookingType");
+  const [currentMonth, setCurrentMonth] = useState(new Date());
 
   return (
     <Stack gap={2}>
@@ -84,27 +57,26 @@ export function AppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName="bookingType"
         >
-          {props.type == ApiAppointmentType.Consultation ? (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeConsultationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
-            />
-          ) : (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeVaccinationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
+          <Stack gap={2}>
+            <Typography level={"body-sm"} sx={{ fontWeight: "500" }}>
+              Aus Terminblock
+            </Typography>
+            <AppointmentPickerField
+              name={"appointmentBlockDate"}
+              currentMonth={currentMonth}
+              setCurrentMonth={setCurrentMonth}
+              monthAppointments={
+                props.type == ApiAppointmentType.Consultation
+                  ? freeConsultationBlockAppointments
+                  : freeVaccinationBlockAppointments
+              }
+              required={bookingTypeFieldProps.value === "AppointmentBlock"}
+              labels={FIELD_LABELS_DE}
             />
-          )}
+          </Stack>
         </SelectableCard>
         <SelectableCard
           key={ApiAppointmentBookingType.UserDefined}
@@ -112,6 +84,7 @@ export function AppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName={"bookingType"}
         >
           <Stack gap={2} sx={{ flexGrow: 1 }}>
@@ -143,6 +116,7 @@ export function AppointmentRadioGroup({
             sx={{ mb: 2 }}
             radioProps={{ overlay: false }}
             allowDeselection={!required}
+            changeBackgroundColor={false}
             forGroupName={"bookingType"}
           >
             <Stack gap={2} sx={{ flexGrow: 1 }}>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
index b1e5aa26c..c6052c11d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/LegacyAppointmentRadioGroup.tsx
@@ -8,19 +8,20 @@ import {
   ApiAppointmentType,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField";
-import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField";
 import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions";
-import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
+import {
+  AppointmentPickerField,
+  FIELD_LABELS_DE,
+} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField";
 import { Stack, Typography } from "@mui/joy";
 import { useField } from "formik";
+import { useState } from "react";
 import { isEmpty } from "remeda";
 
-import { Appointment } from "@/lib/businessModules/travelMedicine/api/models/Appointment";
 import { useGetFreeAppointmentsUnsuspended } from "@/lib/businessModules/travelMedicine/api/queries/appointmentBlocks";
 import { SelectableCard } from "@/lib/shared/components/cards/SelectableCard";
 import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField";
 import { RadioGroupField } from "@/lib/shared/components/formFields/RadioGroupField";
-import { durationBetweenDatesInMinutes } from "@/lib/shared/helpers/dateTime";
 
 interface AppointmentRadioGroupProps {
   type?: ApiAppointmentType;
@@ -42,37 +43,8 @@ export function LegacyAppointmentRadioGroup({
   const freeVaccinationBlockAppointments =
     getAllFreeVaccinationBlockAppointments.data ?? [];
 
-  function createAppointmentOptions(availableBlockAppointments: Appointment[]) {
-    if (availableBlockAppointments) {
-      let needToAddOption = true;
-      const labelOptions: SelectOption[] = availableBlockAppointments.map(
-        (blockAppointment) => {
-          const label = formatDateTime(blockAppointment.start) + " Uhr";
-          if (label == props.appointmentBlockDateOption?.label) {
-            needToAddOption = false;
-          }
-          return {
-            label: label,
-            value:
-              blockAppointment.start.toISOString() +
-              "," +
-              durationBetweenDatesInMinutes(
-                blockAppointment.start,
-                blockAppointment.end,
-              ),
-          };
-        },
-      );
-      if (props.appointmentBlockDateOption && needToAddOption) {
-        labelOptions.push(props.appointmentBlockDateOption);
-      }
-
-      return labelOptions;
-    } else {
-      return [];
-    }
-  }
   const [bookingTypeFieldProps] = useField("bookingType");
+  const [currentMonth, setCurrentMonth] = useState(new Date());
 
   return (
     <Stack gap={2}>
@@ -89,27 +61,26 @@ export function LegacyAppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName="bookingType"
         >
-          {props.type == ApiAppointmentType.Consultation ? (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeConsultationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
-            />
-          ) : (
-            <SelectField
-              label="Termin aus Terminblock"
-              name="appointmentBlockDate"
-              options={createAppointmentOptions(
-                freeVaccinationBlockAppointments,
-              )}
-              sx={{ flexGrow: 1 }}
+          <Stack gap={2}>
+            <Typography level={"body-sm"} sx={{ fontWeight: "500" }}>
+              Aus Terminblock
+            </Typography>
+            <AppointmentPickerField
+              name={"appointmentBlockDate"}
+              currentMonth={currentMonth}
+              setCurrentMonth={setCurrentMonth}
+              monthAppointments={
+                props.type == ApiAppointmentType.Consultation
+                  ? freeConsultationBlockAppointments
+                  : freeVaccinationBlockAppointments
+              }
+              required={bookingTypeFieldProps.value === "AppointmentBlock"}
+              labels={FIELD_LABELS_DE}
             />
-          )}
+          </Stack>
         </SelectableCard>
         <SelectableCard
           key={ApiAppointmentBookingType.UserDefined}
@@ -117,6 +88,7 @@ export function LegacyAppointmentRadioGroup({
           sx={{ mb: 2 }}
           radioProps={{ overlay: false }}
           allowDeselection={!required}
+          changeBackgroundColor={false}
           forGroupName={"bookingType"}
         >
           <Stack gap={2} sx={{ flexGrow: 1 }}>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
index a71ead2d2..2cdc4e3f3 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers.ts
@@ -17,6 +17,7 @@ import {
   formatDate,
   formatDateTime,
 } from "@eshg/lib-portal/formatters/dateTime";
+import { durationBetweenDatesInMinutes } from "@eshg/lib-portal/helpers/dateTime";
 import { isEmpty, isNonNullish } from "remeda";
 
 import { AppointmentSummary } from "@/lib/businessModules/travelMedicine/api/models/AppointmentSummary";
@@ -198,7 +199,7 @@ export function createMedicalAssistantOptions(
 export function determineStartAndDuration(
   bookingType: ApiAppointmentBookingType | undefined,
   userDefinedAppointmentDate: string,
-  appointmentBlockDate: string,
+  appointmentBlockDate: { start: Date; end: Date } | undefined,
   appointmentTypeStandardDuration: number,
 ): { appointmentStart: Date; durationInMinutes: number } {
   let appointmentStart;
@@ -207,9 +208,13 @@ export function determineStartAndDuration(
     appointmentStart = new Date(userDefinedAppointmentDate);
     durationInMinutes = appointmentTypeStandardDuration;
   } else {
-    const split = appointmentBlockDate.split(",");
-    appointmentStart = new Date(split.at(0)!);
-    durationInMinutes = Number.parseInt(split.at(1)!);
+    appointmentStart = appointmentBlockDate?.start ?? new Date();
+    durationInMinutes = appointmentBlockDate
+      ? durationBetweenDatesInMinutes(
+          appointmentBlockDate.start,
+          appointmentBlockDate.end,
+        )
+      : appointmentTypeStandardDuration;
   }
   return { appointmentStart, durationInMinutes };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts b/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
index f4951c8cb..cbf8090d5 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/routes.ts
@@ -37,6 +37,8 @@ export const routes = {
       procedureStepId
         ? `${proceduresPath}/${procedureId}/medical-histories?medical-history=${procedureStepId}`
         : `${proceduresPath}/${procedureId}/medical-histories`,
+    informationStatements: (procedureId: string) =>
+      `${proceduresPath}/${procedureId}/information-statements`,
     certificates: (procedureId: string) =>
       `${proceduresPath}/${procedureId}/certificates`,
     downloadFile: (fileId: string) =>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
index acc281710..c21cd021d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/sideNavigationItem.tsx
@@ -9,17 +9,18 @@ import { Vaccines } from "@mui/icons-material";
 import { isPlainObject } from "remeda";
 
 import { useIsNewFeatureEnabled } from "@/lib/baseModule/api/queries/feature";
-import { SideNavigationItem } from "@/lib/baseModule/components/layout/sideNavigation/types";
+import { UseSideNavigationItemsResult } from "@/lib/baseModule/components/layout/sideNavigation/types";
 import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/travelMedicine/api/queries/featureToggles";
 import { hasUserRole } from "@/lib/shared/helpers/accessControl";
 
 import { routes } from "./routes";
 
-export function useSideNavigationItems(): SideNavigationItem[] {
+export function useSideNavigationItems(): UseSideNavigationItemsResult {
   // our toggles
   const {
     data: informationStatementEnabled,
     isError: isErrorCitizenPortalInformationStatement,
+    isLoading: isCitizenPortalInformationStatementLoading,
   } = useIsNewFeatureEnabledUnsuspended(
     ApiTravelMedicineFeature.CitizenPortalInformationStatement,
   );
@@ -29,65 +30,68 @@ export function useSideNavigationItems(): SideNavigationItem[] {
   // their toggles
   const isInboxEnabled = useIsNewFeatureEnabled(ApiBaseFeature.Inbox);
 
-  return [
-    {
-      name: "Impfberatung",
-      decorator: <Vaccines />,
-      error: isTravelMedicineError
-        ? "Bei der Verbindung zum Modul Impfberatung ist ein Fehler aufgetreten."
-        : undefined,
-      subItems: [
-        {
-          name: "Vorgänge",
-          href: routes.procedures.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Vorgangssuche",
-          href: routes.proceduresSearch.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Terminblöcke",
-          href: routes.appointmentBlockGroups.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Terminarten",
-          href: routes.appointmentTypes.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Anamnese",
-          href: routes.medicalHistoryTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        informationStatementEnabled && {
-          name: "Aufklärungsbögen",
-          href: routes.informationStatementTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Krankheiten",
-          href: routes.diseases.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Impfstoffe",
-          href: routes.vaccines.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        {
-          name: "Sonstige Leistungen",
-          href: routes.otherServiceTemplates.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-        isInboxEnabled && {
-          name: "Posteingang",
-          href: routes.inbox.index,
-          accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
-        },
-      ].filter(isPlainObject),
-    },
-  ];
+  return {
+    isLoading: isCitizenPortalInformationStatementLoading,
+    items: [
+      {
+        name: "Impfberatung",
+        decorator: <Vaccines />,
+        error: isTravelMedicineError
+          ? "Bei der Verbindung zum Modul Impfberatung ist ein Fehler aufgetreten."
+          : undefined,
+        subItems: [
+          {
+            name: "Vorgänge",
+            href: routes.procedures.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Vorgangssuche",
+            href: routes.proceduresSearch.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Terminblöcke",
+            href: routes.appointmentBlockGroups.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Terminarten",
+            href: routes.appointmentTypes.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Anamnese",
+            href: routes.medicalHistoryTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          informationStatementEnabled && {
+            name: "Aufklärungsbögen",
+            href: routes.informationStatementTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Krankheiten",
+            href: routes.diseases.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Impfstoffe",
+            href: routes.vaccines.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          {
+            name: "Sonstige Leistungen",
+            href: routes.otherServiceTemplates.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+          isInboxEnabled && {
+            name: "Posteingang",
+            href: routes.inbox.index,
+            accessCheck: hasUserRole(ApiUserRole.TravelMedicineAdmin),
+          },
+        ].filter(isPlainObject),
+      },
+    ],
+  };
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx
new file mode 100644
index 000000000..b94ec9979
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar.tsx
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Add, ContentPaste, Gesture, Subject } from "@mui/icons-material";
+import Button from "@mui/joy/Button";
+import Stack from "@mui/joy/Stack";
+
+export interface SectionButtonBarProps {
+  textBlockButtonAction: () => void;
+  anamnesisButtonAction: () => void;
+  confirmationButtonAction: () => void;
+}
+
+export function SectionButtonBar(props: Readonly<SectionButtonBarProps>) {
+  return (
+    <Stack direction="row" spacing={2}>
+      <Button
+        data-testid="section-add-textblock-button"
+        startDecorator={<Subject />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.textBlockButtonAction}
+      >
+        Textblock
+      </Button>
+
+      <Button
+        data-testid="section-add-question-button"
+        startDecorator={<ContentPaste />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.anamnesisButtonAction}
+      >
+        Anamnesefrage
+      </Button>
+
+      <Button
+        data-testid="section-add-confirmation-button"
+        startDecorator={<Gesture />}
+        endDecorator={<Add />}
+        variant="outlined"
+        color="primary"
+        onClick={props.confirmationButtonAction}
+      >
+        Bestätigungsfeld
+      </Button>
+    </Stack>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
index 80e954e08..001b07370 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionTitle.tsx
@@ -21,7 +21,7 @@ export function SectionTitle({
         name={`${sectionFormikPath}.sectionTitle`}
         placeholder="Sektionstitel eingeben"
         sx={{ flex: 1 }}
-        data-testid="sectionTitle"
+        data-testid="section-title"
       />
       <Stack alignItems="center" paddingTop={"6px"}>
         <IconButton
@@ -30,7 +30,7 @@ export function SectionTitle({
           variant="outlined"
           onClick={sectionDeleteHandler}
           title="Sektion Löschen"
-          data-testid="deleteSection"
+          data-testid="section-delete-button"
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
index d8d6baf50..4eabb4880 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSection.tsx
@@ -5,14 +5,31 @@
 
 import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
-import { Add } from "@mui/icons-material";
-import { Button, Sheet, Stack } from "@mui/joy";
+import { Sheet, Stack } from "@mui/joy";
 import { FieldArray } from "formik";
 import { ReactNode } from "react";
 
-import { createEmptySectionElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList";
+import { SectionButtonBar } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/SectionButtonBar";
 import { SectionDataElementList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList";
 
+export function createEmptyAnamnesisQuestionElement(): ApiTemplateSectionElement {
+  return {
+    anamnesisQuestion: {
+      questionText: "",
+      subElementMultiSelect: [],
+      subElementText: undefined,
+    },
+  };
+}
+
+export function createEmptyTextBlockElement(): ApiTemplateSectionElement {
+  return { textBlock: { textField: "" } };
+}
+
+export function createEmptyConfirmationElement(): ApiTemplateSectionElement {
+  return { confirmation: { confirmationTextField: "" } };
+}
+
 export interface MedicalHistoryTemplateSectionProp {
   sectionFormikPath: string;
   sectionElements: ApiTemplateSectionElement[];
@@ -69,15 +86,17 @@ export function TemplateSection({
                 }
                 replaceSectionElementHandler={replace}
               />
-
-              <Button
-                startDecorator={<Add />}
-                variant="outlined"
-                color="primary"
-                onClick={() => push(createEmptySectionElement())}
-              >
-                Frage hinzufügen
-              </Button>
+              <SectionButtonBar
+                textBlockButtonAction={() =>
+                  push(createEmptyTextBlockElement())
+                }
+                anamnesisButtonAction={() =>
+                  push(createEmptyAnamnesisQuestionElement())
+                }
+                confirmationButtonAction={() =>
+                  push(createEmptyConfirmationElement())
+                }
+              />
             </Stack>
           </Stack>
         )}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
index 582d56bc4..260b20775 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/TemplateSectionList.tsx
@@ -3,10 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import {
-  ApiTemplateSection,
-  ApiTemplateSectionElement,
-} from "@eshg/employee-portal-api/travelMedicine";
+import { ApiTemplateSection } from "@eshg/employee-portal-api/travelMedicine";
 import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider";
 import { CreateNewFolder } from "@mui/icons-material";
 import { Button } from "@mui/joy";
@@ -18,27 +15,11 @@ import { TemplateSection } from "@/lib/businessModules/travelMedicine/shared/tem
 export function createEmptySection() {
   const section: ApiTemplateSection = {
     sectionTitle: "",
-    sectionElements: [createEmptySectionElement()],
+    sectionElements: [],
   };
   return section;
 }
 
-export function createEmptySectionElement() {
-  const sectionElement: ApiTemplateSectionElement = {
-    elementData: createEmptySectionElementData(),
-    elementType: "option",
-  };
-  return sectionElement;
-}
-
-export function createEmptySectionElementData() {
-  return {
-    questionText: "",
-    subElementMultiSelect: [],
-    subElementText: undefined,
-  };
-}
-
 export interface MedicalHistoryTemplateSectionListProp {
   sections: ApiTemplateSection[];
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx
new file mode 100644
index 000000000..236f58a13
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox.tsx
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { RequiresChildren } from "@eshg/lib-portal/types/react";
+import { Box } from "@mui/joy";
+
+interface DataElementBoxProps extends RequiresChildren {
+  "data-testid"?: string;
+}
+
+export function DataElementBox(props: Readonly<DataElementBoxProps>) {
+  return (
+    <Box
+      boxShadow="sm"
+      border="1px solid var(--neutral-outlined-border, #CDD7E1)"
+      borderRadius={12}
+      component="section"
+      flex={1}
+      style={{
+        padding: 12,
+        background: "var(--background-level-1, #F0F4F8)",
+      }}
+      data-testid={props["data-testid"]}
+    >
+      {props.children}
+    </Box>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx
new file mode 100644
index 000000000..e6d8c699b
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading.tsx
@@ -0,0 +1,15 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { RequiresChildren } from "@eshg/lib-portal/types/react";
+import { Typography } from "@mui/joy";
+
+export function DataElementHeading(props: Readonly<RequiresChildren>) {
+  return (
+    <Typography sx={{ fontSize: 14, fontWeight: "bold", marginBottom: 2 }}>
+      {props.children}
+    </Typography>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
index c76da3826..b7007e57c 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList.tsx
@@ -5,13 +5,9 @@
 
 import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine";
 
-import { MainQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion";
-import {
-  SectionDataElement,
-  createEmptySubTextElement,
-} from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement";
+import { SectionElementComponentFactory } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory";
 
-export interface MedicalHistoryTemplateSectionElementProp {
+export interface TemplateSectionElementProp {
   sectionElementsFormikPath: string;
   sectionElements: ApiTemplateSectionElement[];
   sectionElementDeleteHandler: (index: number) => void;
@@ -21,54 +17,11 @@ export interface MedicalHistoryTemplateSectionElementProp {
   ) => void;
 }
 
-export function SectionDataElementList({
-  sectionElementsFormikPath,
-  sectionElements,
-  sectionElementDeleteHandler,
-  replaceSectionElementHandler,
-}: Readonly<MedicalHistoryTemplateSectionElementProp>) {
-  function getElementDataFormikPath(index: number) {
-    return `${sectionElementsFormikPath}[${index}].elementData`;
-  }
+export function SectionDataElementList(
+  props: Readonly<TemplateSectionElementProp>,
+) {
+  const factory = new SectionElementComponentFactory(props);
+  const mappedSectionElements = factory.createSectionElementComponents();
 
-  function addSectionElementSubText(index: number) {
-    const selectedSectionElement = sectionElements[index];
-
-    if (selectedSectionElement) {
-      selectedSectionElement.elementData.subElementText =
-        createEmptySubTextElement();
-      replaceSectionElementHandler(index, selectedSectionElement);
-    }
-  }
-
-  function removeSectionElementSubText(index: number) {
-    const selectedSectionElement = sectionElements[index];
-
-    if (selectedSectionElement) {
-      selectedSectionElement.elementData.subElementText = undefined;
-      replaceSectionElementHandler(index, selectedSectionElement);
-    }
-  }
-
-  return (
-    <>
-      {sectionElements.map((sectionElement, index) => (
-        <SectionDataElement
-          elementDataFormikPath={getElementDataFormikPath(index)}
-          sectionElementData={sectionElement.elementData}
-          addSubElementHandler={() => addSectionElementSubText(index)}
-          removeSubQuestionHandler={() => removeSectionElementSubText(index)}
-          mainQuestion={
-            <MainQuestion
-              elementDataFormikPath={getElementDataFormikPath(index)}
-              sectionElementDeleteHandler={() =>
-                sectionElementDeleteHandler(index)
-              }
-            />
-          }
-          key={index}
-        />
-      ))}
-    </>
-  );
+  return <>{mappedSectionElements}</>;
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx
new file mode 100644
index 000000000..ff1ac1290
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionElementComponentFactory.tsx
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ApiTemplateSectionElement } from "@eshg/employee-portal-api/travelMedicine/models";
+
+import { TemplateSectionElementProp } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElementList";
+import { TemplateConfirmation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation";
+import { TemplateTextBlock } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock";
+import {
+  AnamnesisQuestion,
+  createEmptySubTextElement,
+} from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion";
+import { MainQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion";
+
+export class SectionElementComponentFactory {
+  private sectionElementsFormikPath;
+
+  public constructor(private sectionProps: TemplateSectionElementProp) {
+    this.sectionElementsFormikPath = sectionProps.sectionElementsFormikPath;
+  }
+
+  public createSectionElementComponents() {
+    return this.sectionProps.sectionElements.map((element, index) => {
+      if (element.anamnesisQuestion) {
+        return this.createAnamnesisComponent(index, element);
+      } else if (element.textBlock) {
+        return this.createTextBlockComponent(index);
+      } else if (element.confirmation) {
+        return this.createTemplateConfirmationComponent(index);
+      }
+      throw new Error(
+        "Can't create section element component due to faulty ApiTemplateSectionElement value",
+      );
+    });
+  }
+
+  private createTextBlockComponent(index: number) {
+    return (
+      <TemplateTextBlock
+        sectionElementFormikPath={this.getTextBlockFormikPath(index)}
+        sectionElementDeleteHandler={() =>
+          this.sectionProps.sectionElementDeleteHandler(index)
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getFormikArrayPath(index: number) {
+    return `${this.sectionElementsFormikPath}[${index}]`;
+  }
+
+  private getTextBlockFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.textBlock`;
+  }
+
+  private createTemplateConfirmationComponent(index: number) {
+    return (
+      <TemplateConfirmation
+        sectionElementFormikPath={this.getTemplateConfirmationFormikPath(index)}
+        sectionElementDeleteHandler={() =>
+          this.sectionProps.sectionElementDeleteHandler(index)
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getTemplateConfirmationFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.confirmation`;
+  }
+
+  private createAnamnesisComponent(
+    index: number,
+    sectionElement: ApiTemplateSectionElement,
+  ) {
+    return (
+      <AnamnesisQuestion
+        anamnesisFormikPath={this.getAnamnesisFormikPath(index)}
+        templateAnamnesisQuestion={sectionElement.anamnesisQuestion!}
+        addSubElementHandler={() => this.addAnamnesisSubText(index)}
+        removeSubQuestionHandler={() => this.removeAnamnesisSubText(index)}
+        mainQuestion={
+          <MainQuestion
+            elementDataFormikPath={this.getAnamnesisFormikPath(index)}
+            sectionElementDeleteHandler={() =>
+              this.sectionProps.sectionElementDeleteHandler(index)
+            }
+          />
+        }
+        key={index}
+      />
+    );
+  }
+
+  private getAnamnesisFormikPath(index: number) {
+    return `${this.getFormikArrayPath(index)}.anamnesisQuestion`;
+  }
+
+  private addAnamnesisSubText(index: number) {
+    const selectedSectionElement = this.sectionProps.sectionElements[index];
+
+    if (selectedSectionElement) {
+      selectedSectionElement.anamnesisQuestion!.subElementText =
+        createEmptySubTextElement();
+      this.sectionProps.replaceSectionElementHandler(
+        index,
+        selectedSectionElement,
+      );
+    }
+  }
+
+  private removeAnamnesisSubText(index: number) {
+    const selectedSectionElement = this.sectionProps.sectionElements[index];
+
+    if (selectedSectionElement) {
+      selectedSectionElement.anamnesisQuestion!.subElementText = undefined;
+      this.sectionProps.replaceSectionElementHandler(
+        index,
+        selectedSectionElement,
+      );
+    }
+  }
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx
new file mode 100644
index 000000000..f24065a83
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateConfirmation.tsx
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { InputField } from "@eshg/lib-portal/components/formFields/InputField";
+import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
+import { IconButton, Stack } from "@mui/joy";
+
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { notEmptyFieldValidation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/templateFieldValidation";
+
+export interface TemplateConfirmationProps {
+  sectionElementFormikPath: string;
+  sectionElementDeleteHandler: () => void;
+}
+export function TemplateConfirmation(
+  props: Readonly<TemplateConfirmationProps>,
+) {
+  return (
+    <DataElementBox data-testid="section-element-confirmation">
+      <DataElementHeading>Bestätigungsfeld</DataElementHeading>
+      <Stack direction="row" spacing={1} alignItems={"flex-start"}>
+        <InputField
+          label
+          name={`${props.sectionElementFormikPath}.confirmationTextField`}
+          placeholder="Textfeld"
+          sx={{ flex: 1 }}
+          validate={notEmptyFieldValidation}
+          data-testid="element-main-text"
+        />
+        <Stack alignItems="center" paddingTop={"6px"}>
+          <IconButton
+            onClick={props.sectionElementDeleteHandler}
+            aria-label="Entfernen"
+            color="warning"
+            variant="outlined"
+            title="Entfernen"
+            data-testid="element-delete-button"
+          >
+            <DeleteOutlineIcon />
+          </IconButton>
+        </Stack>
+      </Stack>
+    </DataElementBox>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx
new file mode 100644
index 000000000..3a9bd8fb0
--- /dev/null
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/TemplateTextBlock.tsx
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
+import { IconButton, Stack } from "@mui/joy";
+
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { notEmptyFieldValidation } from "@/lib/businessModules/travelMedicine/shared/templateEditor/templateFieldValidation";
+import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
+
+export interface TemplateTextBlockProps {
+  sectionElementFormikPath: string;
+  sectionElementDeleteHandler: () => void;
+}
+
+export function TemplateTextBlock(props: Readonly<TemplateTextBlockProps>) {
+  return (
+    <DataElementBox data-testid="section-element-textbox">
+      <DataElementHeading>Textblock</DataElementHeading>
+      <Stack direction="row" spacing={1} alignItems={"flex-start"}>
+        <TextareaField
+          label
+          name={`${props.sectionElementFormikPath}.textField`}
+          placeholder="Text"
+          sx={{ flex: 1 }}
+          validate={notEmptyFieldValidation}
+          data-testid="element-main-text"
+        />
+        <Stack alignItems="center" paddingTop={"6px"}>
+          <IconButton
+            onClick={props.sectionElementDeleteHandler}
+            aria-label="Entfernen"
+            color="warning"
+            variant="outlined"
+            title="Entfernen"
+            data-testid="element-delete-button"
+          >
+            <DeleteOutlineIcon />
+          </IconButton>
+        </Stack>
+      </Stack>
+    </DataElementBox>
+  );
+}
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
similarity index 56%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
index 0a5e0b5aa..b33d92924 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/SectionDataElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/AnamnesisQuestion.tsx
@@ -4,7 +4,7 @@
  */
 
 import {
-  ApiTemplateSectionElementData,
+  ApiTemplateAnamnesisQuestion,
   ApiTemplateSubElementText,
 } from "@eshg/employee-portal-api/travelMedicine";
 import { Add } from "@mui/icons-material";
@@ -12,8 +12,10 @@ import { Box, Button } from "@mui/joy";
 import { FieldArray } from "formik";
 import { ReactNode } from "react";
 
-import { SubMultiSelectList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList";
-import { SubQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion";
+import { DataElementBox } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementBox";
+import { DataElementHeading } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/DataElementHeading";
+import { SubMultiSelectList } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList";
+import { SubQuestion } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion";
 
 export function createEmptySubTextElement() {
   const subtextElement: ApiTemplateSubElementText = {
@@ -22,37 +24,26 @@ export function createEmptySubTextElement() {
   return subtextElement;
 }
 
-export interface MedicalHistoryTemplateSectionElementProp {
-  elementDataFormikPath: string;
-  sectionElementData: ApiTemplateSectionElementData;
+export interface AnamnesisQuestionProp {
+  anamnesisFormikPath: string;
+  templateAnamnesisQuestion: ApiTemplateAnamnesisQuestion;
   addSubElementHandler: () => void;
   removeSubQuestionHandler: () => void;
   mainQuestion: ReactNode;
 }
 
-export function SectionDataElement({
-  elementDataFormikPath,
-  sectionElementData,
+export function AnamnesisQuestion({
+  anamnesisFormikPath,
+  templateAnamnesisQuestion,
   addSubElementHandler,
   mainQuestion,
   removeSubQuestionHandler,
-}: Readonly<MedicalHistoryTemplateSectionElementProp>) {
-  const multiSelectElementsFormikPath = `${elementDataFormikPath}.subElementMultiSelect`;
+}: Readonly<AnamnesisQuestionProp>) {
+  const multiSelectElementsFormikPath = `${anamnesisFormikPath}.subElementMultiSelect`;
   return (
-    <Box
-      boxShadow="sm"
-      border="1px solid var(--neutral-outlined-border, #CDD7E1)"
-      borderRadius={12}
-      component="section"
-      flex={1}
-      style={{
-        padding: 12,
-        background: "var(--background-level-1, #F0F4F8)",
-      }}
-      data-testid="questions"
-    >
+    <DataElementBox data-testid="section-element-question">
+      <DataElementHeading>Anamnesefrage</DataElementHeading>
       {mainQuestion}
-
       <Box sx={{ paddingLeft: 4, mt: 2 }}>
         <FieldArray
           name={multiSelectElementsFormikPath}
@@ -60,24 +51,26 @@ export function SectionDataElement({
         >
           {({ push, remove }) => (
             <>
-              {sectionElementData.subElementMultiSelect.length > 0 && (
+              {templateAnamnesisQuestion.subElementMultiSelect.length > 0 && (
                 <SubMultiSelectList
                   multiSelectElementsFormikPath={multiSelectElementsFormikPath}
-                  multiSelectElements={sectionElementData.subElementMultiSelect}
+                  multiSelectElements={
+                    templateAnamnesisQuestion.subElementMultiSelect
+                  }
                   removeMultiSelectElementHandler={remove}
                 />
               )}
-              {sectionElementData.subElementText && (
+              {templateAnamnesisQuestion.subElementText && (
                 <SubQuestion
-                  subElementTextFormikPath={`${elementDataFormikPath}.subElementText`}
+                  subElementTextFormikPath={`${anamnesisFormikPath}.subElementText`}
                   subQuestionDeleteHandler={removeSubQuestionHandler}
                   multiSelectLength={
-                    sectionElementData.subElementMultiSelect.length
+                    templateAnamnesisQuestion.subElementMultiSelect.length
                   }
                 />
               )}
 
-              {!sectionElementData.subElementText && (
+              {!templateAnamnesisQuestion.subElementText && (
                 <Button
                   startDecorator={<Add />}
                   onClick={addSubElementHandler}
@@ -90,9 +83,9 @@ export function SectionDataElement({
                 startDecorator={<Add />}
                 onClick={() => push(createEmptySubTextElement())}
                 variant="plain"
-                data-testid="addMultiSelectQuestionText"
+                data-testid="element-add-multi-select-button"
               >
-                {sectionElementData.subElementMultiSelect.length == 0
+                {templateAnamnesisQuestion.subElementMultiSelect.length == 0
                   ? "Mehrfachauswahl hinzufügen"
                   : "Antwortmöglichkeit hinzufügen"}
               </Button>
@@ -100,6 +93,6 @@ export function SectionDataElement({
           )}
         </FieldArray>
       </Box>
-    </Box>
+    </DataElementBox>
   );
 }
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
similarity index 94%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
index 08f32dabf..f6b415c40 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/MainQuestion.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/MainQuestion.tsx
@@ -36,7 +36,7 @@ export function MainQuestion(
         placeholder="Frage eingeben"
         sx={{ flex: 1 }}
         validate={notEmptyFieldValidation}
-        data-testid="mainQuestionTitle"
+        data-testid="element-main-text"
       />
 
       <Stack direction="row" spacing={1} alignItems="center" paddingTop={"6px"}>
@@ -65,8 +65,8 @@ export function MainQuestion(
           aria-label="Entfernen"
           color="warning"
           variant="outlined"
-          title="Frageblock löschen"
-          data-testid="deleteQuestion"
+          title="Entfernen"
+          data-testid="element-delete-button"
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
similarity index 90%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
index 90f9d4932..9f1039b3d 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement.tsx
@@ -28,7 +28,7 @@ export function SubMultiSelectElement({
         placeholder="Frage eingeben"
         sx={{ flex: 1 }}
         validate={validateSelectField}
-        data-testid={`multiSelectQuestionText-${elementIndex}`}
+        data-testid={`element-multi-select-${elementIndex}`}
       />
       <Stack alignItems="center" paddingTop={"6px"}>
         <IconButton
@@ -37,7 +37,7 @@ export function SubMultiSelectElement({
           color="warning"
           variant="outlined"
           title="Antwortmöglichkeit löschen"
-          data-testid={`deleteMultiSelectQuestionText-${elementIndex}`}
+          data-testid={`element-multi-select-delete-button-${elementIndex}`}
         >
           <DeleteOutlineIcon />
         </IconButton>
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
similarity index 87%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
index 3ce40b2f9..fcfc1a39b 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectList.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectList.tsx
@@ -6,7 +6,7 @@
 import { ApiTemplateSubElementMultiSelect } from "@eshg/employee-portal-api/travelMedicine";
 import { Stack } from "@mui/joy";
 
-import { SubMultiSelectElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubMultiSelectElement";
+import { SubMultiSelectElement } from "@/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubMultiSelectElement";
 
 export function SubMultiSelectList({
   multiSelectElementsFormikPath,
@@ -22,7 +22,7 @@ export function SubMultiSelectList({
       direction="column"
       spacing={1}
       sx={{ mt: 2, mb: 2 }}
-      data-testid="multiselects"
+      data-testid="element-multi-select-list"
     >
       {multiSelectElements.map((element, index) => (
         <SubMultiSelectElement
diff --git a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
similarity index 97%
rename from employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx
rename to employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
index 9e3c8e30b..9c5a17479 100644
--- a/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/subElements/SubQuestion.tsx
+++ b/employee-portal/src/lib/businessModules/travelMedicine/shared/templateEditor/sections/dataElements/anamnesisQuestion/subElements/SubQuestion.tsx
@@ -33,7 +33,7 @@ export function SubQuestion({
             placeholder="Label"
             sx={{ flex: 1 }}
             validate={notEmptyFieldValidation}
-            data-testid="questionText"
+            data-testid="element-subelement-text"
           />
           <Stack alignItems="center" paddingTop={"6px"}>
             <IconButton
diff --git a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
index d841c1b65..4167970c6 100644
--- a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
+++ b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx
@@ -106,12 +106,15 @@ export function OpenDataTable() {
               handleDeleteVersion,
             })}
             getSubRows={(row) => row.subRows}
-            rowNavRoute={(row) => {
-              const { data, type } = row.original;
-              if (type !== "version") {
-                return undefined;
-              }
-              return routes.opendata.details(data.externalId);
+            rowNavigation={{
+              route: (row) => {
+                const { data, type } = row.original;
+                if (type !== "version") {
+                  return undefined;
+                }
+                return routes.opendata.details(data.externalId);
+              },
+              focusColumnAccessorKey: "name",
             }}
           />
         </TableSheet>
diff --git a/employee-portal/src/lib/opendata/components/openDataColumns.tsx b/employee-portal/src/lib/opendata/components/openDataColumns.tsx
index c6e642504..0924eba0f 100644
--- a/employee-portal/src/lib/opendata/components/openDataColumns.tsx
+++ b/employee-portal/src/lib/opendata/components/openDataColumns.tsx
@@ -130,7 +130,6 @@ export function openDataColumns(options: {
 
         return (
           <ActionsMenu
-            disablePortal
             actionItems={[
               {
                 label: "Neue Version anlegen",
diff --git a/employee-portal/src/lib/shared/api/config.ts b/employee-portal/src/lib/shared/api/config.ts
index ec76d2141..118a342ec 100644
--- a/employee-portal/src/lib/shared/api/config.ts
+++ b/employee-portal/src/lib/shared/api/config.ts
@@ -19,6 +19,7 @@ declare module "@eshg/lib-portal/api/ApiProvider" {
     PUBLIC_AUDITLOG_BACKEND_URL: string;
     PUBLIC_OPENDATA_BACKEND_URL: string;
     PUBLIC_STI_PROTECTION_BACKEND_URL: string;
+    PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: string;
   }
 }
 
@@ -38,4 +39,5 @@ export const API_CONFIGURATION: ApiConfiguration = {
   PUBLIC_AUDITLOG_BACKEND_URL: env.PUBLIC_AUDITLOG_BACKEND_URL,
   PUBLIC_OPENDATA_BACKEND_URL: env.PUBLIC_OPENDATA_BACKEND_URL,
   PUBLIC_STI_PROTECTION_BACKEND_URL: env.PUBLIC_STI_PROTECTION_BACKEND_URL,
+  PUBLIC_MEDICAL_REGISTRY_BACKEND_URL: env.PUBLIC_MEDICAL_REGISTRY_BACKEND_URL,
 };
diff --git a/employee-portal/src/lib/shared/components/SearchableGroups.tsx b/employee-portal/src/lib/shared/components/SearchableGroups.tsx
index 55cc2b5c0..e5b97b7c3 100644
--- a/employee-portal/src/lib/shared/components/SearchableGroups.tsx
+++ b/employee-portal/src/lib/shared/components/SearchableGroups.tsx
@@ -42,7 +42,7 @@ export interface SearchableGroupsProps<
 > {
   groups: SearchableGroup<TItem>[];
   label?: string;
-  placeholder: string;
+  placeholder?: string;
   hideSearch?: boolean;
   renderItem: (item: TItem) => ReactNode;
 }
diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
index 79809e28e..afd7892b3 100644
--- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
+++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTable.tsx
@@ -105,10 +105,11 @@ export function ArchiveTable(props: ArchiveTableProps) {
               sorting={tableControl.tableSorting}
               enableSortingRemoval={false}
               rowSelectionProps={rowSelectionProps}
-              focusColumnHeader="Geschlossen am"
-              rowNavRoute={(row) =>
-                props.procedureDetailsRoute(row.original.procedureId)
-              }
+              rowNavigation={{
+                route: (row) =>
+                  props.procedureDetailsRoute(row.original.procedureId),
+                focusColumnAccessorKey: "closedAt",
+              }}
             />
           </TableSheet>
         </TablePage>
diff --git a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
index 664d0f37f..d6ea1d1f3 100644
--- a/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/ActionsMenu.tsx
@@ -31,7 +31,6 @@ export interface ActionsMenuProps extends MenuButtonProps {
   actionDescription?: string;
   sx?: SxProps;
   color?: ColorPaletteProp;
-  disablePortal?: boolean;
   rowHeight?: boolean;
 }
 
@@ -142,22 +141,7 @@ export function ActionsMenu(props: ActionsMenuProps) {
         >
           <MoreVertIcon color={props.color ?? "primary"} />
         </MenuButton>
-        <Menu
-          placement="bottom-end"
-          disablePortal={props.disablePortal}
-          popperOptions={{ strategy: "fixed" }}
-          sx={
-            props.disablePortal
-              ? {
-                  // This allows the menu to work in table cells,
-                  // since it is part of the table cell's content and overflows the cell.
-                  "td:has(&)": {
-                    overflow: "visible",
-                  },
-                }
-              : undefined
-          }
-        >
+        <Menu placement="bottom-end">
           {actionItems.map((item) => createActionsLinkOrButton(item))}
         </Menu>
       </Stack>
diff --git a/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
index 0c3c1f613..d5869b550 100644
--- a/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
+++ b/employee-portal/src/lib/shared/components/buttons/IconTooltipButton.tsx
@@ -4,7 +4,11 @@
  */
 
 import { InfoOutlined } from "@mui/icons-material";
-import { IconButton as JoyIconButton, Tooltip } from "@mui/joy";
+import {
+  ColorPaletteProp,
+  IconButton as JoyIconButton,
+  Tooltip,
+} from "@mui/joy";
 import { SxProps } from "@mui/joy/styles/types";
 import { PropsWithChildren, ReactNode, forwardRef, useState } from "react";
 
@@ -16,12 +20,14 @@ export function InfoIconTooltipButton({
   size,
   iconLabelledBy,
   iconLabel: iconLabelProp,
+  tooltipColor,
 }: Readonly<{
   title: ReactNode;
   sx?: SxProps;
   iconLabel?: string;
   iconLabelledBy?: string;
   size?: "sm" | "md" | "lg";
+  tooltipColor?: ColorPaletteProp;
 }>) {
   const iconLabel =
     !iconLabelledBy && !iconLabelProp ? "Mehr Informationen" : iconLabelProp;
@@ -31,6 +37,7 @@ export function InfoIconTooltipButton({
       iconLabel={iconLabel}
       iconLabelledBy={iconLabelledBy}
       title={title}
+      tooltipColor={tooltipColor}
     />
   );
 }
@@ -40,17 +47,19 @@ export function IconTooltipButton({
   iconLabel,
   iconLabelledBy,
   title,
+  tooltipColor,
 }: Readonly<{
   icon: ReactNode;
   iconLabel?: string;
   iconLabelledBy?: string;
   title: ReactNode;
+  tooltipColor?: ColorPaletteProp;
 }>) {
   const [open, setOpen] = useState(false);
 
   return (
     <>
-      <Tooltip arrow variant="outlined" title={title}>
+      <Tooltip arrow color={tooltipColor} variant="outlined" title={title}>
         <IconButton
           aria-label={iconLabel}
           aria-labelledby={iconLabelledBy}
diff --git a/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx b/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
index 8dfe2478a..73c7488a1 100644
--- a/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
+++ b/employee-portal/src/lib/shared/components/cards/SelectableCard.tsx
@@ -19,12 +19,14 @@ export function SelectableCard({
   sx,
   radioProps,
   allowDeselection = false,
+  changeBackgroundColor = true,
   forGroupName,
 }: PropsWithChildren<{
   value?: unknown;
   sx?: SxProps;
   radioProps?: RadioProps;
   allowDeselection?: boolean;
+  changeBackgroundColor?: boolean;
   forGroupName?: string;
 }>) {
   const [field, _, helpers] = useField<string | null>(forGroupName!);
@@ -47,7 +49,7 @@ export function SelectableCard({
         gap: 2,
         borderRadius: "8px",
         [`:has(> .${radioClasses.checked})`]: {
-          backgroundColor: "primary.100",
+          backgroundColor: changeBackgroundColor ? "primary.100" : "",
           borderColor: "primary.300",
         },
         [`& .${radioClasses.checked}`]: {
diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
index 733182242..3d4084668 100644
--- a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
+++ b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx
@@ -88,7 +88,7 @@ export function ContentEditor({
         editorElement,
         insertAfter,
       },
-    }).catch();
+    });
     return response.element;
   }
 
@@ -146,7 +146,7 @@ export function ContentEditor({
     await deleteEditorElement({
       editorId: editorData.id,
       elementId: selectedElement.id,
-    }).catch();
+    });
     // update local elements
     const updatedElements = elements.filter(
       (el) => el.id !== selectedElement.id,
@@ -172,7 +172,7 @@ export function ContentEditor({
       editorId: editorData.id,
       elementId: selectedElement.id,
       apiUpdateEditorRequest: request,
-    }).catch();
+    });
     return response.element;
   }
 
diff --git a/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx b/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
deleted file mode 100644
index fabe992e4..000000000
--- a/employee-portal/src/lib/shared/components/detailsCard/DetailsCard.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-"use client";
-
-import { Row } from "@eshg/lib-portal/components/Row";
-import { Sheet, Typography, styled } from "@mui/joy";
-import { PropsWithChildren, ReactElement, useId } from "react";
-
-export function DetailsCard({
-  title,
-  fullHeight,
-  children,
-  actionButton,
-}: PropsWithChildren<{
-  title: string;
-  fullHeight?: boolean;
-  actionButton?: ReactElement;
-}>) {
-  const titleId = useId();
-  return (
-    <Sheet
-      component="section"
-      sx={{ padding: 3, height: fullHeight ? "100%" : "auto" }}
-      aria-labelledby={titleId}
-    >
-      <Row marginBottom={3} minHeight={36} justifyContent="space-between">
-        <Typography
-          component="h2"
-          fontWeight="600"
-          textOverflow="ellipsis"
-          fontSize="20px"
-          noWrap
-          level="body-md"
-          id={titleId}
-        >
-          {title}
-        </Typography>
-        {actionButton}
-      </Row>
-      <DetailSections>{children}</DetailSections>
-    </Sheet>
-  );
-}
-const DetailSections = styled("div")`
-  display: flex;
-  & > * {
-    margin-left: ${({ theme }) => theme.spacing(4)};
-    padding-left: ${({ theme }) => theme.spacing(4)};
-    border-left: 1px solid #636b744d;
-  }
-  & > *:first-child {
-    margin: 0;
-    padding: 0;
-    border: none;
-  }
-  ${({ theme }) => theme.breakpoints.down("xs")} {
-    flex-direction: column;
-    flex-basis: auto;
-  }
-`;
diff --git a/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx b/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
deleted file mode 100644
index d29fa1da4..000000000
--- a/employee-portal/src/lib/shared/components/detailsCard/LabeledValue.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink";
-import { Stack, Typography, styled } from "@mui/joy";
-import { SxProps } from "@mui/joy/styles/types";
-import { useId } from "react";
-
-import { multiLineEllipsis } from "@/lib/baseModule/theme/theme";
-
-export const ValueList = styled("div")<{ rowLayout?: boolean }>(
-  ({ theme, rowLayout }) => ({
-    display: "flex",
-    flexDirection: rowLayout ? "row" : "column",
-    flexWrap: "wrap",
-    gap: rowLayout ? theme.spacing(3) : theme.spacing(1),
-    flexBasis: rowLayout ? "auto" : theme.spacing(50),
-    overflow: "hidden",
-
-    [theme.breakpoints.down("xs")]: {
-      flexBasis: "auto",
-      flexDirection: "column",
-      marginLeft: 0,
-      paddingLeft: 0,
-      marginTop: theme.spacing(4),
-      paddingTop: theme.spacing(4),
-      borderLeft: "none",
-      borderTop: "1px solid #636b744d",
-    },
-  }),
-);
-
-export function LabeledValue({
-  label,
-  value,
-  href,
-  sx,
-}: {
-  label: string;
-  value: string | undefined;
-  href?: string;
-  sx?: SxProps;
-}) {
-  const labelId = useId();
-
-  if (!value) {
-    return undefined;
-  }
-
-  const blankTabAttrs = href?.startsWith("mailto:")
-    ? {
-        target: "_blank",
-        rel: "noopener noreferrer",
-      }
-    : {};
-  const content =
-    href && value ? (
-      <InternalLink
-        display="block"
-        sx={{
-          ...multiLineEllipsis(1),
-        }}
-        href={href}
-        {...blankTabAttrs}
-      >
-        {value}
-      </InternalLink>
-    ) : (
-      value
-    );
-
-  return (
-    <Stack sx={sx}>
-      <Typography
-        id={labelId}
-        fontWeight={500}
-        level="body-xs"
-        sx={{ color: "text.secondary", overflow: "hidden", textWrap: "nowrap" }}
-      >
-        {label}
-      </Typography>
-      <Typography
-        level="title-md"
-        fontWeight={600}
-        aria-labelledby={labelId}
-        title={value}
-        sx={{
-          ...multiLineEllipsis(1),
-        }}
-      >
-        {content}
-      </Typography>
-    </Stack>
-  );
-}
diff --git a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
index 9a3b881e2..384c91d25 100644
--- a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
+++ b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx
@@ -10,7 +10,7 @@ import { ReactNode } from "react";
 import { isNonNullish, isString } from "remeda";
 
 export interface DetailsCellProps {
-  name: string;
+  name?: string;
   label: string;
   value?: string | number | ReactNode;
   showIfEmpty?: boolean;
@@ -23,7 +23,7 @@ export interface DetailsCellProps {
 }
 
 export function DetailsCell({
-  name,
+  name: givenName,
   label,
   value,
   children,
@@ -38,6 +38,7 @@ export function DetailsCell({
     isNonNullish(value) && (!isString(value) || isNonEmptyString(value));
   const isRenderable =
     isRenderableValue || showIfEmpty === true || isNonNullish(children);
+  const name = givenName ?? label;
 
   return (
     isRenderable && (
diff --git a/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx b/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
index b61b2e331..ea71e5b84 100644
--- a/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
+++ b/employee-portal/src/lib/shared/components/detailsSection/DetailsSection.tsx
@@ -6,7 +6,7 @@
 "use client";
 
 import { Stack } from "@mui/joy";
-import { ReactNode, useMemo, useState } from "react";
+import { ReactNode, useId, useMemo, useState } from "react";
 import { isNonNullish, isNullish } from "remeda";
 
 import { BaseModalProps } from "@/lib/shared/components/BaseModal";
@@ -15,7 +15,7 @@ import { SectionHeader } from "@/lib/shared/components/detailsSection/SectionHea
 export type SimplifiedModalProps = Pick<BaseModalProps, "open" | "onClose">;
 
 interface DetailsSectionProps {
-  name: string;
+  name?: string;
   title: string;
   canEdit?: boolean;
   canDelete?: boolean;
@@ -53,7 +53,8 @@ export function DetailsSection({
     return undefined;
   }, [canEditCallback, canRenderModal, onEdit]);
 
-  const headerId = `${name}-header`;
+  const backupId = useId();
+  const headerId = `${name ?? backupId}-header`;
 
   return (
     <Stack
diff --git a/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx b/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
index 2e0a8a261..25f6918ee 100644
--- a/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
+++ b/employee-portal/src/lib/shared/components/filterSettings/FilterTemplates.tsx
@@ -79,9 +79,11 @@ export function FilterTemplates(props: FilterTemplatesProps) {
           placeholder="Filter-Vorlagen"
           aria-label="Filter-Vorlagen"
           value={props.selectedFilterTemplateId}
-          onChange={(_, newValue: string | null) =>
-            props.onFilterTemplateIdChanged(newValue)
-          }
+          onChange={(_, newValue: string | null) => {
+            if (newValue !== null) {
+              props.onFilterTemplateIdChanged(newValue);
+            }
+          }}
         >
           {props.templates.map((it) => (
             <Option key={it.id} value={it.id}>
diff --git a/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx b/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
index 1035b531f..3949421b6 100644
--- a/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
+++ b/employee-portal/src/lib/shared/components/form/FormGroupGrid.tsx
@@ -7,21 +7,15 @@ import { RequiresChildren } from "@eshg/lib-portal/types/react";
 import { Grid, GridProps } from "@mui/joy";
 
 interface FormGroupGridProps
-  extends Pick<GridProps, "columns">,
+  extends Pick<GridProps, "columns" | "aria-labelledby" | "component">,
     RequiresChildren {
   "data-testid"?: string;
 }
 
-export function FormGroupGrid(props: FormGroupGridProps) {
+export function FormGroupGrid({ children, ...props }: FormGroupGridProps) {
   return (
-    <Grid
-      container
-      columnSpacing={4}
-      rowSpacing={2}
-      data-testid={props["data-testid"]}
-      columns={props.columns}
-    >
-      {props.children}
+    <Grid container columnSpacing={4} rowSpacing={2} {...props}>
+      {children}
     </Grid>
   );
 }
diff --git a/employee-portal/src/lib/shared/components/formFields/SliderField.tsx b/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
index aa7ce846a..792b4cc5c 100644
--- a/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/SliderField.tsx
@@ -10,6 +10,7 @@ import { Slider } from "@mui/joy";
 export interface SliderFieldProps extends Omit<FieldProps<number>, "label"> {
   min: number;
   max: number;
+  ariaLabel?: string;
 }
 
 export function SliderField(props: SliderFieldProps) {
@@ -25,6 +26,7 @@ export function SliderField(props: SliderFieldProps) {
       valueLabelDisplay="auto"
       size="lg"
       sx={{ zIndex: 2 }}
+      aria-label={props.ariaLabel}
     />
   );
 }
diff --git a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
index 1846a2d8a..fb69cf323 100644
--- a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx
@@ -24,6 +24,7 @@ interface TextareaFieldProps extends ValidationRules<string> {
   minRows?: number;
   untrimmedInput?: boolean;
   disabled?: boolean;
+  "data-testid"?: string;
 }
 
 export function TextareaField(props: TextareaFieldProps) {
@@ -63,6 +64,7 @@ export function TextareaField(props: TextareaFieldProps) {
         readOnly={props.readOnly}
         disabled={disabled}
         slotProps={{ textarea: { disabled } }}
+        data-testid={props["data-testid"]}
       />
     </BaseField>
   );
diff --git a/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx b/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
index a0038eee0..00b0ed523 100644
--- a/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
+++ b/employee-portal/src/lib/shared/components/formFields/ToggleButtonGroupField.tsx
@@ -40,15 +40,11 @@ export function ToggleButtonGroupField(props: ToggleButtonGroupFieldProps) {
           void field.helpers.setValue(newValue);
         }}
         variant="outlined"
+        color="primary"
         sx={{ width: "100%" }}
       >
         {props.options.map((it) => (
-          <Button
-            key={it.label}
-            value={it.value}
-            sx={{ width: "100%" }}
-            color="primary"
-          >
+          <Button key={it.label} value={it.value} sx={{ width: "100%" }}>
             {it.label}
           </Button>
         ))}
diff --git a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
index 7594b913f..09fe0aaee 100644
--- a/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
+++ b/employee-portal/src/lib/shared/components/layout/MainContentLayout.tsx
@@ -8,8 +8,6 @@
 import { AlertSlot } from "@eshg/lib-portal/errorHandling/AlertContext";
 import { Stack, StackProps, styled } from "@mui/joy";
 
-import { PAGE_ALERT_STYLE } from "@/lib/shared/styles";
-
 export interface MainContentLayoutProps extends StackProps {
   /**
    * If true, the layout will limit it's height to the height of the viewport.
@@ -27,6 +25,11 @@ const LayoutStack = styled(Stack, {
   marginInline: theme.spacing(3),
 }));
 
+const AlertContainer = styled(Stack)(({ theme }) => ({
+  gap: theme.spacing(2),
+  marginBlockEnd: theme.spacing(3),
+}));
+
 /**
  * This layout applies global margin or padding to it's children.
  *
@@ -46,7 +49,7 @@ export function MainContentLayout(props: MainContentLayoutProps) {
       {...stackProps}
       className={props.fullViewportHeight ? "fullViewportHeight" : undefined}
     >
-      <AlertSlot sx={PAGE_ALERT_STYLE} />
+      <AlertSlot container={AlertContainer} />
       {children}
     </LayoutStack>
   );
diff --git a/employee-portal/src/lib/shared/components/page/PageGrid.tsx b/employee-portal/src/lib/shared/components/page/PageGrid.tsx
index d971f150f..b8f7cc27a 100644
--- a/employee-portal/src/lib/shared/components/page/PageGrid.tsx
+++ b/employee-portal/src/lib/shared/components/page/PageGrid.tsx
@@ -15,8 +15,7 @@ export function PageGrid({ children }: { children: ReactNode }) {
   return (
     <Grid
       container
-      columnSpacing={{ xxs: 2, xs: 2, sm: 3, md: 4, lg: 4, xl: 4, xxl: 5 }}
-      rowSpacing={{ xxs: 6, xs: 6, sm: 8, md: 9, lg: 12, xl: 12, xxl: 12 }}
+      spacing={{ xxs: 2, xs: 2, sm: 3, md: 4, lg: 4, xl: 4, xxl: 5 }}
     >
       {children}
     </Grid>
diff --git a/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx b/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
index d38b6a0a3..77117411c 100644
--- a/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
+++ b/employee-portal/src/lib/shared/components/personSidebar/search/PersonSearchResults.tsx
@@ -10,6 +10,7 @@ import ArrowBackIosOutlined from "@mui/icons-material/ArrowBackIosOutlined";
 import { Box, Button, Stack, Typography } from "@mui/joy";
 import { Formik } from "formik";
 import { Ref } from "react";
+import { isDefined } from "remeda";
 
 import { PersonCardContent } from "@/lib/baseModule/components/person/PersonCardContent";
 import { NoSearchResults } from "@/lib/shared/components/NoSearchResult";
@@ -31,7 +32,7 @@ interface PersonSearchResultsProps {
   onSelectPerson: (person: ApiGetReferencePersonResponse) => void;
   onCreatePerson: () => void;
   onCancel: () => void;
-  onBack: () => void;
+  onBack?: () => void;
   sidebarFormRef: Ref<SidebarFormHandle>;
 }
 
@@ -61,14 +62,16 @@ export function PersonSearchResults(props: PersonSearchResultsProps) {
             header={
               <>
                 <Stack gap={2}>
-                  <Button
-                    variant="plain"
-                    startDecorator={<ArrowBackIosOutlined />}
-                    sx={{ alignSelf: "start", paddingInline: 0 }}
-                    onClick={props.onBack}
-                  >
-                    Eingabe ändern
-                  </Button>
+                  {isDefined(props.onBack) && (
+                    <Button
+                      variant="plain"
+                      startDecorator={<ArrowBackIosOutlined />}
+                      sx={{ alignSelf: "start", paddingInline: 0 }}
+                      onClick={props.onBack}
+                    >
+                      Eingabe ändern
+                    </Button>
+                  )}
                   <Stack>
                     Bereits vorhandene Einträge zur Person:
                     <Typography level={"title-md"}>
diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
index eaafb84e5..0105e5e06 100644
--- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx
@@ -73,11 +73,13 @@ export function InboxProceduresTable(props: InboxProceduresTableProps) {
           data={inboxProcedures}
           sorting={tableControl.tableSorting}
           columns={inboxProcedureColumns}
-          rowNavRoute={(row) =>
-            buildRoutePreservingSearchParams(
-              props.routes.details(row.original.inboxProcedureId),
-            )
-          }
+          rowNavigation={{
+            route: (row) =>
+              buildRoutePreservingSearchParams(
+                props.routes.details(row.original.inboxProcedureId),
+              ),
+            focusColumnAccessorKey: "inboxProgressEntry.subject",
+          }}
         />
       </TableSheet>
     </TablePage>
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
index fae493f29..c75adadeb 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/ProgressEntriesContext.tsx
@@ -6,6 +6,7 @@
 "use client";
 
 import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
   ApiManualProgressEntry,
   ApiProcedureStatus,
 } from "@eshg/employee-portal-api/businessProcedures";
@@ -17,7 +18,7 @@ import { buildName } from "@/lib/shared/components/procedures/progress-entries/h
 import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl";
 import { useIsOffline } from "@/lib/shared/hooks/useIsOffline";
 
-import { ProgressEntriesConfig, RelatedEntry } from "./types";
+import { ProgressEntriesConfig, RelatedProgressEntry } from "./types";
 
 interface ProgressEntriesContextProps {
   config: ProgressEntriesConfig;
@@ -181,7 +182,7 @@ export function useResolvedUserName(userId: string) {
 }
 
 export function useFilteredAndSortedRelatedEntries(
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[],
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[],
 ) {
   return relatedKeyDocumentProgressEntries
     .filter(hasUndeletedFileAndKeyDocumentVersion)
@@ -191,8 +192,8 @@ export function useFilteredAndSortedRelatedEntries(
 }
 
 function hasUndeletedFileAndKeyDocumentVersion(
-  entry: ApiManualProgressEntry,
-): entry is RelatedEntry {
+  entry: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+): entry is RelatedProgressEntry {
   return (
     isDefined(entry.fileReference) &&
     !entry.fileReference.deleted &&
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
index 19602185a..32b57d646 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/constants.tsx
@@ -87,7 +87,7 @@ export const inboxProgressEntryTitles = {
   [ApiInboxProgressEntryType.PhoneCall]: "Anruf erhalten",
 } satisfies Record<ApiInboxProgressEntryType, string>;
 
-export const manualProgressEntryKeyDocumentTypes: Record<string, string> = {
+export const keyDocumentTypes: Record<string, string> = {
   INVOICE: "Rechnung",
   REPORT: "Bericht",
 };
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
index 48d0c5b26..05dc2dfcd 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/EntryDeletionRequestModal.tsx
@@ -35,15 +35,13 @@ export function EntryDeletionRequestModalContent() {
 
   async function handleSubmit(values: DeletionRequestValues) {
     if (entryIdForDeletion !== null)
-      await requestProgressEntryDeletion
-        .mutateAsync(
-          {
-            entryId: entryIdForDeletion,
-            createApprovalRequest: values,
-          },
-          { onSuccess: handleClose },
-        )
-        .catch();
+      await requestProgressEntryDeletion.mutateAsync(
+        {
+          entryId: entryIdForDeletion,
+          createApprovalRequest: values,
+        },
+        { onSuccess: handleClose },
+      );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
index 5e750bd11..593ca8141 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/modals/FileDeletionRequestModal.tsx
@@ -24,12 +24,10 @@ export function FileDeletionRequestModal() {
 
   async function handleSubmit(values: DeletionRequestValues) {
     if (fileIdForDeletion !== null)
-      await requestFileDeletion
-        .mutateAsync(
-          { fileId: fileIdForDeletion, createApprovalRequest: values },
-          { onSuccess: handleClose },
-        )
-        .catch();
+      await requestFileDeletion.mutateAsync(
+        { fileId: fileIdForDeletion, createApprovalRequest: values },
+        { onSuccess: handleClose },
+      );
   }
 
   function handleClose() {
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
index 5a5dfde62..0b4bbcb86 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/CreateProgressEntrySidebar.tsx
@@ -21,8 +21,8 @@ import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"
 import { FileField } from "@/lib/shared/components/formFields/file/FileField";
 import { useProgressEntriesConfig } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
 import {
+  keyDocumentTypes,
   manualProgressEntryFileTypes,
-  manualProgressEntryKeyDocumentTypes,
   manualProgressEntryTypeNames,
 } from "@/lib/shared/components/procedures/progress-entries/constants";
 import {
@@ -138,9 +138,7 @@ function CreateProgressEntrySidebarContent({
                         value: "",
                         label: "",
                       },
-                    ].concat(
-                      buildEnumOptions(manualProgressEntryKeyDocumentTypes),
-                    )}
+                    ].concat(buildEnumOptions(keyDocumentTypes))}
                   />
                 )}
                 <TextareaField name={"text"} label={"Text"} />
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
index d50bba686..e0503adcd 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper.tsx
@@ -12,13 +12,25 @@ import {
 import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime";
 import DateRangeOutlinedIcon from "@mui/icons-material/DateRangeOutlined";
 import PersonOutlineIcon from "@mui/icons-material/PersonOutline";
-import { Divider, Stack, Typography } from "@mui/joy";
+import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
+import {
+  Accordion,
+  AccordionDetails,
+  AccordionGroup,
+  AccordionSummary,
+  Box,
+  Divider,
+  Sheet,
+  Stack,
+  Typography,
+} from "@mui/joy";
 import { PropsWithChildren, ReactNode } from "react";
 import { isDefined } from "remeda";
 
 import { FileCardWithActions } from "@/lib/shared/components/procedures/progress-entries/FileCardWithActions";
 import { DeletionNote } from "@/lib/shared/components/procedures/progress-entries/FileOrDeletionNote";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
+import { RelatedProgressEntry } from "@/lib/shared/components/procedures/progress-entries/types";
 import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent";
 
 type OneOrMoreNodes = ReactNode | ReactNode[];
@@ -26,7 +38,6 @@ type OneOrMoreNodes = ReactNode | ReactNode[];
 interface AdditionalFileElements {
   start?: OneOrMoreNodes;
   end?: OneOrMoreNodes;
-  mail?: OneOrMoreNodes;
 }
 
 interface DetailsContentWrapperProps {
@@ -104,23 +115,31 @@ interface UndeletedFileSectionProps
 
 interface EmailFieldsProps {
   file: ApiMail;
-  additionalElements?: OneOrMoreNodes;
   subject?: string;
   message?: string;
 }
 
-function EmailFields({ file, additionalElements }: EmailFieldsProps) {
+function EmailFields({ file }: EmailFieldsProps) {
   return (
     <>
       <LabelValueDisplay
         label="Absender"
-        value={isDefined(file.metaData) ? file.metaData.mailFrom : ""}
+        value={file.metaData?.mailFrom ?? ""}
       />
       <LabelValueDisplay
         label="Empfänger"
-        value={isDefined(file.metaData) ? file.metaData.mailTo : ""}
+        value={file.metaData?.mailTo ?? ""}
+      />
+      <LabelValueDisplay
+        key="subject"
+        label="Betreff"
+        value={file.metaData?.subject ?? ""}
+      />
+      <LabelValueDisplay
+        key="email-text"
+        label="Email-Text"
+        value={file.metaData?.messageText ?? ""}
       />
-      {isDefined(additionalElements) && additionalElements}
     </>
   );
 }
@@ -136,13 +155,66 @@ function UndeletedFileSection({
       </Typography>
       <FileCardWithActions file={file} />
       {additionalElements?.start}
-      {file.type === "Mail" && (
-        <EmailFields
-          file={file as ApiMail}
-          additionalElements={additionalElements?.mail}
-        />
-      )}
+      {file.type === "Mail" && <EmailFields file={file as ApiMail} />}
       {isDefined(additionalElements?.end) ? additionalElements.end : <></>}
     </>
   );
 }
+
+export function NewerVersionHint() {
+  return (
+    <Sheet variant="soft" color="neutral">
+      <Typography
+        level="body-sm"
+        startDecorator={<WarningAmberOutlinedIcon color="warning" />}
+      >
+        Es existiert eine neuere Version dieser Datei.
+      </Typography>
+    </Sheet>
+  );
+}
+
+export function AllKeyDocumentVersions({
+  relatedEntries,
+}: {
+  relatedEntries: RelatedProgressEntry[];
+}) {
+  return (
+    <AccordionGroup
+      variant="soft"
+      color="primary"
+      size="sm"
+      sx={{ borderRadius: "md" }}
+    >
+      <Accordion>
+        <AccordionSummary
+          color="primary"
+          sx={{
+            button: {
+              color: "var(--joy-palette-primary-700)",
+              borderRadius: "md",
+            },
+          }}
+        >
+          Alle Versionen anzeigen
+        </AccordionSummary>
+        <AccordionDetails>
+          <Stack gap={1} padding={1}>
+            {relatedEntries.map((entry) => (
+              <Box key={entry.keyDocumentVersion} data-testid="relatedVersion">
+                <Typography
+                  level="body-sm"
+                  fontWeight="500"
+                >{`Version ${entry.keyDocumentVersion}`}</Typography>
+                <FileCardWithActions
+                  detailsProgressEntryId={entry.progressEntryId}
+                  file={entry.fileReference}
+                />
+              </Box>
+            ))}
+          </Stack>
+        </AccordionDetails>
+      </Accordion>
+    </AccordionGroup>
+  );
+}
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
index 52aa92114..c9e655e35 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ManualProgressEntryDetails.tsx
@@ -3,22 +3,13 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { ApiManualProgressEntry } from "@eshg/employee-portal-api/businessProcedures";
+import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+  ApiManualProgressEntry,
+} from "@eshg/employee-portal-api/businessProcedures";
 import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton";
 import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
-import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
-import {
-  Accordion,
-  AccordionDetails,
-  AccordionGroup,
-  AccordionSummary,
-  Box,
-  Button,
-  Chip,
-  Sheet,
-  Stack,
-  Typography,
-} from "@mui/joy";
+import { Button, Chip } from "@mui/joy";
 import { Formik } from "formik";
 import { ReactNode, useContext, useState } from "react";
 import { isDefined } from "remeda";
@@ -26,7 +17,6 @@ import { isDefined } from "remeda";
 import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar";
 import { SidebarForm } from "@/lib/shared/components/form/SidebarForm";
 import { TextareaField } from "@/lib/shared/components/formFields/TextareaField";
-import { FileCardWithActions } from "@/lib/shared/components/procedures/progress-entries/FileCardWithActions";
 import {
   ProgressEntriesContext,
   useFilteredAndSortedRelatedEntries,
@@ -34,7 +24,7 @@ import {
   useProgressEntriesConfig,
 } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
 import {
-  manualProgressEntryKeyDocumentTypes,
+  keyDocumentTypes,
   manualProgressEntryTypeNames,
 } from "@/lib/shared/components/procedures/progress-entries/constants";
 import { extractFileDescriptionValue } from "@/lib/shared/components/procedures/progress-entries/helper";
@@ -44,10 +34,13 @@ import {
   mapToPatchRequest,
   mapToUpdateMetaDataRequest,
 } from "@/lib/shared/components/procedures/progress-entries/mapper";
-import { DetailsContentWrapper } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
+import {
+  AllKeyDocumentVersions,
+  DetailsContentWrapper,
+  NewerVersionHint,
+} from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
 import { DetailsHistory } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
-import { RelatedEntry } from "@/lib/shared/components/procedures/progress-entries/types";
 import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions";
 
 type ManualProgressEntryDetailsView = "DETAILS" | "HISTORY";
@@ -58,7 +51,7 @@ export function ManualProgressEntryDetails({
   onClose,
 }: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
 }) {
   const [currentView, setCurrentView] =
@@ -91,7 +84,7 @@ export function ManualProgressEntryDetails({
 
 export function ManualProgressEntryDetailsView(props: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
   onHistory: () => void;
 }) {
@@ -140,7 +133,7 @@ function EditableManualProgressEntryDetails({
   onHistory,
 }: {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   onClose: () => void;
   onHistory: () => void;
 }) {
@@ -153,22 +146,20 @@ function EditableManualProgressEntryDetails({
   };
 
   async function handleSubmit(values: ProgressEntryDetailsValues) {
-    await patchProgressEntry
-      .mutateAsync(
-        {
-          entryId: entry.progressEntryId,
-          patchProgressEntryRequest: mapToPatchRequest(values, entry.note),
-          fileId: entry.fileReference?.fileId,
-          updateFileMetaDataRequest: mapToUpdateMetaDataRequest(
-            values,
-            entry.fileReference,
-          ),
-        },
-        {
-          onSuccess: onClose,
-        },
-      )
-      .catch();
+    await patchProgressEntry.mutateAsync(
+      {
+        entryId: entry.progressEntryId,
+        patchProgressEntryRequest: mapToPatchRequest(values, entry.note),
+        fileId: entry.fileReference?.fileId,
+        updateFileMetaDataRequest: mapToUpdateMetaDataRequest(
+          values,
+          entry.fileReference,
+        ),
+      },
+      {
+        onSuccess: onClose,
+      },
+    );
   }
 
   return (
@@ -221,7 +212,7 @@ function isFileLocked(entry: ApiManualProgressEntry) {
 
 interface ManualProgressEntryDetailsTemplateProps {
   entry: ApiManualProgressEntry;
-  relatedKeyDocumentProgressEntries: ApiManualProgressEntry[];
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
   handleClose: () => void;
   elements: {
     submit?: ReactNode;
@@ -265,11 +256,7 @@ function ManualProgressEntryDetailsTemplate({
             <>
               <LabelValueDisplay
                 label="Dokumenttyp"
-                value={
-                  manualProgressEntryKeyDocumentTypes[
-                    entry.keyDocumentType ?? ""
-                  ] ?? ""
-                }
+                value={keyDocumentTypes[entry.keyDocumentType ?? ""] ?? ""}
                 endDecorator={
                   entry.keyDocumentVersion ? (
                     <Chip color="primary">{`Version ${entry.keyDocumentVersion}`}</Chip>
@@ -282,18 +269,6 @@ function ManualProgressEntryDetailsTemplate({
               )}
             </>
           ),
-          mail: [
-            <LabelValueDisplay
-              key="subject"
-              label="Betreff"
-              value={isDefined(entry.subject) ? entry.subject : ""}
-            />,
-            <LabelValueDisplay
-              key="email-text"
-              label="Email-Text"
-              value={isDefined(entry.messageText) ? entry.messageText : ""}
-            />,
-          ],
           end: elements.fileDescription,
         }}
         endSlot={
@@ -335,61 +310,3 @@ function ManualProgressEntryDetailsTemplate({
     </>
   );
 }
-
-function AllKeyDocumentVersions({
-  relatedEntries,
-}: {
-  relatedEntries: RelatedEntry[];
-}) {
-  return (
-    <AccordionGroup
-      variant="soft"
-      color="primary"
-      size="sm"
-      sx={{ borderRadius: "md" }}
-    >
-      <Accordion>
-        <AccordionSummary
-          color="primary"
-          sx={{
-            button: {
-              color: "var(--joy-palette-primary-700)",
-              borderRadius: "md",
-            },
-          }}
-        >
-          Alle Versionen anzeigen
-        </AccordionSummary>
-        <AccordionDetails>
-          <Stack gap={1} padding={1}>
-            {relatedEntries.map((entry) => (
-              <Box key={entry.keyDocumentVersion} data-testid="relatedVersion">
-                <Typography
-                  level="body-sm"
-                  fontWeight="500"
-                >{`Version ${entry.keyDocumentVersion}`}</Typography>
-                <FileCardWithActions
-                  detailsProgressEntryId={entry.progressEntryId}
-                  file={entry.fileReference}
-                />
-              </Box>
-            ))}
-          </Stack>
-        </AccordionDetails>
-      </Accordion>
-    </AccordionGroup>
-  );
-}
-
-function NewerVersionHint() {
-  return (
-    <Sheet variant="soft" color="neutral">
-      <Typography
-        level="body-sm"
-        startDecorator={<WarningAmberOutlinedIcon color="warning" />}
-      >
-        Es existiert eine neuere Version dieser Datei.
-      </Typography>
-    </Sheet>
-  );
-}
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
index 10a98e65b..47fd24012 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/ProgressEntryDetailsSidebar.tsx
@@ -58,7 +58,12 @@ export function ProgressEntryDetailsSidebar({
           )}
         {isDefined(progressEntry) &&
           progressEntry.type === "SystemProgressEntry" && (
-            <SystemProgressEntryDetails entry={progressEntry} />
+            <SystemProgressEntryDetails
+              entry={progressEntry}
+              relatedKeyDocumentProgressEntries={
+                relatedKeyDocumentProgressEntries
+              }
+            />
           )}
         {isDefined(progressEntry) &&
           progressEntry.type === "ProcessedInboxProgressEntry" && (
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
index 589ed4149..63a24a09a 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/SystemProgressEntryDetails.tsx
@@ -3,22 +3,49 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { ApiSystemProgressEntry } from "@eshg/employee-portal-api/businessProcedures";
+import {
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
+  ApiSystemProgressEntry,
+} from "@eshg/employee-portal-api/businessProcedures";
+import { Chip } from "@mui/joy";
 import { isDefined } from "remeda";
 
-import { systemProgressEntryTypeTitles } from "@/lib/shared/components/procedures/progress-entries/constants";
+import { useFilteredAndSortedRelatedEntries } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext";
+import {
+  keyDocumentTypes,
+  systemProgressEntryTypeTitles,
+} from "@/lib/shared/components/procedures/progress-entries/constants";
 import { displayTriggerer } from "@/lib/shared/components/procedures/progress-entries/helper";
-import { DetailsContentWrapper } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
+import {
+  AllKeyDocumentVersions,
+  DetailsContentWrapper,
+  NewerVersionHint,
+} from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsContentWrapper";
 import { LabelValueDisplay } from "@/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/LabelValueDisplay";
 
 export function SystemProgressEntryDetails({
   entry,
+  relatedKeyDocumentProgressEntries,
 }: {
   entry: ApiSystemProgressEntry;
+  relatedKeyDocumentProgressEntries: ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner[];
 }) {
   const titlePrefix = "Details";
   const titleSuffix =
     systemProgressEntryTypeTitles[entry.systemProgressEntryType];
+
+  const relatedEntries = useFilteredAndSortedRelatedEntries(
+    relatedKeyDocumentProgressEntries,
+  );
+
+  const { keyDocumentVersion } = entry;
+  const showNewerVersionHint =
+    isDefined(keyDocumentVersion) &&
+    isDefined(relatedEntries) &&
+    relatedEntries.some(
+      (relatedEntry) => relatedEntry.keyDocumentVersion > keyDocumentVersion,
+    );
+
   return (
     <DetailsContentWrapper
       entry={entry}
@@ -26,6 +53,25 @@ export function SystemProgressEntryDetails({
         isDefined(titleSuffix) ? `${titlePrefix} ${titleSuffix}` : titlePrefix
       }
       creatorName={displayTriggerer(entry)}
+      additionalFileElements={{
+        start: (
+          <>
+            <LabelValueDisplay
+              label="Dokumenttyp"
+              value={keyDocumentTypes[entry.keyDocumentType ?? ""] ?? ""}
+              endDecorator={
+                entry.keyDocumentVersion ? (
+                  <Chip color="primary">{`Version ${entry.keyDocumentVersion}`}</Chip>
+                ) : undefined
+              }
+            />
+            {showNewerVersionHint && <NewerVersionHint />}
+            {isDefined(relatedEntries) && relatedEntries.length > 0 && (
+              <AllKeyDocumentVersions relatedEntries={relatedEntries} />
+            )}
+          </>
+        ),
+      }}
     >
       <LabelValueDisplay
         label="Text"
diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
index e9b1a2a37..a5a0f6398 100644
--- a/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
+++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/types.ts
@@ -11,15 +11,15 @@ import {
   ApiCreateApprovalRequestRequest,
   ApiCreateManualProgressEntryRequest,
   ApiFileMetaData,
-  ApiGenericFileReference,
   ApiGetDetailedProcedureResponse,
   ApiGetFile200Response,
   ApiGetProcedureApprovalRequestsResponse,
   ApiGetProgressEntriesResponseProgressEntriesInner,
   ApiGetProgressEntryResponse,
-  ApiInboxProgressEntryFileReference,
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner,
   ApiManualProgressEntry,
   ApiPatchManualProgressEntryRequest,
+  ApiProgressEntry,
   ApiProgressEntryClass,
   ApiProgressEntryReferenceFilePair,
   ApiUpdateFileMetaDataRequest,
@@ -74,6 +74,7 @@ export interface ProgressEntriesUrlParams {
   params: Readonly<{ id: string; entryId?: string }>;
   searchParams: SearchParams;
 }
+
 interface ProgressEntryApiActions {
   useCreateProgressEntry: () => UseMutationResult<
     ApiManualProgressEntry,
@@ -87,6 +88,7 @@ interface ProgressEntryApiActions {
   >;
   useDeleteFile: () => UseMutationResult<void, Error, string, unknown>;
   useDeleteProgressEntry: () => UseMutationResult<void, Error, string, unknown>;
+
   usePatchProgressEntry(): UseMutationResult<
     {
       entry?: ApiManualProgressEntry;
@@ -101,6 +103,7 @@ interface ProgressEntryApiActions {
     },
     unknown
   >;
+
   useRequestProgressEntryDeletion: () => UseMutationResult<
     ApiApprovalRequest,
     Error,
@@ -180,11 +183,11 @@ export interface ProgressEntriesFilters {
   progressEntryClass?: Set<ApiProgressEntryClass>;
 }
 
-export interface RelatedEntry
-  extends Omit<ApiManualProgressEntry, "fileReference" | "keyDocumentVersion"> {
-  fileReference: Exclude<
-    ApiInboxProgressEntryFileReference,
-    { type: "GenericFileReference" } & ApiGenericFileReference
-  >;
+interface RelatedEntry extends Omit<ApiProgressEntry, "fileReference"> {
+  fileReference: ApiGetFile200Response;
   keyDocumentVersion: number;
 }
+
+export type RelatedProgressEntry =
+  ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner &
+    RelatedEntry;
diff --git a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
index b2796d17d..433a317ea 100644
--- a/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
+++ b/employee-portal/src/lib/shared/components/sidebar/SidebarContent.tsx
@@ -11,8 +11,9 @@ import { isDefined, isNonNullish } from "remeda";
 
 import { sidebarPadding } from "@/lib/shared/components/sidebar/Sidebar";
 
-const AlertContainer = styled("div")(({ theme }) => ({
+const AlertContainer = styled(Stack)(({ theme }) => ({
   paddingInline: theme.spacing(sidebarPadding),
+  gap: theme.spacing(2),
 }));
 
 export interface SidebarContentProps {
@@ -81,6 +82,7 @@ export function SidebarContent({
           sx={{
             paddingRight: sidebarPadding,
             paddingLeft: sidebarPadding,
+            paddingBottom: 3,
             flex: 1,
           }}
         >
diff --git a/employee-portal/src/lib/shared/components/table/DataTable.tsx b/employee-portal/src/lib/shared/components/table/DataTable.tsx
index df629e4e6..67c21c54e 100644
--- a/employee-portal/src/lib/shared/components/table/DataTable.tsx
+++ b/employee-portal/src/lib/shared/components/table/DataTable.tsx
@@ -10,6 +10,7 @@ import { SxProps } from "@mui/joy/styles/types";
 import { visuallyHidden } from "@mui/utils";
 import {
   Cell,
+  DeepKeys,
   Row,
   RowSelectionOptions,
   RowSelectionTableState,
@@ -82,6 +83,15 @@ type SupportedRowSelectionOptions =
   | "enableRowSelection"
   | "onRowSelectionChange";
 
+interface RowNavigation<TData> {
+  route: (row: Row<TData>) => string | undefined;
+  /**
+   * focusColumnAccessorKey should be set to the accessor key of an accessor column containing non-interactive cells.
+   * Background: This is necessary to enable keyboard-based row navigation, which requires a focusable cell in each row. Currently, we expect each table to have at least one accessor column. If not the case, please consider extending this interface to support additional options.
+   */
+  focusColumnAccessorKey: DeepKeys<TData> & string;
+}
+
 export interface DataTableProps<TData> {
   data: TableOptions<TData>["data"];
   columns: TableOptions<TData>["columns"];
@@ -98,8 +108,7 @@ export interface DataTableProps<TData> {
    */
   striped?: boolean;
   noDataComponent?: () => ReactNode;
-  rowNavRoute?: (row: Row<TData>) => string | undefined;
-  focusColumnHeader?: string;
+  rowNavigation?: RowNavigation<TData>;
   /** By default, the (text) content is truncated. Set to true, to break the content into multiple lines. */
   wrapContent?: boolean;
   /** Set to true to break the header text into multiple lines. */
@@ -232,12 +241,15 @@ export function DataTable<TData>(props: Readonly<DataTableProps<TData>>) {
         )}
 
         <TableNavigationProvider
-          enabled={isDefined(props.rowNavRoute)}
-          focusColumnHeader={props.focusColumnHeader}
+          enabled={isDefined(props.rowNavigation)}
+          focusColumnAccessorKey={props.rowNavigation?.focusColumnAccessorKey}
         >
           {reactTable.getRowModel().rows.map((row) => (
             <Fragment key={row.id}>
-              <TableRow<TData> row={row} rowNavRoute={props.rowNavRoute} />
+              <TableRow<TData>
+                row={row}
+                rowNavigation={props.rowNavigation?.route}
+              />
               {isDefined(props.subRowColumns) && (
                 <TableSubRow<TData>
                   row={row}
diff --git a/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx b/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
index ef00e2b83..b20a706b6 100644
--- a/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
+++ b/employee-portal/src/lib/shared/components/table/TableNavigationContext.tsx
@@ -17,7 +17,7 @@ type Router = ReturnType<typeof useRouter>;
 
 interface TableNavigationContext {
   onCellClick: (route: string) => void;
-  focusColumnHeader: string | undefined;
+  focusColumnAccessorKey: string | undefined;
 }
 
 function handleCallToAction(td: Element, router: Router) {
@@ -51,12 +51,12 @@ export const TableNavigationContext = createContext<
 
 export function TableNavigationProvider({
   enabled,
-  focusColumnHeader,
+  focusColumnAccessorKey,
   children,
 }: {
   enabled: boolean;
   children: ReactNode;
-  focusColumnHeader: string | undefined;
+  focusColumnAccessorKey: string | undefined;
 }) {
   const tableRef = useRef<HTMLTableSectionElement>(null);
   const router = useRouter();
@@ -99,7 +99,7 @@ export function TableNavigationProvider({
   return (
     <tbody ref={tableRef}>
       <TableNavigationContext.Provider
-        value={{ onCellClick, focusColumnHeader }}
+        value={{ onCellClick, focusColumnAccessorKey: focusColumnAccessorKey }}
       >
         {children}
       </TableNavigationContext.Provider>
diff --git a/employee-portal/src/lib/shared/components/table/TableRow.tsx b/employee-portal/src/lib/shared/components/table/TableRow.tsx
index 4bfeb4fca..65b36023a 100644
--- a/employee-portal/src/lib/shared/components/table/TableRow.tsx
+++ b/employee-portal/src/lib/shared/components/table/TableRow.tsx
@@ -40,24 +40,28 @@ const StyledCell = styled("td")<{ canNavigate: boolean } & StyledCellProps>(({
 
 function isFocusColumn<TData>(
   cell: Cell<TData, unknown>,
-  focusColumnName: string | undefined,
+  focusColumnAccessorKey: string | undefined,
 ) {
-  return focusColumnName === cell.column.columnDef.header;
+  // column id is sometimes different from the accessor key
+  return (
+    "accessorKey" in cell.column.columnDef &&
+    focusColumnAccessorKey === cell.column.columnDef.accessorKey
+  );
 }
 
 export function TableRow<TData>({
   row,
-  rowNavRoute,
+  rowNavigation,
 }: Readonly<{
   row: Row<TData>;
-  rowNavRoute?: (cell: Row<TData>) => string | undefined;
+  rowNavigation?: (cell: Row<TData>) => string | undefined;
 }>) {
   const navContext = useContext(TableNavigationContext);
   const focusCell = row
     .getVisibleCells()
-    .find((cell) => isFocusColumn(cell, navContext?.focusColumnHeader));
+    .find((cell) => isFocusColumn(cell, navContext?.focusColumnAccessorKey));
   const rowLabel = focusCell ? getAriaLabel(focusCell) : undefined;
-  const navRoute = rowNavRoute?.(row);
+  const navRoute = rowNavigation?.(row);
   const isParentRow = row.depth === 0;
 
   function cellCanNavigate(cell: Cell<TData, unknown>) {
diff --git a/employee-portal/src/lib/shared/styles.ts b/employee-portal/src/lib/shared/styles.ts
deleted file mode 100644
index a31dd7ceb..000000000
--- a/employee-portal/src/lib/shared/styles.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright 2024 cronn GmbH
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { SxProps } from "@mui/joy/styles/types";
-
-export const PAGE_ALERT_STYLE: SxProps = {
-  marginBlockEnd: 3,
-};
diff --git a/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts b/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts
new file mode 100644
index 000000000..b892b6e18
--- /dev/null
+++ b/employee-portal/src/serviceWorker/common/unregisterBroadCastChannel.ts
@@ -0,0 +1,10 @@
+/**
+ * Copyright 2024 SCOOP Software GmbH, cronn GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export function createUnregisterBroadCastChannelEndpoint() {
+  return new BroadcastChannel("service-worker-unregister");
+}
+
+export const UNREGISTER = "unregister";
diff --git a/employee-portal/src/serviceWorker/sw/index.ts b/employee-portal/src/serviceWorker/sw/index.ts
index 2ac90e7f6..a44f4961f 100644
--- a/employee-portal/src/serviceWorker/sw/index.ts
+++ b/employee-portal/src/serviceWorker/sw/index.ts
@@ -16,6 +16,10 @@ import {
   PAGES_CACHE_NAME,
   PAGES_RSC_CACHE_NAME,
 } from "@/serviceWorker/common/common";
+import {
+  UNREGISTER,
+  createUnregisterBroadCastChannelEndpoint,
+} from "@/serviceWorker/common/unregisterBroadCastChannel";
 import { CacheableResponsePlugin } from "@/serviceWorker/sw/CacheableResponsePlugin";
 import { EncryptPlugin } from "@/serviceWorker/sw/EncryptPlugin";
 import { RedirectOnErrorPlugin } from "@/serviceWorker/sw/RedirectOnErrorPlugin";
@@ -190,3 +194,11 @@ getGlobalSelf().addEventListener("message", (event: ExtendableMessageEvent) => {
     );
   }
 });
+
+const unregisterChannel = createUnregisterBroadCastChannelEndpoint();
+
+unregisterChannel.onmessage = async (event: MessageEvent) => {
+  if (event.data === UNREGISTER) {
+    await getGlobalSelf().registration.unregister();
+  }
+};
diff --git a/employee-portal/src/serviceWorker/sw/requestHandlers.ts b/employee-portal/src/serviceWorker/sw/requestHandlers.ts
index a160335f1..dbc6b4710 100644
--- a/employee-portal/src/serviceWorker/sw/requestHandlers.ts
+++ b/employee-portal/src/serviceWorker/sw/requestHandlers.ts
@@ -34,7 +34,7 @@ export function getApiPatchHandler(
     try {
       return await Promise.race([
         fetch(request.clone()),
-        new Promise<Response>((_, reject) =>
+        new Promise<Response>((_resolve, reject) =>
           setTimeout(
             () => reject(new Error("Request timed out")),
             NETWORK_TIMEOUT_IN_SECONDS * 1000,
@@ -67,7 +67,7 @@ export function getApiDeleteHandler(
     try {
       return await Promise.race([
         fetch(new Request(request, { body: null })),
-        new Promise<Response>((_, reject) =>
+        new Promise<Response>((_resolve, reject) =>
           setTimeout(
             () => reject(new Error("Request timed out")),
             NETWORK_TIMEOUT_IN_SECONDS * 1000,
diff --git a/lib-portal/package.json b/lib-portal/package.json
index f8a7132ab..2eee54872 100644
--- a/lib-portal/package.json
+++ b/lib-portal/package.json
@@ -8,25 +8,37 @@
   },
   "dependencies": {
     "@eshg/employee-portal-api": "workspace:*",
-    "@mui/icons-material": "5.16.7",
-    "@mui/joy": "5.0.0-beta.48",
-    "@mui/material": "npm:@mui/joy@5.0.0-beta.48",
-    "@tanstack/react-query": "5.59.10",
-    "next": "14.2.14",
-    "react": "18.3.1",
-    "react-dom": "18.3.1",
-    "react-error-boundary": "4.0.13",
-    "uuid": "10.0.0"
+    "@mui/icons-material": "catalog:joy",
+    "@mui/joy": "catalog:joy",
+    "@mui/material": "catalog:joy",
+    "@tanstack/react-query": "catalog:common",
+    "date-fns": "catalog:common",
+    "formik": "catalog:common",
+    "next": "catalog:next",
+    "react": "catalog:react",
+    "react-dom": "catalog:react",
+    "remeda": "catalog:common",
+    "uuid": "catalog:common"
   },
   "devDependencies": {
-    "@tanstack/eslint-plugin-query": "5.59.7",
-    "@types/react": "18.3.11",
-    "@types/react-dom": "18.3.1",
-    "@types/uuid": "10.0.0",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint-config-next": "14.2.14",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@types/react": "catalog:react",
+    "@types/react-dom": "catalog:react",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-config-next": "catalog:next",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
index 8dd47c248..152023cd4 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentCalendar.tsx
@@ -38,7 +38,10 @@ export function AppointmentCalendar({
   prevMonthLabel,
 }: AppointmentCalendarProps) {
   return (
-    <div style={{ width: "min-content" }}>
+    <div
+      style={{ width: "min-content" }}
+      data-testid="appointment-picker-calender"
+    >
       <Row justifyContent="space-around">
         <MonthSelection
           currentMonth={currentMonth}
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
index d7ad137e8..ec3bd622e 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx
@@ -87,7 +87,6 @@ export function AppointmentListForDate<T extends Appointment>({
         wrap
         size="sm"
         sx={{ marginBottom: "16px", gap: "8px", padding: 0 }}
-        // eslint-disable-next-line jsx-a11y/aria-props
         aria-description={description}
       >
         {appointments.map((apt) => {
diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
index 4426081e3..02566ffed 100644
--- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
+++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentPickerField.tsx
@@ -127,7 +127,11 @@ export function AppointmentPickerField<T extends Appointment>({
         />
       }
       appointmentList={
-        <FormControl error={field.error} required={field.required}>
+        <FormControl
+          error={field.error}
+          required={field.required}
+          data-testid="appointment-picker-slots"
+        >
           <AppointmentList
             {...listProps}
             field={field}
@@ -153,7 +157,7 @@ function DefaultLayout({
   appointmentList,
 }: AppointmentPickerLayoutProps) {
   return (
-    <Stack sx={sx} className={className}>
+    <Stack sx={sx} className={className} data-testid="appointment-picker">
       {calendar}
       {appointmentList}
     </Stack>
diff --git a/lib-portal/src/errorHandling/AlertContext.tsx b/lib-portal/src/errorHandling/AlertContext.tsx
index 2488e02d9..b2f2de746 100644
--- a/lib-portal/src/errorHandling/AlertContext.tsx
+++ b/lib-portal/src/errorHandling/AlertContext.tsx
@@ -5,7 +5,15 @@
 
 "use client";
 
-import { ReactNode, createContext, useContext, useMemo, useState } from "react";
+import {
+  Fragment,
+  ReactNode,
+  createContext,
+  useContext,
+  useEffect,
+  useMemo,
+  useState,
+} from "react";
 import { doNothing, isDefined } from "remeda";
 
 import { Alert, AlertProps } from "../components/Alert";
@@ -13,12 +21,12 @@ import { useUuid } from "../hooks/useUuid";
 import { RequiresChildren } from "../types/react";
 
 interface AlertContextValue {
-  state: AlertState | null;
+  alerts: AlertInstance[];
   open: (alertId: string, props: AlertProps, options?: AlertOptions) => void;
   close: (alertid?: string) => void;
 }
 
-interface AlertState {
+interface AlertInstance {
   alertId: string;
   props: AlertProps;
   options: AlertOptions;
@@ -31,7 +39,7 @@ interface AlertOptions {
 const AlertContext = createContext<AlertContextValue | null>(null);
 
 export function AlertContextProvider(props: RequiresChildren) {
-  const [state, setState] = useState<AlertState | null>(null);
+  const [alerts, setAlerts] = useState<AlertInstance[]>([]);
 
   const contextValue = useMemo(() => {
     function open(
@@ -39,31 +47,53 @@ export function AlertContextProvider(props: RequiresChildren) {
       props: AlertProps,
       options: AlertOptions = {},
     ): void {
-      setState({
-        alertId,
-        props,
-        options,
+      setAlerts((prevAlerts) => {
+        const alertIndex = prevAlerts.findIndex(
+          (alert) => alert.alertId === alertId,
+        );
+        const alertInstance: AlertInstance = {
+          alertId,
+          props,
+          options,
+        };
+
+        if (alertIndex >= 0) {
+          // replace previous alert
+          return prevAlerts.toSpliced(alertIndex, 1, alertInstance);
+        } else {
+          // insert alert at the start
+          return [alertInstance, ...prevAlerts];
+        }
       });
     }
 
     function close(alertId?: string): void {
-      if (state === null) {
+      if (
+        alerts.length === 0 ||
+        (isDefined(alertId) &&
+          alerts.every((alert) => alert.alertId !== alertId))
+      ) {
         return;
       }
 
-      if (isDefined(alertId) && alertId !== state.alertId) {
+      // close all alerts
+      if (alertId === undefined) {
+        setAlerts([]);
         return;
       }
 
-      setState(null);
+      // close specified alert
+      setAlerts((prevAlerts) =>
+        prevAlerts.filter((alert) => alert.alertId !== alertId),
+      );
     }
 
     return {
-      state,
+      alerts,
       open,
       close,
     };
-  }, [state, setState]);
+  }, [alerts, setAlerts]);
 
   return (
     <AlertContext.Provider value={contextValue}>
@@ -94,6 +124,9 @@ interface AlertOpenOptions
   extends Pick<AlertProps, "title" | "message" | "action">,
     AlertOptions {}
 
+/**
+ * Creates an alert instance to open alerts of different types
+ */
 export function useAlert(): UseAlertResult {
   const alertContext = useAlertContext();
   const alertId = useUuid();
@@ -118,7 +151,7 @@ export function useAlert(): UseAlertResult {
   }
 
   return {
-    isOpen: alertContext.state?.alertId === alertId,
+    isOpen: alertContext.alerts.some((alert) => alert.alertId === alertId),
     notification: (options) => openWithColor("primary", options),
     warning: (options) => openWithColor("warning", options),
     error: (options) => openWithColor("danger", options),
@@ -126,6 +159,33 @@ export function useAlert(): UseAlertResult {
   };
 }
 
+interface UseControlledAlertOptions extends AlertOpenOptions {
+  type: AlertType;
+  open: boolean;
+}
+
+type AlertType = "notification" | "warning" | "error";
+
+/**
+ * Creates a controlled alert instance which automatically opens and closes itself
+ */
+export function useControlledAlert(options: UseControlledAlertOptions): void {
+  const { type, open, ...alertOptions } = options;
+  const alert = useAlert();
+
+  useEffect(() => {
+    if (open === alert.isOpen) {
+      return;
+    }
+
+    if (open) {
+      alert[type](alertOptions);
+    } else {
+      alert.close();
+    }
+  }, [open, type, alertOptions, alert]);
+}
+
 export function useResetAlertContext(): () => void {
   // TODO: replace by useAlertContext when all usages are within a QueryBoundary
   const alertContext = useContext(AlertContext);
@@ -142,30 +202,34 @@ interface AlertSlotProps extends Pick<AlertProps, "sx"> {
 }
 
 export function AlertSlot(props: AlertSlotProps) {
-  const { container: Container, ...alertProps } = props;
+  const { container, ...commonAlertProps } = props;
   const alertContext = useContext(AlertContext);
 
   if (alertContext === null) {
     return null;
   }
 
-  const alertState = alertContext.state ?? null;
+  const alerts = alertContext.alerts ?? [];
 
-  if (alertState === null) {
+  if (alerts.length === 0) {
     return null;
   }
 
-  const { closeable } = alertState.options;
-
-  const alert = (
-    <Alert
-      {...alertProps}
-      {...alertState.props}
-      onClose={
-        closeable ? () => alertContext.close(alertState.alertId) : undefined
-      }
-    />
+  const Container = container ?? Fragment;
+  return (
+    <Container>
+      {alerts.map((alert) => (
+        <Alert
+          {...commonAlertProps}
+          {...alert.props}
+          key={alert.alertId}
+          onClose={
+            alert.options.closeable
+              ? () => alertContext.close(alert.alertId)
+              : undefined
+          }
+        />
+      ))}
+    </Container>
   );
-
-  return isDefined(Container) ? <Container>{alert}</Container> : alert;
 }
diff --git a/lib-portal/src/errorHandling/errorMappers.tsx b/lib-portal/src/errorHandling/errorMappers.tsx
index 3740b2c2c..b690f3cf3 100644
--- a/lib-portal/src/errorHandling/errorMappers.tsx
+++ b/lib-portal/src/errorHandling/errorMappers.tsx
@@ -4,13 +4,10 @@
  */
 
 import { Button } from "@mui/joy";
-import { useQueryClient } from "@tanstack/react-query";
-import { useRouter } from "next/navigation";
 import { PropsWithChildren, ReactNode } from "react";
 
 import { ActionButtonProps } from "../components/Alert";
 
-import { useResetAlertContext } from "./AlertContext";
 import { PortalErrorCode } from "./PortalErrorCode";
 
 interface ErrorDescription {
@@ -119,15 +116,14 @@ function renderRetryButton(onReset: (() => void) | undefined) {
 interface ReloadButtonProps extends ActionButtonProps, PropsWithChildren {}
 
 function ReloadButton(props: ReloadButtonProps) {
-  const router = useRouter();
-  const queryClient = useQueryClient();
-  const resetAlertContext = useResetAlertContext();
   const { children, ...buttonProps } = props;
 
   function handleReload() {
-    resetAlertContext();
-    void queryClient.invalidateQueries();
-    router.refresh();
+    /**
+     * Use browser reload instead of Next.js router.reload() to force displaying suspense fallbacks for refetched queries.
+     * Invalidating queries will trigger background refetches, which do not display the suspense fallbacks.
+     */
+    window.location.reload();
   }
 
   return (
diff --git a/lib-portal/src/formatters/numbers.ts b/lib-portal/src/formatters/numbers.ts
new file mode 100644
index 000000000..71d07ab48
--- /dev/null
+++ b/lib-portal/src/formatters/numbers.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2024 cronn GmbH
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export enum LOCALE_OPTION {
+  auto,
+  manual,
+}
+
+interface AutoLocale {
+  localOption: LOCALE_OPTION.auto;
+}
+interface ManualLocale {
+  localOption: LOCALE_OPTION.manual;
+  locale: string;
+}
+
+export function formatCurrency(
+  fee: number | undefined,
+  option: AutoLocale | ManualLocale,
+) {
+  if (!fee) {
+    return;
+  }
+  if (option.localOption === LOCALE_OPTION.manual) {
+    return Intl.NumberFormat(option.locale, {
+      style: "currency",
+      currency: "EUR",
+    }).format(fee);
+  } else {
+    const browserLocale = navigator.language;
+    return Intl.NumberFormat(browserLocale, {
+      style: "currency",
+      currency: "EUR",
+    }).format(fee);
+  }
+}
diff --git a/lib-portal/src/hooks/useUuid.ts b/lib-portal/src/hooks/useUuid.ts
index fe3300bd2..ef8bda171 100644
--- a/lib-portal/src/hooks/useUuid.ts
+++ b/lib-portal/src/hooks/useUuid.ts
@@ -7,6 +7,6 @@ import { useState } from "react";
 import { v4 as uuidv4 } from "uuid";
 
 export function useUuid(): string {
-  const [uuid] = useState(uuidv4);
+  const [uuid] = useState(() => uuidv4());
   return uuid;
 }
diff --git a/package.json b/package.json
index 77cb9b78c..a83219935 100644
--- a/package.json
+++ b/package.json
@@ -3,26 +3,26 @@
   "version": "0.1.0",
   "type": "module",
   "private": true,
-  "dependencies": {
-    "date-fns": "4.1.0",
-    "remeda": "2.15.0"
-  },
   "devDependencies": {
-    "@cyclonedx/cdxgen": "10.10.4",
-    "@eslint/compat": "1.2.1",
-    "@eslint/eslintrc": "3.1.0",
-    "@trivago/prettier-plugin-sort-imports": "4.3.0",
-    "@types/node": "20.16.11",
-    "@vitejs/plugin-react": "4.3.2",
-    "@vitest/coverage-istanbul": "2.1.2",
-    "eslint": "9.13.0",
-    "eslint-config-prettier": "9.1.0",
-    "eslint-plugin-import": "2.31.0",
-    "eslint-plugin-unused-imports": "4.1.4",
-    "prettier": "3.3.3",
-    "typescript": "5.6.3",
-    "typescript-eslint": "8.10.0",
-    "vite-tsconfig-paths": "5.0.1",
-    "vitest": "2.1.2"
+    "@cyclonedx/cdxgen": "10.10.7",
+    "@eslint/compat": "catalog:eslint",
+    "@eslint/eslintrc": "catalog:eslint",
+    "@eslint/js": "catalog:eslint",
+    "@tanstack/eslint-plugin-query": "catalog:common",
+    "@trivago/prettier-plugin-sort-imports": "catalog:prettier",
+    "@types/node": "catalog:common",
+    "@vitejs/plugin-react": "catalog:vitest",
+    "@vitest/coverage-istanbul": "catalog:vitest",
+    "eslint": "catalog:eslint",
+    "eslint-config-prettier": "catalog:eslint",
+    "eslint-plugin-import": "catalog:eslint",
+    "eslint-plugin-unused-imports": "catalog:eslint",
+    "eslint-plugin-promise": "catalog:eslint",
+    "next": "catalog:next",
+    "prettier": "catalog:prettier",
+    "typescript": "catalog:common",
+    "typescript-eslint": "catalog:eslint",
+    "vite-tsconfig-paths": "catalog:vitest",
+    "vitest": "catalog:vitest"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b32048942..baa1cebc5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,267 +7,522 @@ settings:
   autoInstallPeers: true
   excludeLinksFromLockfile: false
 
+catalogs:
+  common:
+    '@tanstack/eslint-plugin-query':
+      specifier: 5.59.7
+      version: 5.59.7
+    '@tanstack/react-query':
+      specifier: 5.59.16
+      version: 5.59.16
+    '@tanstack/react-table':
+      specifier: 8.20.5
+      version: 8.20.5
+    '@types/node':
+      specifier: 20.17.1
+      version: 20.17.1
+    date-fns:
+      specifier: 4.1.0
+      version: 4.1.0
+    formik:
+      specifier: 2.4.6
+      version: 2.4.6
+    react-error-boundary:
+      specifier: 4.1.2
+      version: 4.1.2
+    remeda:
+      specifier: 2.16.0
+      version: 2.16.0
+    server-only:
+      specifier: 0.0.1
+      version: 0.0.1
+    typescript:
+      specifier: 5.6.3
+      version: 5.6.3
+    use-debounce:
+      specifier: 10.0.4
+      version: 10.0.4
+    uuid:
+      specifier: 11.0.1
+      version: 11.0.1
+    valibot:
+      specifier: 0.42.1
+      version: 0.42.1
+  eslint:
+    '@eslint/compat':
+      specifier: 1.2.1
+      version: 1.2.1
+    '@eslint/eslintrc':
+      specifier: 3.1.0
+      version: 3.1.0
+    '@eslint/js':
+      specifier: 9.13.0
+      version: 9.13.0
+    eslint:
+      specifier: 9.13.0
+      version: 9.13.0
+    eslint-config-prettier:
+      specifier: 9.1.0
+      version: 9.1.0
+    eslint-plugin-import:
+      specifier: 2.31.0
+      version: 2.31.0
+    eslint-plugin-promise:
+      specifier: 7.1.0
+      version: 7.1.0
+    eslint-plugin-unused-imports:
+      specifier: 4.1.4
+      version: 4.1.4
+    typescript-eslint:
+      specifier: 8.11.0
+      version: 8.11.0
+  fullcalendar:
+    '@fullcalendar/core':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/daygrid':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/interaction':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/list':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/react':
+      specifier: 6.1.15
+      version: 6.1.15
+    '@fullcalendar/timegrid':
+      specifier: 6.1.15
+      version: 6.1.15
+  i18next:
+    i18next:
+      specifier: 23.16.4
+      version: 23.16.4
+    i18next-resources-to-backend:
+      specifier: 1.2.1
+      version: 1.2.1
+    react-i18next:
+      specifier: 15.1.0
+      version: 15.1.0
+  joy:
+    '@emotion/react':
+      specifier: 11.13.3
+      version: 11.13.3
+    '@emotion/styled':
+      specifier: 11.13.0
+      version: 11.13.0
+    '@fontsource/poppins':
+      specifier: 5.1.0
+      version: 5.1.0
+    '@mui/icons-material':
+      specifier: 5.16.7
+      version: 5.16.7
+    '@mui/joy':
+      specifier: 5.0.0-beta.48
+      version: 5.0.0-beta.48
+    '@mui/material':
+      specifier: npm:@mui/joy@5.0.0-beta.48
+      version: 5.0.0-beta.48
+  next:
+    '@next/bundle-analyzer':
+      specifier: 14.2.14
+      version: 14.2.14
+    eslint-config-next:
+      specifier: 14.2.14
+      version: 14.2.14
+    next:
+      specifier: 14.2.14
+      version: 14.2.14
+  prettier:
+    '@trivago/prettier-plugin-sort-imports':
+      specifier: 4.3.0
+      version: 4.3.0
+    prettier:
+      specifier: 3.3.3
+      version: 3.3.3
+  react:
+    '@types/react':
+      specifier: 18.3.12
+      version: 18.3.12
+    '@types/react-dom':
+      specifier: 18.3.1
+      version: 18.3.1
+    react:
+      specifier: 18.3.1
+      version: 18.3.1
+    react-dom:
+      specifier: 18.3.1
+      version: 18.3.1
+  vitest:
+    '@vitejs/plugin-react':
+      specifier: 4.3.3
+      version: 4.3.3
+    '@vitest/coverage-istanbul':
+      specifier: 2.1.4
+      version: 2.1.4
+    vite-tsconfig-paths:
+      specifier: 5.0.1
+      version: 5.0.1
+    vitest:
+      specifier: 2.1.4
+      version: 2.1.4
+
 importers:
 
   .:
-    dependencies:
-      date-fns:
-        specifier: 4.1.0
-        version: 4.1.0
-      remeda:
-        specifier: 2.15.0
-        version: 2.15.0
     devDependencies:
       '@cyclonedx/cdxgen':
-        specifier: 10.10.4
-        version: 10.10.4
+        specifier: 10.10.7
+        version: 10.10.7
       '@eslint/compat':
-        specifier: 1.2.1
+        specifier: catalog:eslint
         version: 1.2.1(eslint@9.13.0)
       '@eslint/eslintrc':
-        specifier: 3.1.0
+        specifier: catalog:eslint
         version: 3.1.0
+      '@eslint/js':
+        specifier: catalog:eslint
+        version: 9.13.0
+      '@tanstack/eslint-plugin-query':
+        specifier: catalog:common
+        version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
       '@trivago/prettier-plugin-sort-imports':
-        specifier: 4.3.0
+        specifier: catalog:prettier
         version: 4.3.0(prettier@3.3.3)
       '@types/node':
-        specifier: 20.16.11
-        version: 20.16.11
+        specifier: catalog:common
+        version: 20.17.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
       eslint:
-        specifier: 9.13.0
+        specifier: catalog:eslint
         version: 9.13.0
       eslint-config-prettier:
-        specifier: 9.1.0
+        specifier: catalog:eslint
         version: 9.1.0(eslint@9.13.0)
       eslint-plugin-import:
-        specifier: 2.31.0
-        version: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
       eslint-plugin-unused-imports:
-        specifier: 4.1.4
-        version: 4.1.4(@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      next:
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       prettier:
-        specifier: 3.3.3
+        specifier: catalog:prettier
         version: 3.3.3
       typescript:
-        specifier: 5.6.3
+        specifier: catalog:common
         version: 5.6.3
       typescript-eslint:
-        specifier: 8.10.0
-        version: 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+        specifier: catalog:eslint
+        version: 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   admin-portal:
     dependencies:
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
       '@eshg/lib-portal':
         specifier: workspace:*
         version: link:../lib-portal
+      '@fontsource/poppins':
+        specifier: catalog:joy
+        version: 5.1.0
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
       '@tanstack/react-table':
-        specifier: 8.20.5
+        specifier: catalog:common
         version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       asn1js:
         specifier: 3.0.5
         version: 3.0.5
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       i18next:
-        specifier: 23.15.2
-        version: 23.15.2
+        specifier: catalog:i18next
+        version: 23.16.4
       i18next-resources-to-backend:
-        specifier: 1.2.1
+        specifier: catalog:i18next
         version: 1.2.1
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       pkijs:
         specifier: 3.2.4
         version: 3.2.4
-      pvutils:
-        specifier: 1.1.3
-        version: 1.1.3
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
       react-i18next:
-        specifier: 15.0.2
-        version: 15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:i18next
+        version: 15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
+      use-debounce:
+        specifier: catalog:common
+        version: 10.0.4(react@18.3.1)
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
       zod:
         specifier: 3.23.8
         version: 3.23.8
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   admin-portal-api: {}
 
   citizen-portal:
     dependencies:
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/citizen-portal-api':
         specifier: workspace:*
         version: link:../citizen-portal-api
       '@eshg/lib-portal':
         specifier: workspace:*
         version: link:../lib-portal
+      '@fontsource/poppins':
+        specifier: catalog:joy
+        version: 5.1.0
+      '@fullcalendar/core':
+        specifier: catalog:fullcalendar
+        version: 6.1.15
+      '@fullcalendar/daygrid':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)
+      '@fullcalendar/interaction':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)
+      '@fullcalendar/react':
+        specifier: catalog:fullcalendar
+        version: 6.1.15(@fullcalendar/core@6.1.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mdx-js/mdx':
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 3.1.0
+        version: 3.1.0(acorn@8.13.0)
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
-      '@tanstack/react-table':
-        specifier: 8.20.5
-        version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@types/negotiator':
-        specifier: 0.6.3
-        version: 0.6.3
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       i18next:
-        specifier: 23.15.2
-        version: 23.15.2
+        specifier: catalog:i18next
+        version: 23.16.4
       i18next-resources-to-backend:
-        specifier: 1.2.1
+        specifier: catalog:i18next
         version: 1.2.1
       negotiator:
-        specifier: 0.6.3
-        version: 0.6.3
+        specifier: 1.0.0
+        version: 1.0.0
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
+      react-i18next:
+        specifier: catalog:i18next
+        version: 15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       server-only:
-        specifier: 0.0.1
+        specifier: catalog:common
         version: 0.0.1
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
       '@types/mdx':
         specifier: 2.0.13
         version: 2.0.13
+      '@types/negotiator':
+        specifier: 0.6.3
+        version: 0.6.3
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   citizen-portal-api: {}
 
   e2e:
-    dependencies:
-      otpauth:
-        specifier: 9.3.4
-        version: 9.3.4
     devDependencies:
       '@axe-core/playwright':
         specifier: 4.10.0
-        version: 4.10.0(playwright-core@1.48.1)
+        version: 4.10.0(playwright-core@1.48.2)
       '@eshg/admin-portal-api':
         specifier: workspace:*
         version: link:../admin-portal-api
@@ -280,15 +535,66 @@ importers:
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@keycloak/keycloak-admin-client':
-        specifier: 26.0.0
-        version: 26.0.0
+        specifier: 26.0.2
+        version: 26.0.2
       '@playwright/test':
-        specifier: 1.48.1
-        version: 1.48.1
+        specifier: 1.48.2
+        version: 1.48.2
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
+      axe-core:
+        specifier: 4.10.2
+        version: 4.10.2
       axe-html-reporter:
         specifier: 2.2.11
-        version: 2.2.11(axe-core@4.10.0)
+        version: 2.2.11(axe-core@4.10.2)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
+      uuid:
+        specifier: catalog:common
+        version: 11.0.1
+      vite-tsconfig-paths:
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
+      vitest:
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   e2e-api: {}
 
@@ -296,16 +602,13 @@ importers:
     dependencies:
       '@ducanh2912/next-pwa':
         specifier: 10.2.9
-        version: 10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)
-      '@emotion/cache':
-        specifier: 11.13.1
-        version: 11.13.1
+        version: 10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)
       '@emotion/react':
-        specifier: 11.13.3
-        version: 11.13.3(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/styled':
-        specifier: 11.13.0
-        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@eshg/employee-portal-api':
         specifier: workspace:*
         version: link:../employee-portal-api
@@ -313,50 +616,53 @@ importers:
         specifier: workspace:*
         version: link:../lib-portal
       '@fontsource/poppins':
-        specifier: 5.1.0
+        specifier: catalog:joy
         version: 5.1.0
       '@fullcalendar/core':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15
       '@fullcalendar/daygrid':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/interaction':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/list':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@fullcalendar/react':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@fullcalendar/timegrid':
-        specifier: 6.1.15
+        specifier: catalog:fullcalendar
         version: 6.1.15(@fullcalendar/core@6.1.15)
       '@hello-pangea/dnd':
         specifier: 17.0.0
-        version: 17.0.0(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: 17.0.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mdx-js/mdx':
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 3.1.0
+        version: 3.1.0(acorn@8.13.0)
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
       '@tanstack/react-table':
-        specifier: 8.20.5
+        specifier: catalog:common
         version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       compressorjs:
         specifier: 1.2.1
         version: 1.2.1
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
       drauu:
         specifier: 0.4.1
         version: 0.4.1
@@ -366,15 +672,12 @@ importers:
       echarts-for-react:
         specifier: 3.0.2
         version: 3.0.2(echarts@5.5.1)(react@18.3.1)
-      echarts-stat:
-        specifier: 1.2.0
-        version: 1.2.0
       formik:
-        specifier: 2.4.6
+        specifier: catalog:common
         version: 2.4.6(react@18.3.1)
       hpke-js:
-        specifier: 1.4.3
-        version: 1.4.3
+        specifier: 1.5.0
+        version: 1.5.0
       iso8601-duration:
         specifier: 2.1.2
         version: 2.1.2
@@ -382,72 +685,111 @@ importers:
         specifier: 34.3.1
         version: 34.3.1
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
       react-error-boundary:
-        specifier: 4.0.13
-        version: 4.0.13(react@18.3.1)
+        specifier: catalog:common
+        version: 4.1.2(react@18.3.1)
       react-infinite-scroll-hook:
         specifier: 5.0.1
         version: 5.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      react-transition-group:
-        specifier: 4.4.5
-        version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       server-only:
-        specifier: 0.0.1
+        specifier: catalog:common
         version: 0.0.1
       use-debounce:
-        specifier: 10.0.3
-        version: 10.0.3(react@18.3.1)
+        specifier: catalog:common
+        version: 10.0.4(react@18.3.1)
       uuid:
-        specifier: 10.0.0
-        version: 10.0.0
+        specifier: catalog:common
+        version: 11.0.1
       valibot:
-        specifier: 0.42.1
+        specifier: catalog:common
         version: 0.42.1(typescript@5.6.3)
+      workbox-background-sync:
+        specifier: 7.1.0
+        version: 7.1.0
+      workbox-core:
+        specifier: 7.1.0
+        version: 7.1.0
+      workbox-window:
+        specifier: 7.1.0
+        version: 7.1.0
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@next/bundle-analyzer':
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
       '@types/mdx':
         specifier: 2.0.13
         version: 2.0.13
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       '@types/react-transition-group':
         specifier: 4.4.11
         version: 4.4.11
-      '@types/uuid':
-        specifier: 10.0.0
-        version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   employee-portal-api: {}
 
@@ -457,60 +799,96 @@ importers:
         specifier: workspace:*
         version: link:../employee-portal-api
       '@mui/icons-material':
-        specifier: 5.16.7
-        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
       '@mui/joy':
-        specifier: 5.0.0-beta.48
-        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:joy
+        version: 5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/material':
-        specifier: npm:@mui/joy@5.0.0-beta.48
-        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+        specifier: catalog:joy
+        version: '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       '@tanstack/react-query':
-        specifier: 5.59.10
-        version: 5.59.10(react@18.3.1)
+        specifier: catalog:common
+        version: 5.59.16(react@18.3.1)
+      date-fns:
+        specifier: catalog:common
+        version: 4.1.0
+      formik:
+        specifier: catalog:common
+        version: 2.4.6(react@18.3.1)
       next:
-        specifier: 14.2.14
-        version: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: catalog:next
+        version: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       react:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
       react-dom:
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1(react@18.3.1)
-      react-error-boundary:
-        specifier: 4.0.13
-        version: 4.0.13(react@18.3.1)
+      remeda:
+        specifier: catalog:common
+        version: 2.16.0
       uuid:
-        specifier: 10.0.0
-        version: 10.0.0
+        specifier: catalog:common
+        version: 11.0.1
     devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
       '@tanstack/eslint-plugin-query':
-        specifier: 5.59.7
+        specifier: catalog:common
         version: 5.59.7(eslint@9.13.0)(typescript@5.6.3)
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
       '@types/react':
-        specifier: 18.3.11
-        version: 18.3.11
+        specifier: catalog:react
+        version: 18.3.12
       '@types/react-dom':
-        specifier: 18.3.1
+        specifier: catalog:react
         version: 18.3.1
-      '@types/uuid':
-        specifier: 10.0.0
-        version: 10.0.0
       '@vitejs/plugin-react':
-        specifier: 4.3.2
-        version: 4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       '@vitest/coverage-istanbul':
-        specifier: 2.1.2
-        version: 2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
       eslint-config-next:
-        specifier: 14.2.14
+        specifier: catalog:next
         version: 14.2.14(eslint@9.13.0)(typescript@5.6.3)
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
       vite-tsconfig-paths:
-        specifier: 5.0.1
-        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
+        specifier: catalog:vitest
+        version: 5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
       vitest:
-        specifier: 2.1.2
-        version: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+        specifier: catalog:vitest
+        version: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
 
   performance-test:
     dependencies:
@@ -521,20 +899,54 @@ importers:
         specifier: workspace:*
         version: link:../employee-portal-api
       '@faker-js/faker':
-        specifier: 9.0.3
-        version: 9.0.3
+        specifier: 9.1.0
+        version: 9.1.0
       '@grafana/schema':
-        specifier: 11.2.2
-        version: 11.2.2
+        specifier: 11.3.0
+        version: 11.3.0
       '@keycloak/keycloak-admin-client':
-        specifier: 26.0.0
-        version: 26.0.0
+        specifier: 26.0.2
+        version: 26.0.2
       '@types/k6':
         specifier: 0.54.1
         version: 0.54.1
       tsx:
-        specifier: 4.19.1
-        version: 4.19.1
+        specifier: 4.19.2
+        version: 4.19.2
+    devDependencies:
+      '@eslint/compat':
+        specifier: catalog:eslint
+        version: 1.2.1(eslint@9.13.0)
+      '@eslint/eslintrc':
+        specifier: catalog:eslint
+        version: 3.1.0
+      '@trivago/prettier-plugin-sort-imports':
+        specifier: catalog:prettier
+        version: 4.3.0(prettier@3.3.3)
+      '@types/node':
+        specifier: catalog:common
+        version: 20.17.1
+      eslint:
+        specifier: catalog:eslint
+        version: 9.13.0
+      eslint-config-prettier:
+        specifier: catalog:eslint
+        version: 9.1.0(eslint@9.13.0)
+      eslint-plugin-import:
+        specifier: catalog:eslint
+        version: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      eslint-plugin-promise:
+        specifier: catalog:eslint
+        version: 7.1.0(eslint@9.13.0)
+      eslint-plugin-unused-imports:
+        specifier: catalog:eslint
+        version: 4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)
+      prettier:
+        specifier: catalog:prettier
+        version: 3.3.3
+      typescript:
+        specifier: catalog:common
+        version: 5.6.3
 
 packages:
 
@@ -562,10 +974,6 @@ packages:
     peerDependencies:
       playwright-core: '>= 1.0.0'
 
-  '@babel/code-frame@7.24.7':
-    resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/code-frame@7.25.7':
     resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
     engines: {node: '>=6.9.0'}
@@ -578,10 +986,6 @@ packages:
     resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/core@7.24.7':
-    resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/core@7.25.8':
     resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==}
     engines: {node: '>=6.9.0'}
@@ -590,14 +994,6 @@ packages:
     resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/generator@7.24.7':
-    resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/generator@7.25.6':
-    resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/generator@7.25.7':
     resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
     engines: {node: '>=6.9.0'}
@@ -651,20 +1047,10 @@ packages:
     resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-module-imports@7.24.7':
-    resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-module-imports@7.25.7':
     resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-module-transforms@7.24.7':
-    resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-
   '@babel/helper-module-transforms@7.25.7':
     resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
     engines: {node: '>=6.9.0'}
@@ -707,22 +1093,10 @@ packages:
     resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-string-parser@7.24.7':
-    resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/helper-string-parser@7.24.8':
-    resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-string-parser@7.25.7':
     resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-validator-identifier@7.24.7':
-    resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helper-validator-identifier@7.25.7':
     resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
     engines: {node: '>=6.9.0'}
@@ -739,32 +1113,14 @@ packages:
     resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helpers@7.24.7':
-    resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/helpers@7.25.7':
     resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/highlight@7.24.7':
-    resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/highlight@7.25.7':
     resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/parser@7.24.7':
-    resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-
-  '@babel/parser@7.25.6':
-    resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-
   '@babel/parser@7.25.8':
     resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
     engines: {node: '>=6.0.0'}
@@ -1210,26 +1566,10 @@ packages:
   '@babel/regjsgen@0.8.0':
     resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
 
-  '@babel/runtime@7.24.7':
-    resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/runtime@7.25.4':
-    resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/runtime@7.25.6':
     resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/template@7.24.7':
-    resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/template@7.25.0':
-    resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/template@7.25.7':
     resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
     engines: {node: '>=6.9.0'}
@@ -1238,14 +1578,6 @@ packages:
     resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/traverse@7.24.7':
-    resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/traverse@7.25.6':
-    resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/traverse@7.25.7':
     resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
     engines: {node: '>=6.9.0'}
@@ -1254,14 +1586,6 @@ packages:
     resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/types@7.24.7':
-    resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
-    engines: {node: '>=6.9.0'}
-
-  '@babel/types@7.25.6':
-    resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
-    engines: {node: '>=6.9.0'}
-
   '@babel/types@7.25.8':
     resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==}
     engines: {node: '>=6.9.0'}
@@ -1303,8 +1627,8 @@ packages:
     resolution: {integrity: sha512-AQWpffIGygeQIr1f7QC1rXcp3O7DgYGQFZC9Jlwx0Bv/e8VKaw8nYoTNMukUms87bw8FC0jo3CWaje7iTX5svQ==}
     cpu: [x64]
 
-  '@cyclonedx/cdxgen@10.10.4':
-    resolution: {integrity: sha512-H8QG91GPfuT5N3jpU6UZDwSHGlIABS/iiKP0X4soe9yQle2K0O1mcGxotyKBbyw3HdXRF22DGXE2L/wAaho9aw==}
+  '@cyclonedx/cdxgen@10.10.7':
+    resolution: {integrity: sha512-CCK3BWIRGR7exUETeH+VZR4VkUOXlAd13pczbH5kBi7LnBYc54QNrMx5XAywvlrAVPadT3BbEpz1E+EJ0QdYBw==}
     engines: {node: '>=20'}
     hasBin: true
 
@@ -1663,10 +1987,6 @@ packages:
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
 
-  '@eslint-community/regexpp@4.10.1':
-    resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==}
-    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-
   '@eslint-community/regexpp@4.11.1':
     resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -1704,8 +2024,8 @@ packages:
     resolution: {integrity: sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@faker-js/faker@9.0.3':
-    resolution: {integrity: sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==}
+  '@faker-js/faker@9.1.0':
+    resolution: {integrity: sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==}
     engines: {node: '>=18.0.0', npm: '>=9.0.0'}
 
   '@floating-ui/core@1.6.2':
@@ -1759,8 +2079,8 @@ packages:
   '@gar/promisify@1.1.3':
     resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
 
-  '@grafana/schema@11.2.2':
-    resolution: {integrity: sha512-x5+FY3tx1DlxwQ9OsS5cgr4GFh7/FjV+sub1dQJQvV/ujkRmlO5Sp/cEVMZd83BZ6O+5bJfSOeE1WKw+Rha/OQ==}
+  '@grafana/schema@11.3.0':
+    resolution: {integrity: sha512-KOgk0omDs8URuhL++ksgMDPMB49di8oCKqMNI10z3zSQniECUow+3Ggz1WeenReL7ajyJVG/O3rGVEaqteDMzg==}
 
   '@hello-pangea/dnd@17.0.0':
     resolution: {integrity: sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==}
@@ -1808,6 +2128,10 @@ packages:
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
     engines: {node: '>=12'}
 
+  '@isaacs/fs-minipass@4.0.1':
+    resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+    engines: {node: '>=18.0.0'}
+
   '@isaacs/string-locale-compare@1.1.0':
     resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==}
 
@@ -1836,8 +2160,8 @@ packages:
   '@jridgewell/trace-mapping@0.3.25':
     resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
-  '@keycloak/keycloak-admin-client@26.0.0':
-    resolution: {integrity: sha512-fKWJ71hQKPG/vyT1YSLxjZBJ0c1CvXIQh3mssciG8uuavApbmxBMF+zZLx3gZ9VZPS24YqU/uzj/Im+AnbXStw==}
+  '@keycloak/keycloak-admin-client@26.0.2':
+    resolution: {integrity: sha512-FFXIZRR9rssm1SiCz2XngotxrTiX5MSmNlJ0ArbocXliXz1ArYc+56uuED19EkBTb8ghSeYrMCFyTnyrSZMOxQ==}
     engines: {node: '>=18'}
 
   '@matrix-org/matrix-sdk-crypto-wasm@7.0.0':
@@ -1847,8 +2171,8 @@ packages:
   '@matrix-org/olm@3.2.15':
     resolution: {integrity: sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==}
 
-  '@mdx-js/mdx@3.0.1':
-    resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==}
+  '@mdx-js/mdx@3.1.0':
+    resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==}
 
   '@mui/base@5.0.0-beta.40':
     resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==}
@@ -2046,9 +2370,13 @@ packages:
     resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@npmcli/arborist@7.5.4':
-    resolution: {integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/agent@3.0.0':
+    resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  '@npmcli/arborist@8.0.0':
+    resolution: {integrity: sha512-APDXxtXGSftyXibl0dZ3CuZYmmVnkiN3+gkqwXshY4GKC2rof2+Lg0sGuj6H1p2YfBAKd7PRwuMVhu6Pf/nQ/A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
   '@npmcli/fs@1.1.1':
@@ -2058,62 +2386,66 @@ packages:
     resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  '@npmcli/git@5.0.7':
-    resolution: {integrity: sha512-WaOVvto604d5IpdCRV2KjQu8PzkfE96d50CQGKgywXh2GxXmDeUO5EWcBC4V57uFyrNqx83+MewuJh3WTR3xPA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/fs@4.0.0':
+    resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/installed-package-contents@2.1.0':
-    resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/git@6.0.1':
+    resolution: {integrity: sha512-BBWMMxeQzalmKadyimwb2/VVQyJB01PH0HhVSNLHNBDZN/M/h/02P6f8fxedIiFhpMj11SO9Ep5tKTBE7zL2nw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  '@npmcli/installed-package-contents@3.0.0':
+    resolution: {integrity: sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
-  '@npmcli/map-workspaces@3.0.6':
-    resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/map-workspaces@4.0.1':
+    resolution: {integrity: sha512-g5H8ljH7Z+4T1ASsfcL09gZl4YGw6M4GbjzPt6HgE+pCRSKC4nlNc4nY75zshi88eEHcdoh3Q8XgWFkGKoVOPw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/metavuln-calculator@7.1.1':
-    resolution: {integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/metavuln-calculator@8.0.1':
+    resolution: {integrity: sha512-WXlJx9cz3CfHSt9W9Opi1PTFc4WZLFomm5O8wekxQZmkyljrBRwATwDxfC9iOXJwYVmfiW1C1dUe0W2aN0UrSg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@npmcli/move-file@1.1.2':
     resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
     engines: {node: '>=10'}
     deprecated: This functionality has been moved to @npmcli/fs
 
-  '@npmcli/name-from-folder@2.0.0':
-    resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/name-from-folder@3.0.0':
+    resolution: {integrity: sha512-61cDL8LUc9y80fXn+lir+iVt8IS0xHqEKwPu/5jCjxQTVoSCmkXvw4vbMrzAMtmghz3/AkiBjhHkDKUH+kf7kA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/node-gyp@3.0.0':
-    resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/node-gyp@4.0.0':
+    resolution: {integrity: sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/package-json@5.2.0':
-    resolution: {integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/package-json@6.0.1':
+    resolution: {integrity: sha512-YW6PZ99sc1Q4DINEY2td5z9Z3rwbbsx7CyCnOc7UXUUdePXh5gPi1UeaoQVmKQMVbIU7aOwX2l1OG5ZfjgGi5g==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/promise-spawn@7.0.2':
-    resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/promise-spawn@8.0.2':
+    resolution: {integrity: sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/query@3.1.0':
-    resolution: {integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/query@4.0.0':
+    resolution: {integrity: sha512-3pPbese0fbCiFJ/7/X1GBgxAKYFE8sxBddA7GtuRmOgNseH4YbGsXJ807Ig3AEwNITjDUISHglvy89cyDJnAwA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/redact@2.0.1':
-    resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/redact@3.0.0':
+    resolution: {integrity: sha512-/1uFzjVcfzqrgCeGW7+SZ4hv0qLWmKXVzFahZGJ6QuJBj6Myt9s17+JL86i76NV9YSnJRcGXJYQbAU0rn1YTCQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@npmcli/run-script@8.1.0':
-    resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/run-script@9.0.1':
+    resolution: {integrity: sha512-q9C0uHrb6B6cm3qXVM32UmpqTKuFGbtP23O2K5sLvPMz2hilKd0ptqGXSpuunOuOmPQb/aT5F/kCXFc1P2gO/A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@pkgjs/parseargs@0.11.0':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
-  '@playwright/test@1.48.1':
-    resolution: {integrity: sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==}
+  '@playwright/test@1.48.2':
+    resolution: {integrity: sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -2261,32 +2593,32 @@ packages:
   '@sec-ant/readable-stream@0.4.1':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
 
-  '@sigstore/bundle@2.3.2':
-    resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/bundle@3.0.0':
+    resolution: {integrity: sha512-XDUYX56iMPAn/cdgh/DTJxz5RWmqKV4pwvUAEKEWJl+HzKdCd/24wUa9JYNMlDSCb7SUHAdtksxYX779Nne/Zg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/core@1.1.0':
-    resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/core@2.0.0':
+    resolution: {integrity: sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@sigstore/protobuf-specs@0.3.2':
     resolution: {integrity: sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@sigstore/sign@2.3.2':
-    resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/sign@3.0.0':
+    resolution: {integrity: sha512-UjhDMQOkyDoktpXoc5YPJpJK6IooF2gayAr5LvXI4EL7O0vd58okgfRcxuaH+YTdhvb5aa1Q9f+WJ0c2sVuYIw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/tuf@2.3.4':
-    resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/tuf@3.0.0':
+    resolution: {integrity: sha512-9Xxy/8U5OFJu7s+OsHzI96IX/OzjF/zj0BSSaWhgJgTqtlBhQIV2xdrQI5qxLD7+CWWDepadnXAxzaZ3u9cvRw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sigstore/verify@1.2.1':
-    resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@sigstore/verify@2.0.0':
+    resolution: {integrity: sha512-Ggtq2GsJuxFNUvQzLoXqRwS4ceRfLAJnrIHUDrzAD0GgnOhwujJkKkxM/s5Bako07c3WtAs/sZo5PJq7VHjeDg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  '@sindresorhus/is@7.0.0':
-    resolution: {integrity: sha512-WDTlVTyvFivSOuyvMeedzg2hdoBLZ3f1uNVuEida2Rl9BrfjrIRjWA/VZIrMRLvSwJYCAlCRA3usDt1THytxWQ==}
+  '@sindresorhus/is@7.0.1':
+    resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==}
     engines: {node: '>=18'}
 
   '@surma/rollup-plugin-off-main-thread@2.2.3':
@@ -2307,11 +2639,11 @@ packages:
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@tanstack/query-core@5.59.10':
-    resolution: {integrity: sha512-XxvnKeBWqDTHstyjA1qmSD5VS/FZ2g/qYvPMhFM7IZF0JnMqMxtzbiUkiTFaZ4YZo/Q84LS0hZi0UncKJ3vIhg==}
+  '@tanstack/query-core@5.59.16':
+    resolution: {integrity: sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==}
 
-  '@tanstack/react-query@5.59.10':
-    resolution: {integrity: sha512-CwXzqOhB4JZJ6Wa8pp+NmaaNuWhscIJAlVCyyiYhyR0Y8a9GprS96WTcOgmTgK9gad9Y+dLhNZwmPWeBAk83aw==}
+  '@tanstack/react-query@5.59.16':
+    resolution: {integrity: sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==}
     peerDependencies:
       react: ^18 || ^19
 
@@ -2343,9 +2675,9 @@ packages:
     resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
-  '@tufjs/models@2.0.1':
-    resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@tufjs/models@3.0.1':
+    resolution: {integrity: sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   '@types/acorn@4.0.6':
     resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
@@ -2368,8 +2700,8 @@ packages:
   '@types/eslint-scope@3.7.7':
     resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
 
-  '@types/eslint@8.56.12':
-    resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==}
+  '@types/eslint@9.6.1':
+    resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
 
   '@types/estree-jsx@1.0.5':
     resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -2416,12 +2748,15 @@ packages:
   '@types/negotiator@0.6.3':
     resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==}
 
-  '@types/node@20.16.11':
-    resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==}
+  '@types/node@20.17.1':
+    resolution: {integrity: sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q==}
 
   '@types/node@22.7.6':
     resolution: {integrity: sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==}
 
+  '@types/node@22.8.2':
+    resolution: {integrity: sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw==}
+
   '@types/parse-json@4.0.2':
     resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
 
@@ -2434,8 +2769,8 @@ packages:
   '@types/react-transition-group@4.4.11':
     resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
 
-  '@types/react@18.3.11':
-    resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==}
+  '@types/react@18.3.12':
+    resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
 
   '@types/resolve@1.20.2':
     resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@@ -2455,9 +2790,6 @@ packages:
   '@types/use-sync-external-store@0.0.3':
     resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
 
-  '@types/uuid@10.0.0':
-    resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
-
   '@types/validator@13.12.0':
     resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==}
 
@@ -2472,8 +2804,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/eslint-plugin@8.8.1':
-    resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
+  '@typescript-eslint/eslint-plugin@8.11.0':
+    resolution: {integrity: sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -2493,8 +2825,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/parser@8.8.1':
-    resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
+  '@typescript-eslint/parser@8.11.0':
+    resolution: {integrity: sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -2507,8 +2839,8 @@ packages:
     resolution: {integrity: sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/scope-manager@8.8.1':
-    resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
+  '@typescript-eslint/scope-manager@8.11.0':
+    resolution: {integrity: sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@typescript-eslint/type-utils@8.10.0':
@@ -2520,8 +2852,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/type-utils@8.8.1':
-    resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
+  '@typescript-eslint/type-utils@8.11.0':
+    resolution: {integrity: sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2533,8 +2865,8 @@ packages:
     resolution: {integrity: sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/types@8.8.1':
-    resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
+  '@typescript-eslint/types@8.11.0':
+    resolution: {integrity: sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@typescript-eslint/typescript-estree@8.10.0':
@@ -2546,8 +2878,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/typescript-estree@8.8.1':
-    resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
+  '@typescript-eslint/typescript-estree@8.11.0':
+    resolution: {integrity: sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -2561,8 +2893,8 @@ packages:
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
 
-  '@typescript-eslint/utils@8.8.1':
-    resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
+  '@typescript-eslint/utils@8.11.0':
+    resolution: {integrity: sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -2571,32 +2903,31 @@ packages:
     resolution: {integrity: sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/visitor-keys@8.8.1':
-    resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
+  '@typescript-eslint/visitor-keys@8.11.0':
+    resolution: {integrity: sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@ungap/structured-clone@1.2.0':
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
 
-  '@vitejs/plugin-react@4.3.2':
-    resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==}
+  '@vitejs/plugin-react@4.3.3':
+    resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.2.0 || ^5.0.0
 
-  '@vitest/coverage-istanbul@2.1.2':
-    resolution: {integrity: sha512-dg7ex3GKrTIenAV0oEp78JucTVFsPMzjl1gYWun22O7SBDxcHFC/REZjWLhMTHRHY8ihm4uBCvmu+CvEu5/Adg==}
+  '@vitest/coverage-istanbul@2.1.4':
+    resolution: {integrity: sha512-NLmfjzXnRSmLF/h4hYkzjvd7hZ85DRZzPUqXu0McPFCMczDfNmOjMoM3KaxjFaEmOc1YzX9HHbU/Rr9VO+35ow==}
     peerDependencies:
-      vitest: 2.1.2
+      vitest: 2.1.4
 
-  '@vitest/expect@2.1.2':
-    resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==}
+  '@vitest/expect@2.1.4':
+    resolution: {integrity: sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==}
 
-  '@vitest/mocker@2.1.2':
-    resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==}
+  '@vitest/mocker@2.1.4':
+    resolution: {integrity: sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==}
     peerDependencies:
-      '@vitest/spy': 2.1.2
-      msw: ^2.3.5
+      msw: ^2.4.9
       vite: ^5.0.0
     peerDependenciesMeta:
       msw:
@@ -2604,20 +2935,20 @@ packages:
       vite:
         optional: true
 
-  '@vitest/pretty-format@2.1.2':
-    resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==}
+  '@vitest/pretty-format@2.1.4':
+    resolution: {integrity: sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==}
 
-  '@vitest/runner@2.1.2':
-    resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==}
+  '@vitest/runner@2.1.4':
+    resolution: {integrity: sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==}
 
-  '@vitest/snapshot@2.1.2':
-    resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==}
+  '@vitest/snapshot@2.1.4':
+    resolution: {integrity: sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==}
 
-  '@vitest/spy@2.1.2':
-    resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==}
+  '@vitest/spy@2.1.4':
+    resolution: {integrity: sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==}
 
-  '@vitest/utils@2.1.2':
-    resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
+  '@vitest/utils@2.1.4':
+    resolution: {integrity: sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==}
 
   '@webassemblyjs/ast@1.12.1':
     resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
@@ -2695,11 +3026,6 @@ packages:
     resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==}
     engines: {node: '>=0.4.0'}
 
-  acorn@8.12.0:
-    resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-
   acorn@8.13.0:
     resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==}
     engines: {node: '>=0.4.0'}
@@ -2774,8 +3100,9 @@ packages:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
-  aria-query@5.1.3:
-    resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+  aria-query@5.3.2:
+    resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+    engines: {node: '>= 0.4'}
 
   array-buffer-byte-length@1.0.1:
     resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
@@ -2842,8 +3169,8 @@ packages:
     resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
     engines: {node: '>= 0.4'}
 
-  axe-core@4.10.0:
-    resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
+  axe-core@4.10.2:
+    resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==}
     engines: {node: '>=4'}
 
   axe-html-reporter@2.2.11:
@@ -2887,9 +3214,9 @@ packages:
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
-  bin-links@4.0.4:
-    resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  bin-links@5.0.0:
+    resolution: {integrity: sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   bindings@1.5.0:
     resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -2975,6 +3302,10 @@ packages:
     resolution: {integrity: sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
+  cacache@19.0.1:
+    resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   cacheable-lookup@7.0.0:
     resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
     engines: {node: '>=14.16'}
@@ -3001,8 +3332,8 @@ packages:
   ccount@2.0.1:
     resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
 
-  chai@5.1.1:
-    resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
+  chai@5.1.2:
+    resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==}
     engines: {node: '>=12'}
 
   chalk@2.4.2:
@@ -3043,6 +3374,10 @@ packages:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
     engines: {node: '>=10'}
 
+  chownr@3.0.0:
+    resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+    engines: {node: '>=18'}
+
   chrome-trace-event@1.0.4:
     resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
     engines: {node: '>=6.0'}
@@ -3062,9 +3397,9 @@ packages:
     resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
     engines: {node: '>=6'}
 
-  cmd-shim@6.0.3:
-    resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  cmd-shim@7.0.0:
+    resolution: {integrity: sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   collapse-white-space@2.1.0:
     resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
@@ -3212,15 +3547,6 @@ packages:
       supports-color:
         optional: true
 
-  debug@4.3.5:
-    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-
   debug@4.3.7:
     resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
     engines: {node: '>=6.0'}
@@ -3241,10 +3567,6 @@ packages:
     resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
     engines: {node: '>=6'}
 
-  deep-equal@2.2.3:
-    resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
-    engines: {node: '>= 0.4'}
-
   deep-extend@0.6.0:
     resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
     engines: {node: '>=4.0.0'}
@@ -3301,9 +3623,6 @@ packages:
     resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
     engines: {node: '>=0.10.0'}
 
-  dom-helpers@5.2.1:
-    resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
-
   dom-serializer@2.0.0:
     resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
 
@@ -3338,9 +3657,6 @@ packages:
       echarts: ^3.0.0 || ^4.0.0 || ^5.0.0
       react: ^15.0.0 || >=16.0.0
 
-  echarts-stat@1.2.0:
-    resolution: {integrity: sha512-zLd7Kgs+tuTSeaK0VQEMNmnMivEkhvHIk1gpBtLzpRerfcIQ+Bd5XudOMmtwpaTc1WDZbA7d1V//iiBccR46Qg==}
-
   echarts@5.5.1:
     resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==}
 
@@ -3381,10 +3697,6 @@ packages:
   end-of-stream@1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
 
-  enhanced-resolve@5.17.0:
-    resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==}
-    engines: {node: '>=10.13.0'}
-
   enhanced-resolve@5.17.1:
     resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
     engines: {node: '>=10.13.0'}
@@ -3415,9 +3727,6 @@ packages:
     resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
     engines: {node: '>= 0.4'}
 
-  es-get-iterator@1.1.3:
-    resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
-
   es-iterator-helpers@1.0.19:
     resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
     engines: {node: '>= 0.4'}
@@ -3443,6 +3752,12 @@ packages:
   es6-error@4.1.1:
     resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
 
+  esast-util-from-estree@2.0.0:
+    resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==}
+
+  esast-util-from-js@2.0.1:
+    resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
+
   esbuild@0.21.5:
     resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
     engines: {node: '>=12'}
@@ -3453,10 +3768,6 @@ packages:
     engines: {node: '>=18'}
     hasBin: true
 
-  escalade@3.1.2:
-    resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
-    engines: {node: '>=6'}
-
   escalade@3.2.0:
     resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
     engines: {node: '>=6'}
@@ -3524,27 +3835,6 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
 
-  eslint-module-utils@2.8.1:
-    resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: '*'
-      eslint-import-resolver-node: '*'
-      eslint-import-resolver-typescript: '*'
-      eslint-import-resolver-webpack: '*'
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-      eslint:
-        optional: true
-      eslint-import-resolver-node:
-        optional: true
-      eslint-import-resolver-typescript:
-        optional: true
-      eslint-import-resolver-webpack:
-        optional: true
-
   eslint-plugin-import@2.31.0:
     resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
     engines: {node: '>=4'}
@@ -3555,12 +3845,18 @@ packages:
       '@typescript-eslint/parser':
         optional: true
 
-  eslint-plugin-jsx-a11y@6.10.0:
-    resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==}
+  eslint-plugin-jsx-a11y@6.10.2:
+    resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
     engines: {node: '>=4.0'}
     peerDependencies:
       eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
 
+  eslint-plugin-promise@7.1.0:
+    resolution: {integrity: sha512-8trNmPxdAy3W620WKDpaS65NlM5yAumod6XeC4LOb+jxlkG4IVcp68c6dXY2ev+uT4U1PtG57YDV6EGAXN0GbQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
+
   eslint-plugin-react-hooks@4.6.2:
     resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
     engines: {node: '>=10'}
@@ -3637,6 +3933,9 @@ packages:
   estree-util-is-identifier-name@3.0.0:
     resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
 
+  estree-util-scope@1.0.0:
+    resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==}
+
   estree-util-to-js@2.0.0:
     resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
 
@@ -3664,6 +3963,10 @@ packages:
     resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
     engines: {node: '>=6'}
 
+  expect-type@1.1.0:
+    resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
+    engines: {node: '>=12.0.0'}
+
   exponential-backoff@3.1.1:
     resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
 
@@ -3868,8 +4171,8 @@ packages:
   gopd@1.0.1:
     resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
 
-  got@14.4.2:
-    resolution: {integrity: sha512-+Te/qEZ6hr7i+f0FNgXx/6WQteSM/QqueGvxeYQQFm0GDfoxLVJ/oiwUKYMTeioColWUTdewZ06hmrBjw6F7tw==}
+  got@14.4.3:
+    resolution: {integrity: sha512-iTC0Z87yxSijWTh/IpvGpwOhIQK7+GgWkYrMRoN/hB9qeRj9RPuLGODwevs0p5idUf7nrxCVa5IlOmK3b8z+KA==}
     engines: {node: '>=20'}
 
   graceful-fs@4.2.11:
@@ -3927,12 +4230,12 @@ packages:
   hoist-non-react-statics@3.3.2:
     resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
 
-  hosted-git-info@7.0.2:
-    resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  hosted-git-info@8.0.0:
+    resolution: {integrity: sha512-4nw3vOVR+vHUOT8+U4giwe2tcGv+R3pwwRidUe67DoMBTjhrfr6rZYJVVwdkBE+Um050SG+X9tf0Jo4fOpn01w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  hpke-js@1.4.3:
-    resolution: {integrity: sha512-di8fhAcPSIcHPJpjtJRTZW8WG53SrnX/RYRM8UEdfVlWeMLddNf+BFFCLEYkyM5b3/JhojfaZ1+yuLj942aZhQ==}
+  hpke-js@1.5.0:
+    resolution: {integrity: sha512-ko4OjlbVQxO5q+NuXOSB2hpIQFnR+Xa5QmWlg3g2EFf2SIj8fIwAS422PZfjw+kxNZ/mMz4fvCx7JrL/UDWccQ==}
     engines: {node: '>=16.0.0'}
 
   html-escaper@2.0.2:
@@ -3977,8 +4280,8 @@ packages:
   i18next-resources-to-backend@1.2.1:
     resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==}
 
-  i18next@23.15.2:
-    resolution: {integrity: sha512-zcPSWzCvw6uKnuYHIqs4W7hTuB9e3AFcSdZgvCWoPXIZsBjBd4djN2/2uOHIB+1DFFkQnMBXvhNg7J3WyCuywQ==}
+  i18next@23.16.4:
+    resolution: {integrity: sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==}
 
   iconv-lite@0.5.2:
     resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==}
@@ -3994,9 +4297,9 @@ packages:
   ieee754@1.2.1:
     resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
 
-  ignore-walk@6.0.5:
-    resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  ignore-walk@7.0.0:
+    resolution: {integrity: sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ignore@5.3.1:
     resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
@@ -4031,6 +4334,10 @@ packages:
   ini@1.3.8:
     resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
 
+  ini@5.0.0:
+    resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   inline-style-parser@0.1.1:
     resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
 
@@ -4051,10 +4358,6 @@ packages:
   is-alphanumerical@2.0.1:
     resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
 
-  is-arguments@1.1.1:
-    resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
-    engines: {node: '>= 0.4'}
-
   is-array-buffer@3.0.4:
     resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
     engines: {node: '>= 0.4'}
@@ -4159,9 +4462,6 @@ packages:
     resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
     engines: {node: '>=0.10.0'}
 
-  is-reference@3.0.2:
-    resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
-
   is-regex@1.1.4:
     resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
     engines: {node: '>= 0.4'}
@@ -4299,9 +4599,9 @@ packages:
   json-parse-even-better-errors@2.3.1:
     resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
 
-  json-parse-even-better-errors@3.0.2:
-    resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  json-parse-even-better-errors@4.0.0:
+    resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   json-schema-traverse@0.4.1:
     resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -4430,6 +4730,9 @@ packages:
   loupe@3.1.1:
     resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==}
 
+  loupe@3.1.2:
+    resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
+
   lowercase-keys@3.0.0:
     resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -4452,11 +4755,11 @@ packages:
   magic-string@0.25.9:
     resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
 
-  magic-string@0.30.11:
-    resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+  magic-string@0.30.12:
+    resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
 
-  magicast@0.3.4:
-    resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==}
+  magicast@0.3.5:
+    resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
 
   make-dir@4.0.0:
     resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
@@ -4466,6 +4769,10 @@ packages:
     resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==}
     engines: {node: ^16.14.0 || >=18.0.0}
 
+  make-fetch-happen@14.0.3:
+    resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   make-fetch-happen@9.1.0:
     resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==}
     engines: {node: '>= 10'}
@@ -4648,6 +4955,10 @@ packages:
     resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
   minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
@@ -4667,6 +4978,10 @@ packages:
     resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  minipass-fetch@4.0.0:
+    resolution: {integrity: sha512-2v6aXUXwLP1Epd/gc32HAMIWoczx+fZwEPRHm/VwtrJzRGwR1qGZXEYV3Zp8ZjjbwaZhMrM6uHV4KVkk+XCc2w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   minipass-flush@1.0.5:
     resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
     engines: {node: '>= 8'}
@@ -4695,6 +5010,10 @@ packages:
     resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
     engines: {node: '>= 8'}
 
+  minizlib@3.0.1:
+    resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
+    engines: {node: '>= 18'}
+
   mkdirp-classic@0.5.3:
     resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
 
@@ -4703,6 +5022,11 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
+  mkdirp@3.0.1:
+    resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
   moment-timezone@0.5.45:
     resolution: {integrity: sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==}
 
@@ -4716,9 +5040,6 @@ packages:
   ms@2.0.0:
     resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
 
-  ms@2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-
   ms@2.1.3:
     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 
@@ -4741,6 +5062,10 @@ packages:
     resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
     engines: {node: '>= 0.6'}
 
+  negotiator@1.0.0:
+    resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+    engines: {node: '>= 0.6'}
+
   neo-async@2.6.2:
     resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
 
@@ -4800,41 +5125,46 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     hasBin: true
 
-  normalize-package-data@6.0.1:
-    resolution: {integrity: sha512-6rvCfeRW+OEZagAB4lMLSNuTNYZWLVtKccK79VSTf//yTY5VOCgcpH80O+bZK8Neps7pUnd5G+QlMg1yV/2iZQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  nopt@8.0.0:
+    resolution: {integrity: sha512-1L/fTJ4UmV/lUxT2Uf006pfZKTvAgCF+chz+0OgBHO8u2Z67pE7AaAUUj7CJy0lXqHmymUvGFt6NE9R3HER0yw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  normalize-package-data@7.0.0:
+    resolution: {integrity: sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   normalize-url@8.0.1:
     resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
     engines: {node: '>=14.16'}
 
-  npm-bundled@3.0.1:
-    resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-bundled@4.0.0:
+    resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-install-checks@6.3.0:
-    resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-install-checks@7.1.0:
+    resolution: {integrity: sha512-bkTildVlofeMX7wiOaWk3PlW7YcBXAuEc7TWpOxwUgalG5ZvgT/ms+6OX9zt7iGLv4+VhKbRZhpOfgQJzk1YAw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-normalize-package-bin@3.0.1:
-    resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-normalize-package-bin@4.0.0:
+    resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-package-arg@11.0.2:
-    resolution: {integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-package-arg@12.0.0:
+    resolution: {integrity: sha512-ZTE0hbwSdTNL+Stx2zxSqdu2KZfNDcrtrLdIk7XGnQFYBWYDho/ORvXtn5XEePcL3tFpGjHCV3X3xrtDh7eZ+A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-packlist@8.0.2:
-    resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  npm-packlist@9.0.0:
+    resolution: {integrity: sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-pick-manifest@9.0.1:
-    resolution: {integrity: sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-pick-manifest@10.0.0:
+    resolution: {integrity: sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  npm-registry-fetch@17.1.0:
-    resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  npm-registry-fetch@18.0.2:
+    resolution: {integrity: sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   npmlog@6.0.2:
     resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==}
@@ -4851,10 +5181,6 @@ packages:
   object-inspect@1.13.1:
     resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
 
-  object-is@1.1.6:
-    resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
-    engines: {node: '>= 0.4'}
-
   object-keys@1.1.1:
     resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
     engines: {node: '>= 0.4'}
@@ -4910,9 +5236,6 @@ packages:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
     engines: {node: '>= 0.8.0'}
 
-  otpauth@9.3.4:
-    resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==}
-
   p-cancelable@4.0.1:
     resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
     engines: {node: '>=14.16'}
@@ -4937,6 +5260,10 @@ packages:
     resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
     engines: {node: '>=10'}
 
+  p-map@7.0.2:
+    resolution: {integrity: sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==}
+    engines: {node: '>=18'}
+
   p-retry@4.6.2:
     resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
     engines: {node: '>=8'}
@@ -4947,18 +5274,23 @@ packages:
   packageurl-js@1.0.2:
     resolution: {integrity: sha512-fWC4ZPxo80qlh3xN5FxfIoQD3phVY4+EyzTIqyksjhKNDmaicdpxSvkWwIrYTtv9C1/RcUN6pxaTwGmj2NzS6A==}
 
-  pacote@18.0.6:
-    resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  pacote@19.0.1:
+    resolution: {integrity: sha512-zIpxWAsr/BvhrkSruspG8aqCQUUrWtpwx0GjiRZQhEM/pZXrigA32ElN3vTcCPUDOFmHr6SFxwYrvVUs5NTEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
+  pacote@20.0.0:
+    resolution: {integrity: sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==}
+    engines: {node: ^18.17.0 || >=20.5.0}
     hasBin: true
 
   parent-module@1.0.1:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
 
-  parse-conflict-json@3.0.1:
-    resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  parse-conflict-json@4.0.0:
+    resolution: {integrity: sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   parse-entities@4.0.1:
     resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==}
@@ -5018,15 +5350,9 @@ packages:
     resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
     engines: {node: '>= 14.16'}
 
-  periscopic@3.1.0:
-    resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
-
   pg-connection-string@2.6.4:
     resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
 
-  picocolors@1.0.1:
-    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
-
   picocolors@1.1.0:
     resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
 
@@ -5038,13 +5364,13 @@ packages:
     resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==}
     engines: {node: '>=12.0.0'}
 
-  playwright-core@1.48.1:
-    resolution: {integrity: sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==}
+  playwright-core@1.48.2:
+    resolution: {integrity: sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==}
     engines: {node: '>=18'}
     hasBin: true
 
-  playwright@1.48.1:
-    resolution: {integrity: sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==}
+  playwright@1.48.2:
+    resolution: {integrity: sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -5052,8 +5378,8 @@ packages:
     resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
     engines: {node: '>= 0.4'}
 
-  postcss-selector-parser@6.1.0:
-    resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
+  postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
     engines: {node: '>=4'}
 
   postcss@8.4.31:
@@ -5096,9 +5422,13 @@ packages:
     resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  proggy@2.0.0:
-    resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  proc-log@5.0.0:
+    resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
+  proggy@3.0.0:
+    resolution: {integrity: sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   promise-all-reject-late@1.0.1:
     resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==}
@@ -5172,16 +5502,16 @@ packages:
     peerDependencies:
       react: ^18.3.1
 
-  react-error-boundary@4.0.13:
-    resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==}
+  react-error-boundary@4.1.2:
+    resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==}
     peerDependencies:
       react: '>=16.13.1'
 
   react-fast-compare@2.0.4:
     resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==}
 
-  react-i18next@15.0.2:
-    resolution: {integrity: sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==}
+  react-i18next@15.1.0:
+    resolution: {integrity: sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==}
     peerDependencies:
       i18next: '>= 23.2.3'
       react: '>= 16.8.0'
@@ -5227,28 +5557,34 @@ packages:
     resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
     engines: {node: '>=0.10.0'}
 
-  react-transition-group@4.4.5:
-    resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
-    peerDependencies:
-      react: '>=16.6.0'
-      react-dom: '>=16.6.0'
-
   react@18.3.1:
     resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
     engines: {node: '>=0.10.0'}
 
-  read-cmd-shim@4.0.0:
-    resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  read-cmd-shim@5.0.0:
+    resolution: {integrity: sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
-  read-package-json-fast@3.0.2:
-    resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  read-package-json-fast@4.0.0:
+    resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
     engines: {node: '>= 6'}
 
+  recma-build-jsx@1.0.0:
+    resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
+
+  recma-jsx@1.0.0:
+    resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==}
+
+  recma-parse@1.0.0:
+    resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==}
+
+  recma-stringify@1.0.0:
+    resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+
   redux@5.0.1:
     resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
 
@@ -5281,6 +5617,9 @@ packages:
     resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
     hasBin: true
 
+  rehype-recma@1.0.0:
+    resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
+
   remark-mdx@3.0.1:
     resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==}
 
@@ -5290,8 +5629,8 @@ packages:
   remark-rehype@11.1.1:
     resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==}
 
-  remeda@2.15.0:
-    resolution: {integrity: sha512-Q0Xdg6z3pDKMGVCAI9wGZ+Yz0y0HOzaxxY3wc9gdjJyxqH93LywDUJ4PJ/zhweicYgcB4HbbOliR8HsIdse6mA==}
+  remeda@2.16.0:
+    resolution: {integrity: sha512-HOymkGg58HW4LT8MBEabQEdW76YsqcRNNFPXPrOrnYm+/9Pmk0b9fm8PKgQxoRPa6WDLnRM/LxTXkHdXf9Ab0w==}
 
   require-directory@2.1.1:
     resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
@@ -5343,6 +5682,10 @@ packages:
     deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
+  rimraf@5.0.10:
+    resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
+    hasBin: true
+
   roarr@2.15.4:
     resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
     engines: {node: '>=8.0'}
@@ -5407,8 +5750,8 @@ packages:
     resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==}
     engines: {node: '>= 10.0.0'}
 
-  sequelize@6.37.3:
-    resolution: {integrity: sha512-V2FTqYpdZjPy3VQrZvjTPnOoLm0KudCRXfGWp48QwhyPPp2yW8z0p0sCYZd/em847Tl2dVxJJ1DR+hF+O77T7A==}
+  sequelize@6.37.5:
+    resolution: {integrity: sha512-10WA4poUb3XWnUROThqL2Apq9C2NhyV1xHPMZuybNMCucDsbbFuKg51jhmyvvAUyUqCiimwTZamc3AHhMoBr2Q==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       ibm_db: '*'
@@ -5486,9 +5829,9 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
-  sigstore@2.3.1:
-    resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  sigstore@3.0.0:
+    resolution: {integrity: sha512-PHMifhh3EN4loMcHCz6l3v/luzgT3za+9f8subGgeMNjbJjzH4Ij/YoX3Gvu+kaouJRIlVdTHHCREADYf+ZteA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   simple-concat@1.0.1:
     resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
@@ -5581,9 +5924,9 @@ packages:
     resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  ssri@11.0.0:
-    resolution: {integrity: sha512-aZpUoMN/Jj2MqA4vMCeiKGnc/8SuSyHbGSBdgFbZxP8OJGF/lFkIuElzPxsN0q8TQQ+prw3P4EDfB3TBHHgfXw==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  ssri@12.0.0:
+    resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ssri@8.0.1:
     resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
@@ -5603,10 +5946,6 @@ packages:
   std-env@3.7.0:
     resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
 
-  stop-iteration-iterator@1.0.0:
-    resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
-    engines: {node: '>= 0.4'}
-
   streamsearch@1.1.0:
     resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
     engines: {node: '>=10.0.0'}
@@ -5729,6 +6068,10 @@ packages:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
 
+  tar@7.4.3:
+    resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+    engines: {node: '>=18'}
+
   temp-dir@2.0.0:
     resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
     engines: {node: '>=8'}
@@ -5779,8 +6122,8 @@ packages:
   tinybench@2.9.0:
     resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
 
-  tinyexec@0.3.0:
-    resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==}
+  tinyexec@0.3.1:
+    resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==}
 
   tinypool@1.0.1:
     resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
@@ -5790,8 +6133,8 @@ packages:
     resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
     engines: {node: '>=14.0.0'}
 
-  tinyspy@3.0.0:
-    resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==}
+  tinyspy@3.0.2:
+    resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
     engines: {node: '>=14.0.0'}
 
   to-fast-properties@2.0.0:
@@ -5854,14 +6197,17 @@ packages:
   tslib@2.6.3:
     resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
 
-  tsx@4.19.1:
-    resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==}
+  tslib@2.7.0:
+    resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
+  tsx@4.19.2:
+    resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==}
     engines: {node: '>=18.0.0'}
     hasBin: true
 
-  tuf-js@2.2.1:
-    resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  tuf-js@3.0.1:
+    resolution: {integrity: sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   tunnel-agent@0.6.0:
     resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
@@ -5902,8 +6248,8 @@ packages:
     resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
     engines: {node: '>= 0.4'}
 
-  typescript-eslint@8.10.0:
-    resolution: {integrity: sha512-YIu230PeN7z9zpu/EtqCIuRVHPs4iSlqW6TEvjbyDAE3MZsSl2RXBo+5ag+lbABCG8sFM1WVKEXhlQ8Ml8A3Fw==}
+  typescript-eslint@8.11.0:
+    resolution: {integrity: sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '*'
@@ -5959,6 +6305,10 @@ packages:
     resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  unique-filename@4.0.0:
+    resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   unique-slug@2.0.2:
     resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
 
@@ -5966,6 +6316,10 @@ packages:
     resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  unique-slug@5.0.0:
+    resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+
   unique-string@2.0.0:
     resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
     engines: {node: '>=8'}
@@ -6023,8 +6377,8 @@ packages:
     resolution: {integrity: sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  use-debounce@10.0.3:
-    resolution: {integrity: sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==}
+  use-debounce@10.0.4:
+    resolution: {integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==}
     engines: {node: '>= 16.0.0'}
     peerDependencies:
       react: '*'
@@ -6050,6 +6404,10 @@ packages:
     resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
     hasBin: true
 
+  uuid@11.0.1:
+    resolution: {integrity: sha512-wt9UB5EcLhnboy1UvA1mvGPXkIIrHSu+3FmUksARfdVw9tuPf3CH/CohxO0Su1ApoKAeT6BVzAJIvjTuQVSmuQ==}
+    hasBin: true
+
   uuid@8.3.2:
     resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
     hasBin: true
@@ -6068,9 +6426,9 @@ packages:
   validate-npm-package-license@3.0.4:
     resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
 
-  validate-npm-package-name@5.0.1:
-    resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  validate-npm-package-name@6.0.0:
+    resolution: {integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   validator@13.12.0:
     resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
@@ -6086,8 +6444,8 @@ packages:
   vfile@6.0.3:
     resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
 
-  vite-node@2.1.2:
-    resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==}
+  vite-node@2.1.4:
+    resolution: {integrity: sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
 
@@ -6127,15 +6485,15 @@ packages:
       terser:
         optional: true
 
-  vitest@2.1.2:
-    resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==}
+  vitest@2.1.4:
+    resolution: {integrity: sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
       '@edge-runtime/vm': '*'
       '@types/node': ^18.0.0 || >=20.0.0
-      '@vitest/browser': 2.1.2
-      '@vitest/ui': 2.1.2
+      '@vitest/browser': 2.1.4
+      '@vitest/ui': 2.1.4
       happy-dom: '*'
       jsdom: '*'
     peerDependenciesMeta:
@@ -6224,6 +6582,11 @@ packages:
     engines: {node: ^16.13.0 || >=18.0.0}
     hasBin: true
 
+  which@5.0.0:
+    resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
+    hasBin: true
+
   why-is-node-running@2.3.0:
     resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
     engines: {node: '>=8'}
@@ -6309,9 +6672,9 @@ packages:
   wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
-  write-file-atomic@5.0.1:
-    resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  write-file-atomic@6.0.0:
+    resolution: {integrity: sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==}
+    engines: {node: ^18.17.0 || >=20.5.0}
 
   ws@7.5.10:
     resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
@@ -6339,6 +6702,10 @@ packages:
   yallist@4.0.0:
     resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 
+  yallist@5.0.0:
+    resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+    engines: {node: '>=18'}
+
   yaml@1.10.2:
     resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
     engines: {node: '>= 6'}
@@ -6384,7 +6751,7 @@ snapshots:
 
   '@appthreat/atom@2.0.21':
     dependencies:
-      '@babel/parser': 7.25.6
+      '@babel/parser': 7.25.8
       typescript: 5.6.3
       yargs: 17.7.2
     optional: true
@@ -6394,45 +6761,20 @@ snapshots:
       '@bufbuild/protobuf': 1.7.2
     optional: true
 
-  '@axe-core/playwright@4.10.0(playwright-core@1.48.1)':
-    dependencies:
-      axe-core: 4.10.0
-      playwright-core: 1.48.1
-
-  '@babel/code-frame@7.24.7':
+  '@axe-core/playwright@4.10.0(playwright-core@1.48.2)':
     dependencies:
-      '@babel/highlight': 7.24.7
-      picocolors: 1.1.0
+      axe-core: 4.10.2
+      playwright-core: 1.48.2
 
   '@babel/code-frame@7.25.7':
     dependencies:
       '@babel/highlight': 7.25.7
-      picocolors: 1.0.1
+      picocolors: 1.1.0
 
   '@babel/compat-data@7.24.7': {}
 
   '@babel/compat-data@7.25.8': {}
 
-  '@babel/core@7.24.7':
-    dependencies:
-      '@ampproject/remapping': 2.3.0
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
-      '@babel/helper-compilation-targets': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
-      '@babel/helpers': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/template': 7.25.0
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
-      convert-source-map: 2.0.0
-      debug: 4.3.7
-      gensync: 1.0.0-beta.2
-      json5: 2.2.3
-      semver: 6.3.1
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/core@7.25.8':
     dependencies:
       '@ampproject/remapping': 2.3.0
@@ -6455,24 +6797,10 @@ snapshots:
 
   '@babel/generator@7.17.7':
     dependencies:
-      '@babel/types': 7.17.0
+      '@babel/types': 7.25.8
       jsesc: 2.5.2
       source-map: 0.5.7
 
-  '@babel/generator@7.24.7':
-    dependencies:
-      '@babel/types': 7.25.6
-      '@jridgewell/gen-mapping': 0.3.5
-      '@jridgewell/trace-mapping': 0.3.25
-      jsesc: 2.5.2
-
-  '@babel/generator@7.25.6':
-    dependencies:
-      '@babel/types': 7.25.6
-      '@jridgewell/gen-mapping': 0.3.5
-      '@jridgewell/trace-mapping': 0.3.25
-      jsesc: 2.5.2
-
   '@babel/generator@7.25.7':
     dependencies:
       '@babel/types': 7.25.8
@@ -6482,12 +6810,12 @@ snapshots:
 
   '@babel/helper-annotate-as-pure@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6507,32 +6835,32 @@ snapshots:
       lru-cache: 5.1.1
       semver: 6.3.1
 
-  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-member-expression-to-functions': 7.24.7
       '@babel/helper-optimise-call-expression': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       regexpu-core: 5.3.2
       semver: 6.3.1
 
-  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)':
+  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
       debug: 4.3.7
       lodash.debounce: 4.0.8
@@ -6542,28 +6870,21 @@ snapshots:
 
   '@babel/helper-environment-visitor@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-function-name@7.24.7':
     dependencies:
-      '@babel/template': 7.24.7
-      '@babel/types': 7.25.6
+      '@babel/template': 7.25.7
+      '@babel/types': 7.25.8
 
   '@babel/helper-hoist-variables@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-member-expression-to-functions@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/helper-module-imports@7.24.7':
-    dependencies:
-      '@babel/traverse': 7.24.7
-      '@babel/types': 7.24.7
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6574,17 +6895,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
-    dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
-      '@babel/helper-simple-access': 7.24.7
-      '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
-    transitivePeerDependencies:
-      - supports-color
-
   '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)':
     dependencies:
       '@babel/core': 7.25.8
@@ -6597,22 +6907,22 @@ snapshots:
 
   '@babel/helper-optimise-call-expression@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@babel/helper-plugin-utils@7.24.7': {}
 
-  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-wrap-function': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)':
+  '@babel/helper-replace-supers@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-member-expression-to-functions': 7.24.7
       '@babel/helper-optimise-call-expression': 7.24.7
@@ -6621,8 +6931,8 @@ snapshots:
 
   '@babel/helper-simple-access@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
@@ -6635,23 +6945,17 @@ snapshots:
 
   '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
     dependencies:
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
   '@babel/helper-split-export-declaration@7.24.7':
     dependencies:
-      '@babel/types': 7.25.6
-
-  '@babel/helper-string-parser@7.24.7': {}
-
-  '@babel/helper-string-parser@7.24.8': {}
+      '@babel/types': 7.25.8
 
   '@babel/helper-string-parser@7.25.7': {}
 
-  '@babel/helper-validator-identifier@7.24.7': {}
-
   '@babel/helper-validator-identifier@7.25.7': {}
 
   '@babel/helper-validator-option@7.24.7': {}
@@ -6661,29 +6965,17 @@ snapshots:
   '@babel/helper-wrap-function@7.24.7':
     dependencies:
       '@babel/helper-function-name': 7.24.7
-      '@babel/template': 7.25.0
-      '@babel/traverse': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/template': 7.25.7
+      '@babel/traverse': 7.25.7
+      '@babel/types': 7.25.8
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helpers@7.24.7':
-    dependencies:
-      '@babel/template': 7.25.0
-      '@babel/types': 7.25.6
-
   '@babel/helpers@7.25.7':
     dependencies:
       '@babel/template': 7.25.7
       '@babel/types': 7.25.8
 
-  '@babel/highlight@7.24.7':
-    dependencies:
-      '@babel/helper-validator-identifier': 7.24.7
-      chalk: 2.4.2
-      js-tokens: 4.0.0
-      picocolors: 1.1.0
-
   '@babel/highlight@7.25.7':
     dependencies:
       '@babel/helper-validator-identifier': 7.25.7
@@ -6691,398 +6983,390 @@ snapshots:
       js-tokens: 4.0.0
       picocolors: 1.1.0
 
-  '@babel/parser@7.24.7':
-    dependencies:
-      '@babel/types': 7.24.7
-
-  '@babel/parser@7.25.6':
-    dependencies:
-      '@babel/types': 7.25.6
-
   '@babel/parser@7.25.8':
     dependencies:
       '@babel/types': 7.25.8
 
-  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
-      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)':
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
 
-  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)':
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-module-imports': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-split-export-declaration': 7.24.7
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/template': 7.25.0
+      '@babel/template': 7.25.7
 
-  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-simple-access': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-hoist-variables': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
+      '@babel/helper-validator-identifier': 7.25.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-compilation-targets': 7.25.7
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8)
 
-  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-annotate-as-pure': 7.24.7
-      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
   '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.8)':
@@ -7095,188 +7379,168 @@ snapshots:
       '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       regenerator-transform: 0.15.2
 
-  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)':
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.25.8)
       '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/preset-env@7.24.7(@babel/core@7.24.7)':
+  '@babel/preset-env@7.24.7(@babel/core@7.25.8)':
     dependencies:
       '@babel/compat-data': 7.24.7
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-compilation-targets': 7.24.7
       '@babel/helper-plugin-utils': 7.24.7
       '@babel/helper-validator-option': 7.24.7
-      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
-      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7)
-      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7)
-      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7)
-      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7)
-      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7)
-      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.8)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.8)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.8)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.8)
+      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.25.8)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.8)
+      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.8)
+      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.25.8)
+      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.8)
       core-js-compat: 3.37.1
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)':
+  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.25.8)':
     dependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
       '@babel/helper-plugin-utils': 7.24.7
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
       esutils: 2.0.3
 
   '@babel/regjsgen@0.8.0': {}
 
-  '@babel/runtime@7.24.7':
-    dependencies:
-      regenerator-runtime: 0.14.1
-
-  '@babel/runtime@7.25.4':
-    dependencies:
-      regenerator-runtime: 0.14.1
-
   '@babel/runtime@7.25.6':
     dependencies:
       regenerator-runtime: 0.14.1
 
-  '@babel/template@7.24.7':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-
-  '@babel/template@7.25.0':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-
   '@babel/template@7.25.7':
     dependencies:
       '@babel/code-frame': 7.25.7
@@ -7285,41 +7549,14 @@ snapshots:
 
   '@babel/traverse@7.23.2':
     dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.24.7
-      '@babel/helper-environment-visitor': 7.24.7
-      '@babel/helper-function-name': 7.24.7
-      '@babel/helper-hoist-variables': 7.24.7
-      '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/parser': 7.24.7
-      '@babel/types': 7.24.7
-      debug: 4.3.7
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/traverse@7.24.7':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
+      '@babel/code-frame': 7.25.7
+      '@babel/generator': 7.25.7
       '@babel/helper-environment-visitor': 7.24.7
       '@babel/helper-function-name': 7.24.7
       '@babel/helper-hoist-variables': 7.24.7
       '@babel/helper-split-export-declaration': 7.24.7
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
-      debug: 4.3.7
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
-  '@babel/traverse@7.25.6':
-    dependencies:
-      '@babel/code-frame': 7.24.7
-      '@babel/generator': 7.25.6
-      '@babel/parser': 7.25.6
-      '@babel/template': 7.25.0
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       debug: 4.3.7
       globals: 11.12.0
     transitivePeerDependencies:
@@ -7339,19 +7576,7 @@ snapshots:
 
   '@babel/types@7.17.0':
     dependencies:
-      '@babel/helper-validator-identifier': 7.24.7
-      to-fast-properties: 2.0.0
-
-  '@babel/types@7.24.7':
-    dependencies:
-      '@babel/helper-string-parser': 7.24.7
-      '@babel/helper-validator-identifier': 7.24.7
-      to-fast-properties: 2.0.0
-
-  '@babel/types@7.25.6':
-    dependencies:
-      '@babel/helper-string-parser': 7.24.8
-      '@babel/helper-validator-identifier': 7.24.7
+      '@babel/helper-validator-identifier': 7.25.7
       to-fast-properties: 2.0.0
 
   '@babel/types@7.25.8':
@@ -7384,11 +7609,11 @@ snapshots:
   '@cyclonedx/cdxgen-plugins-bin@1.6.3':
     optional: true
 
-  '@cyclonedx/cdxgen@10.10.4':
+  '@cyclonedx/cdxgen@10.10.7':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/traverse': 7.25.6
-      '@npmcli/arborist': 7.5.4
+      '@babel/parser': 7.25.8
+      '@babel/traverse': 7.25.7
+      '@npmcli/arborist': 8.0.0
       ajv: 8.17.1
       ajv-formats: 3.0.1(ajv@8.17.1)
       cheerio: 1.0.0
@@ -7396,7 +7621,7 @@ snapshots:
       find-up: 7.0.0
       glob: 11.0.0
       global-agent: 3.0.0
-      got: 14.4.2
+      got: 14.4.3
       iconv-lite: 0.6.3
       js-yaml: 4.1.0
       jws: 4.0.0
@@ -7405,9 +7630,9 @@ snapshots:
       prettify-xml: 1.2.0
       properties-reader: 2.3.0
       semver: 7.6.3
-      ssri: 11.0.0
+      ssri: 12.0.0
       table: 6.8.2
-      tar: 6.2.1
+      tar: 7.4.3
       toml: 3.0.0
       uuid: 10.0.0
       validate-iri: 1.0.1
@@ -7427,7 +7652,7 @@ snapshots:
       compression: 1.7.4
       connect: 3.7.0
       jsonata: 2.0.5
-      sequelize: 6.37.3(sqlite3@5.1.7)
+      sequelize: 6.37.5(sqlite3@5.1.7)
       sqlite3: 5.1.7
     transitivePeerDependencies:
       - bluebird
@@ -7445,10 +7670,10 @@ snapshots:
 
   '@drauu/core@0.4.1': {}
 
-  '@ducanh2912/next-pwa@10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)':
+  '@ducanh2912/next-pwa@10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)':
     dependencies:
       fast-glob: 3.3.2
-      next: 14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      next: 14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       semver: 7.6.3
       webpack: 5.92.1
       workbox-build: 7.1.1(@types/babel__core@7.20.5)
@@ -7461,8 +7686,8 @@ snapshots:
 
   '@emotion/babel-plugin@11.12.0':
     dependencies:
-      '@babel/helper-module-imports': 7.24.7
-      '@babel/runtime': 7.24.7
+      '@babel/helper-module-imports': 7.25.7
+      '@babel/runtime': 7.25.6
       '@emotion/hash': 0.9.2
       '@emotion/memoize': 0.9.0
       '@emotion/serialize': 1.3.1
@@ -7491,9 +7716,9 @@ snapshots:
 
   '@emotion/memoize@0.9.0': {}
 
-  '@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1)':
+  '@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@emotion/babel-plugin': 11.12.0
       '@emotion/cache': 11.13.1
       '@emotion/serialize': 1.3.1
@@ -7503,7 +7728,7 @@ snapshots:
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
     transitivePeerDependencies:
       - supports-color
 
@@ -7517,18 +7742,18 @@ snapshots:
 
   '@emotion/sheet@1.4.0': {}
 
-  '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@emotion/babel-plugin': 11.12.0
       '@emotion/is-prop-valid': 1.3.0
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
       '@emotion/serialize': 1.3.1
       '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1)
       '@emotion/utils': 1.4.0
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
     transitivePeerDependencies:
       - supports-color
 
@@ -7688,8 +7913,6 @@ snapshots:
       eslint: 9.13.0
       eslint-visitor-keys: 3.4.3
 
-  '@eslint-community/regexpp@4.10.1': {}
-
   '@eslint-community/regexpp@4.11.1': {}
 
   '@eslint/compat@1.2.1(eslint@9.13.0)':
@@ -7728,7 +7951,7 @@ snapshots:
     dependencies:
       levn: 0.4.1
 
-  '@faker-js/faker@9.0.3': {}
+  '@faker-js/faker@9.1.0': {}
 
   '@floating-ui/core@1.6.2':
     dependencies:
@@ -7779,11 +8002,11 @@ snapshots:
   '@gar/promisify@1.1.3':
     optional: true
 
-  '@grafana/schema@11.2.2':
+  '@grafana/schema@11.3.0':
     dependencies:
-      tslib: 2.6.3
+      tslib: 2.7.0
 
-  '@hello-pangea/dnd@17.0.0(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@hello-pangea/dnd@17.0.0(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
       css-box-model: 1.2.1
@@ -7791,7 +8014,7 @@ snapshots:
       raf-schd: 4.0.3
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      react-redux: 9.1.2(@types/react@18.3.11)(react@18.3.1)(redux@5.0.1)
+      react-redux: 9.1.2(@types/react@18.3.12)(react@18.3.1)(redux@5.0.1)
       redux: 5.0.1
       use-memo-one: 1.1.3(react@18.3.1)
     transitivePeerDependencies:
@@ -7812,13 +8035,13 @@ snapshots:
     dependencies:
       '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   '@hpke/dhkem-x448@1.4.3':
     dependencies:
       '@hpke/common': 1.4.3
       '@noble/curves': 1.4.2
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   '@humanfs/core@0.19.0': {}
 
@@ -7840,6 +8063,10 @@ snapshots:
       wrap-ansi: 8.1.0
       wrap-ansi-cjs: wrap-ansi@7.0.0
 
+  '@isaacs/fs-minipass@4.0.1':
+    dependencies:
+      minipass: 7.1.2
+
   '@isaacs/string-locale-compare@1.1.0': {}
 
   '@istanbuljs/schema@0.1.3': {}
@@ -7866,7 +8093,7 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.2
       '@jridgewell/sourcemap-codec': 1.5.0
 
-  '@keycloak/keycloak-admin-client@26.0.0':
+  '@keycloak/keycloak-admin-client@26.0.2':
     dependencies:
       camelize-ts: 3.0.0
       url-join: 5.0.0
@@ -7876,7 +8103,7 @@ snapshots:
 
   '@matrix-org/olm@3.2.15': {}
 
-  '@mdx-js/mdx@3.0.1':
+  '@mdx-js/mdx@3.1.0(acorn@8.13.0)':
     dependencies:
       '@types/estree': 1.0.6
       '@types/estree-jsx': 1.0.5
@@ -7884,14 +8111,15 @@ snapshots:
       '@types/mdx': 2.0.13
       collapse-white-space: 2.1.0
       devlop: 1.1.0
-      estree-util-build-jsx: 3.0.1
       estree-util-is-identifier-name: 3.0.0
-      estree-util-to-js: 2.0.0
+      estree-util-scope: 1.0.0
       estree-walker: 3.0.3
-      hast-util-to-estree: 3.1.0
       hast-util-to-jsx-runtime: 2.3.2
       markdown-extensions: 2.0.0
-      periscopic: 3.1.0
+      recma-build-jsx: 1.0.0
+      recma-jsx: 1.0.0(acorn@8.13.0)
+      recma-stringify: 1.0.0
+      rehype-recma: 1.0.0
       remark-mdx: 3.0.1
       remark-parse: 11.0.0
       remark-rehype: 11.1.1
@@ -7902,59 +8130,60 @@ snapshots:
       unist-util-visit: 5.0.0
       vfile: 6.0.3
     transitivePeerDependencies:
+      - acorn
       - supports-color
 
-  '@mui/base@5.0.0-beta.40(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@mui/base@5.0.0-beta.40(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       '@popperjs/core': 2.11.8
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@mui/core-downloads-tracker@5.16.5': {}
 
-  '@mui/icons-material@5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/icons-material@5.16.7(@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/material': '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
+      '@babel/runtime': 7.25.6
+      '@mui/material': '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)'
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@mui/joy@5.0.0-beta.48(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/base': 5.0.0-beta.40(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@babel/runtime': 7.25.6
+      '@mui/base': 5.0.0-beta.40(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@mui/core-downloads-tracker': 5.16.5
-      '@mui/system': 5.16.5(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/system': 5.16.5(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@types/react': 18.3.11
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@types/react': 18.3.12
 
-  '@mui/private-theming@5.16.5(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/private-theming@5.16.5(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/styled-engine@5.16.4(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(react@18.3.1)':
+  '@mui/styled-engine@5.16.4(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.25.6
       '@emotion/cache': 11.13.1
@@ -7962,40 +8191,40 @@ snapshots:
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
 
-  '@mui/system@5.16.5(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/system@5.16.5(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/private-theming': 5.16.5(@types/react@18.3.11)(react@18.3.1)
-      '@mui/styled-engine': 5.16.4(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1))(react@18.3.1)
-      '@mui/types': 7.2.15(@types/react@18.3.11)
-      '@mui/utils': 5.16.5(@types/react@18.3.11)(react@18.3.1)
+      '@babel/runtime': 7.25.6
+      '@mui/private-theming': 5.16.5(@types/react@18.3.12)(react@18.3.1)
+      '@mui/styled-engine': 5.16.4(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
+      '@mui/types': 7.2.15(@types/react@18.3.12)
+      '@mui/utils': 5.16.5(@types/react@18.3.12)(react@18.3.1)
       clsx: 2.1.1
       csstype: 3.1.3
       prop-types: 15.8.1
       react: 18.3.1
     optionalDependencies:
-      '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
-      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.11)(react@18.3.1))(@types/react@18.3.11)(react@18.3.1)
-      '@types/react': 18.3.11
+      '@emotion/react': 11.13.3(@types/react@18.3.12)(react@18.3.1)
+      '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1)
+      '@types/react': 18.3.12
 
-  '@mui/types@7.2.15(@types/react@18.3.11)':
+  '@mui/types@7.2.15(@types/react@18.3.12)':
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@mui/utils@5.16.5(@types/react@18.3.11)(react@18.3.1)':
+  '@mui/utils@5.16.5(@types/react@18.3.12)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.24.7
-      '@mui/types': 7.2.15(@types/react@18.3.11)
+      '@babel/runtime': 7.25.6
+      '@mui/types': 7.2.15(@types/react@18.3.12)
       '@types/prop-types': 15.7.12
       clsx: 2.1.1
       prop-types: 15.8.1
       react: 18.3.1
       react-is: 18.3.1
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@next/bundle-analyzer@14.2.14':
     dependencies:
@@ -8071,41 +8300,51 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@npmcli/arborist@7.5.4':
+  '@npmcli/agent@3.0.0':
+    dependencies:
+      agent-base: 7.1.1
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.4
+      lru-cache: 10.2.2
+      socks-proxy-agent: 8.0.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@npmcli/arborist@8.0.0':
     dependencies:
       '@isaacs/string-locale-compare': 1.1.0
-      '@npmcli/fs': 3.1.1
-      '@npmcli/installed-package-contents': 2.1.0
-      '@npmcli/map-workspaces': 3.0.6
-      '@npmcli/metavuln-calculator': 7.1.1
-      '@npmcli/name-from-folder': 2.0.0
-      '@npmcli/node-gyp': 3.0.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/query': 3.1.0
-      '@npmcli/redact': 2.0.1
-      '@npmcli/run-script': 8.1.0
-      bin-links: 4.0.4
-      cacache: 18.0.3
+      '@npmcli/fs': 4.0.0
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/map-workspaces': 4.0.1
+      '@npmcli/metavuln-calculator': 8.0.1
+      '@npmcli/name-from-folder': 3.0.0
+      '@npmcli/node-gyp': 4.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/query': 4.0.0
+      '@npmcli/redact': 3.0.0
+      '@npmcli/run-script': 9.0.1
+      bin-links: 5.0.0
+      cacache: 19.0.1
       common-ancestor-path: 1.0.1
-      hosted-git-info: 7.0.2
-      json-parse-even-better-errors: 3.0.2
+      hosted-git-info: 8.0.0
+      json-parse-even-better-errors: 4.0.0
       json-stringify-nice: 1.1.4
       lru-cache: 10.2.2
-      minimatch: 9.0.4
-      nopt: 7.2.1
-      npm-install-checks: 6.3.0
-      npm-package-arg: 11.0.2
-      npm-pick-manifest: 9.0.1
-      npm-registry-fetch: 17.1.0
-      pacote: 18.0.6
-      parse-conflict-json: 3.0.1
-      proc-log: 4.2.0
-      proggy: 2.0.0
+      minimatch: 9.0.5
+      nopt: 8.0.0
+      npm-install-checks: 7.1.0
+      npm-package-arg: 12.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      pacote: 19.0.1
+      parse-conflict-json: 4.0.0
+      proc-log: 5.0.0
+      proggy: 3.0.0
       promise-all-reject-late: 1.0.1
       promise-call-limit: 3.0.1
-      read-package-json-fast: 3.0.2
+      read-package-json-fast: 4.0.0
       semver: 7.6.3
-      ssri: 10.0.6
+      ssri: 12.0.0
       treeverse: 3.0.0
       walk-up-path: 3.0.1
     transitivePeerDependencies:
@@ -8122,37 +8361,44 @@ snapshots:
     dependencies:
       semver: 7.6.3
 
-  '@npmcli/git@5.0.7':
+  '@npmcli/fs@4.0.0':
     dependencies:
-      '@npmcli/promise-spawn': 7.0.2
+      semver: 7.6.3
+
+  '@npmcli/git@6.0.1':
+    dependencies:
+      '@npmcli/promise-spawn': 8.0.2
+      ini: 5.0.0
       lru-cache: 10.2.2
-      npm-pick-manifest: 9.0.1
-      proc-log: 4.2.0
+      npm-pick-manifest: 10.0.0
+      proc-log: 5.0.0
       promise-inflight: 1.0.1
       promise-retry: 2.0.1
       semver: 7.6.3
-      which: 4.0.0
+      which: 5.0.0
     transitivePeerDependencies:
       - bluebird
 
-  '@npmcli/installed-package-contents@2.1.0':
+  '@npmcli/installed-package-contents@3.0.0':
     dependencies:
-      npm-bundled: 3.0.1
-      npm-normalize-package-bin: 3.0.1
+      npm-bundled: 4.0.0
+      npm-normalize-package-bin: 4.0.0
 
-  '@npmcli/map-workspaces@3.0.6':
+  '@npmcli/map-workspaces@4.0.1':
     dependencies:
-      '@npmcli/name-from-folder': 2.0.0
+      '@npmcli/name-from-folder': 3.0.0
+      '@npmcli/package-json': 6.0.1
       glob: 10.4.1
-      minimatch: 9.0.4
-      read-package-json-fast: 3.0.2
+      minimatch: 9.0.5
+    transitivePeerDependencies:
+      - bluebird
 
-  '@npmcli/metavuln-calculator@7.1.1':
+  '@npmcli/metavuln-calculator@8.0.1':
     dependencies:
-      cacache: 18.0.3
-      json-parse-even-better-errors: 3.0.2
-      pacote: 18.0.6
-      proc-log: 4.2.0
+      cacache: 19.0.1
+      json-parse-even-better-errors: 4.0.0
+      pacote: 20.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
     transitivePeerDependencies:
       - bluebird
@@ -8164,40 +8410,40 @@ snapshots:
       rimraf: 3.0.2
     optional: true
 
-  '@npmcli/name-from-folder@2.0.0': {}
+  '@npmcli/name-from-folder@3.0.0': {}
 
-  '@npmcli/node-gyp@3.0.0': {}
+  '@npmcli/node-gyp@4.0.0': {}
 
-  '@npmcli/package-json@5.2.0':
+  '@npmcli/package-json@6.0.1':
     dependencies:
-      '@npmcli/git': 5.0.7
+      '@npmcli/git': 6.0.1
       glob: 10.4.1
-      hosted-git-info: 7.0.2
-      json-parse-even-better-errors: 3.0.2
-      normalize-package-data: 6.0.1
-      proc-log: 4.2.0
+      hosted-git-info: 8.0.0
+      json-parse-even-better-errors: 4.0.0
+      normalize-package-data: 7.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
     transitivePeerDependencies:
       - bluebird
 
-  '@npmcli/promise-spawn@7.0.2':
+  '@npmcli/promise-spawn@8.0.2':
     dependencies:
-      which: 4.0.0
+      which: 5.0.0
 
-  '@npmcli/query@3.1.0':
+  '@npmcli/query@4.0.0':
     dependencies:
-      postcss-selector-parser: 6.1.0
+      postcss-selector-parser: 6.1.2
 
-  '@npmcli/redact@2.0.1': {}
+  '@npmcli/redact@3.0.0': {}
 
-  '@npmcli/run-script@8.1.0':
+  '@npmcli/run-script@9.0.1':
     dependencies:
-      '@npmcli/node-gyp': 3.0.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/promise-spawn': 7.0.2
+      '@npmcli/node-gyp': 4.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
       node-gyp: 10.1.0
-      proc-log: 4.2.0
-      which: 4.0.0
+      proc-log: 5.0.0
+      which: 5.0.0
     transitivePeerDependencies:
       - bluebird
       - supports-color
@@ -8205,18 +8451,18 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  '@playwright/test@1.48.1':
+  '@playwright/test@1.48.2':
     dependencies:
-      playwright: 1.48.1
+      playwright: 1.48.2
 
   '@polka/url@1.0.0-next.25': {}
 
   '@popperjs/core@2.11.8': {}
 
-  '@rollup/plugin-babel@5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)':
+  '@rollup/plugin-babel@5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)':
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-module-imports': 7.24.7
+      '@babel/core': 7.25.8
+      '@babel/helper-module-imports': 7.25.7
       '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
       rollup: 2.79.1
     optionalDependencies:
@@ -8318,39 +8564,39 @@ snapshots:
 
   '@sec-ant/readable-stream@0.4.1': {}
 
-  '@sigstore/bundle@2.3.2':
+  '@sigstore/bundle@3.0.0':
     dependencies:
       '@sigstore/protobuf-specs': 0.3.2
 
-  '@sigstore/core@1.1.0': {}
+  '@sigstore/core@2.0.0': {}
 
   '@sigstore/protobuf-specs@0.3.2': {}
 
-  '@sigstore/sign@2.3.2':
+  '@sigstore/sign@3.0.0':
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
-      make-fetch-happen: 13.0.1
-      proc-log: 4.2.0
+      make-fetch-happen: 14.0.3
+      proc-log: 5.0.0
       promise-retry: 2.0.1
     transitivePeerDependencies:
       - supports-color
 
-  '@sigstore/tuf@2.3.4':
+  '@sigstore/tuf@3.0.0':
     dependencies:
       '@sigstore/protobuf-specs': 0.3.2
-      tuf-js: 2.2.1
+      tuf-js: 3.0.1
     transitivePeerDependencies:
       - supports-color
 
-  '@sigstore/verify@1.2.1':
+  '@sigstore/verify@2.0.0':
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
 
-  '@sindresorhus/is@7.0.0': {}
+  '@sindresorhus/is@7.0.1': {}
 
   '@surma/rollup-plugin-off-main-thread@2.2.3':
     dependencies:
@@ -8372,17 +8618,17 @@ snapshots:
 
   '@tanstack/eslint-plugin-query@5.59.7(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@tanstack/query-core@5.59.10': {}
+  '@tanstack/query-core@5.59.16': {}
 
-  '@tanstack/react-query@5.59.10(react@18.3.1)':
+  '@tanstack/react-query@5.59.16(react@18.3.1)':
     dependencies:
-      '@tanstack/query-core': 5.59.10
+      '@tanstack/query-core': 5.59.16
       react: 18.3.1
 
   '@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -8399,7 +8645,7 @@ snapshots:
   '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.3.3)':
     dependencies:
       '@babel/generator': 7.17.7
-      '@babel/parser': 7.24.7
+      '@babel/parser': 7.25.8
       '@babel/traverse': 7.23.2
       '@babel/types': 7.17.0
       javascript-natural-sort: 0.7.1
@@ -8410,10 +8656,10 @@ snapshots:
 
   '@tufjs/canonical-json@2.0.0': {}
 
-  '@tufjs/models@2.0.1':
+  '@tufjs/models@3.0.1':
     dependencies:
       '@tufjs/canonical-json': 2.0.0
-      minimatch: 9.0.4
+      minimatch: 9.0.5
 
   '@types/acorn@4.0.6':
     dependencies:
@@ -8421,24 +8667,24 @@ snapshots:
 
   '@types/babel__core@7.20.5':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       '@types/babel__generator': 7.6.8
       '@types/babel__template': 7.4.4
       '@types/babel__traverse': 7.20.6
 
   '@types/babel__generator@7.6.8':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@types/babel__template@7.4.4':
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
 
   '@types/babel__traverse@7.20.6':
     dependencies:
-      '@babel/types': 7.25.6
+      '@babel/types': 7.25.8
 
   '@types/debug@4.1.12':
     dependencies:
@@ -8446,10 +8692,10 @@ snapshots:
 
   '@types/eslint-scope@3.7.7':
     dependencies:
-      '@types/eslint': 8.56.12
+      '@types/eslint': 9.6.1
       '@types/estree': 1.0.6
 
-  '@types/eslint@8.56.12':
+  '@types/eslint@9.6.1':
     dependencies:
       '@types/estree': 1.0.6
       '@types/json-schema': 7.0.15
@@ -8472,7 +8718,7 @@ snapshots:
 
   '@types/hoist-non-react-statics@3.3.5':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
       hoist-non-react-statics: 3.3.2
 
   '@types/http-cache-semantics@4.0.4': {}
@@ -8493,7 +8739,7 @@ snapshots:
 
   '@types/negotiator@0.6.3': {}
 
-  '@types/node@20.16.11':
+  '@types/node@20.17.1':
     dependencies:
       undici-types: 6.19.8
 
@@ -8502,19 +8748,23 @@ snapshots:
       undici-types: 6.19.8
     optional: true
 
+  '@types/node@22.8.2':
+    dependencies:
+      undici-types: 6.19.8
+
   '@types/parse-json@4.0.2': {}
 
   '@types/prop-types@15.7.12': {}
 
   '@types/react-dom@18.3.1':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
   '@types/react-transition-group@4.4.11':
     dependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
 
-  '@types/react@18.3.11':
+  '@types/react@18.3.12':
     dependencies:
       '@types/prop-types': 15.7.12
       csstype: 3.1.3
@@ -8531,14 +8781,12 @@ snapshots:
 
   '@types/use-sync-external-store@0.0.3': {}
 
-  '@types/uuid@10.0.0': {}
-
   '@types/validator@13.12.0':
     optional: true
 
   '@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@eslint-community/regexpp': 4.10.1
+      '@eslint-community/regexpp': 4.11.1
       '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       '@typescript-eslint/scope-manager': 8.10.0
       '@typescript-eslint/type-utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
@@ -8554,14 +8802,33 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.11.1
+      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
+      eslint: 9.13.0
+      graphemer: 1.4.0
+      ignore: 5.3.1
+      natural-compare: 1.4.0
+      ts-api-utils: 1.3.0(typescript@5.6.3)
+    optionalDependencies:
+      typescript: 5.6.3
+    transitivePeerDependencies:
+      - supports-color
+    optional: true
+
+  '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@eslint-community/regexpp': 4.10.1
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/type-utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@eslint-community/regexpp': 4.11.1
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
       eslint: 9.13.0
       graphemer: 1.4.0
       ignore: 5.3.1
@@ -8585,12 +8852,12 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
+      '@typescript-eslint/visitor-keys': 8.11.0
       debug: 4.3.7
       eslint: 9.13.0
     optionalDependencies:
@@ -8603,10 +8870,10 @@ snapshots:
       '@typescript-eslint/types': 8.10.0
       '@typescript-eslint/visitor-keys': 8.10.0
 
-  '@typescript-eslint/scope-manager@8.8.1':
+  '@typescript-eslint/scope-manager@8.11.0':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/visitor-keys': 8.11.0
 
   '@typescript-eslint/type-utils@8.10.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
@@ -8620,10 +8887,10 @@ snapshots:
       - eslint
       - supports-color
 
-  '@typescript-eslint/type-utils@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/type-utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       debug: 4.3.7
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8634,7 +8901,7 @@ snapshots:
 
   '@typescript-eslint/types@8.10.0': {}
 
-  '@typescript-eslint/types@8.8.1': {}
+  '@typescript-eslint/types@8.11.0': {}
 
   '@typescript-eslint/typescript-estree@8.10.0(typescript@5.6.3)':
     dependencies:
@@ -8643,7 +8910,7 @@ snapshots:
       debug: 4.3.7
       fast-glob: 3.3.2
       is-glob: 4.0.3
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       semver: 7.6.3
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8651,14 +8918,14 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.3)':
+  '@typescript-eslint/typescript-estree@8.11.0(typescript@5.6.3)':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/visitor-keys': 8.8.1
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/visitor-keys': 8.11.0
       debug: 4.3.7
       fast-glob: 3.3.2
       is-glob: 4.0.3
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       semver: 7.6.3
       ts-api-utils: 1.3.0(typescript@5.6.3)
     optionalDependencies:
@@ -8677,12 +8944,12 @@ snapshots:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@8.8.1(eslint@9.13.0)(typescript@5.6.3)':
+  '@typescript-eslint/utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0)
-      '@typescript-eslint/scope-manager': 8.8.1
-      '@typescript-eslint/types': 8.8.1
-      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
+      '@typescript-eslint/scope-manager': 8.11.0
+      '@typescript-eslint/types': 8.11.0
+      '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
       eslint: 9.13.0
     transitivePeerDependencies:
       - supports-color
@@ -8693,52 +8960,25 @@ snapshots:
       '@typescript-eslint/types': 8.10.0
       eslint-visitor-keys: 3.4.3
 
-  '@typescript-eslint/visitor-keys@8.8.1':
+  '@typescript-eslint/visitor-keys@8.11.0':
     dependencies:
-      '@typescript-eslint/types': 8.8.1
+      '@typescript-eslint/types': 8.11.0
       eslint-visitor-keys: 3.4.3
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-react@4.3.2(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))':
+  '@vitejs/plugin-react@4.3.3(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
       '@babel/core': 7.25.8
       '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8)
       '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8)
       '@types/babel__core': 7.20.5
       react-refresh: 0.14.2
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitejs/plugin-react@4.3.2(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))':
-    dependencies:
-      '@babel/core': 7.25.8
-      '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8)
-      '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8)
-      '@types/babel__core': 7.20.5
-      react-refresh: 0.14.2
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0))':
-    dependencies:
-      '@istanbuljs/schema': 0.1.3
-      debug: 4.3.7
-      istanbul-lib-coverage: 3.2.2
-      istanbul-lib-instrument: 6.0.3
-      istanbul-lib-report: 3.0.1
-      istanbul-lib-source-maps: 5.0.6
-      istanbul-reports: 3.1.7
-      magicast: 0.3.4
-      test-exclude: 7.0.1
-      tinyrainbow: 1.2.0
-      vitest: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0))':
+  '@vitest/coverage-istanbul@2.1.4(vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
       '@istanbuljs/schema': 0.1.3
       debug: 4.3.7
@@ -8747,59 +8987,51 @@ snapshots:
       istanbul-lib-report: 3.0.1
       istanbul-lib-source-maps: 5.0.6
       istanbul-reports: 3.1.7
-      magicast: 0.3.4
+      magicast: 0.3.5
       test-exclude: 7.0.1
       tinyrainbow: 1.2.0
-      vitest: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/expect@2.1.2':
+  '@vitest/expect@2.1.4':
     dependencies:
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
+      '@vitest/spy': 2.1.4
+      '@vitest/utils': 2.1.4
+      chai: 5.1.2
       tinyrainbow: 1.2.0
 
-  '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))':
-    dependencies:
-      '@vitest/spy': 2.1.2
-      estree-walker: 3.0.3
-      magic-string: 0.30.11
-    optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-
-  '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))':
     dependencies:
-      '@vitest/spy': 2.1.2
+      '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
-      magic-string: 0.30.11
+      magic-string: 0.30.12
     optionalDependencies:
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
 
-  '@vitest/pretty-format@2.1.2':
+  '@vitest/pretty-format@2.1.4':
     dependencies:
       tinyrainbow: 1.2.0
 
-  '@vitest/runner@2.1.2':
+  '@vitest/runner@2.1.4':
     dependencies:
-      '@vitest/utils': 2.1.2
+      '@vitest/utils': 2.1.4
       pathe: 1.1.2
 
-  '@vitest/snapshot@2.1.2':
+  '@vitest/snapshot@2.1.4':
     dependencies:
-      '@vitest/pretty-format': 2.1.2
-      magic-string: 0.30.11
+      '@vitest/pretty-format': 2.1.4
+      magic-string: 0.30.12
       pathe: 1.1.2
 
-  '@vitest/spy@2.1.2':
+  '@vitest/spy@2.1.4':
     dependencies:
-      tinyspy: 3.0.0
+      tinyspy: 3.0.2
 
-  '@vitest/utils@2.1.2':
+  '@vitest/utils@2.1.4':
     dependencies:
-      '@vitest/pretty-format': 2.1.2
-      loupe: 3.1.1
+      '@vitest/pretty-format': 2.1.4
+      loupe: 3.1.2
       tinyrainbow: 1.2.0
 
   '@webassemblyjs/ast@1.12.1':
@@ -8903,9 +9135,7 @@ snapshots:
 
   acorn-walk@8.3.3:
     dependencies:
-      acorn: 8.12.0
-
-  acorn@8.12.0: {}
+      acorn: 8.13.0
 
   acorn@8.13.0: {}
 
@@ -8981,9 +9211,7 @@ snapshots:
 
   argparse@2.0.1: {}
 
-  aria-query@5.1.3:
-    dependencies:
-      deep-equal: 2.2.3
+  aria-query@5.3.2: {}
 
   array-buffer-byte-length@1.0.1:
     dependencies:
@@ -9079,11 +9307,11 @@ snapshots:
     dependencies:
       possible-typed-array-names: 1.0.0
 
-  axe-core@4.10.0: {}
+  axe-core@4.10.2: {}
 
-  axe-html-reporter@2.2.11(axe-core@4.10.0):
+  axe-html-reporter@2.2.11(axe-core@4.10.2):
     dependencies:
-      axe-core: 4.10.0
+      axe-core: 4.10.2
       mustache: 4.2.0
 
   axobject-query@4.1.0: {}
@@ -9094,27 +9322,27 @@ snapshots:
       cosmiconfig: 7.1.0
       resolve: 1.22.8
 
-  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7):
+  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.8):
     dependencies:
       '@babel/compat-data': 7.24.7
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7):
+  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.25.8):
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
       core-js-compat: 3.37.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7):
+  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.25.8):
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8)
     transitivePeerDependencies:
       - supports-color
 
@@ -9127,12 +9355,13 @@ snapshots:
   base64-js@1.5.1:
     optional: true
 
-  bin-links@4.0.4:
+  bin-links@5.0.0:
     dependencies:
-      cmd-shim: 6.0.3
-      npm-normalize-package-bin: 3.0.1
-      read-cmd-shim: 4.0.0
-      write-file-atomic: 5.0.1
+      cmd-shim: 7.0.0
+      npm-normalize-package-bin: 4.0.0
+      proc-log: 5.0.0
+      read-cmd-shim: 5.0.0
+      write-file-atomic: 6.0.0
 
   bindings@1.5.0:
     dependencies:
@@ -9265,6 +9494,21 @@ snapshots:
       tar: 6.2.1
       unique-filename: 3.0.0
 
+  cacache@19.0.1:
+    dependencies:
+      '@npmcli/fs': 4.0.0
+      fs-minipass: 3.0.3
+      glob: 10.4.1
+      lru-cache: 10.2.2
+      minipass: 7.1.2
+      minipass-collect: 2.0.1
+      minipass-flush: 1.0.5
+      minipass-pipeline: 1.2.4
+      p-map: 7.0.2
+      ssri: 12.0.0
+      tar: 7.4.3
+      unique-filename: 4.0.0
+
   cacheable-lookup@7.0.0: {}
 
   cacheable-request@12.0.1:
@@ -9293,7 +9537,7 @@ snapshots:
 
   ccount@2.0.1: {}
 
-  chai@5.1.1:
+  chai@5.1.2:
     dependencies:
       assertion-error: 2.0.1
       check-error: 2.1.1
@@ -9350,6 +9594,8 @@ snapshots:
 
   chownr@2.0.0: {}
 
+  chownr@3.0.0: {}
+
   chrome-trace-event@1.0.4: {}
 
   clean-stack@2.2.0: {}
@@ -9364,7 +9610,7 @@ snapshots:
 
   clsx@2.1.1: {}
 
-  cmd-shim@6.0.3: {}
+  cmd-shim@7.0.0: {}
 
   collapse-white-space@2.1.0: {}
 
@@ -9513,10 +9759,6 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
-  debug@4.3.5:
-    dependencies:
-      ms: 2.1.2
-
   debug@4.3.7:
     dependencies:
       ms: 2.1.3
@@ -9529,28 +9771,7 @@ snapshots:
     dependencies:
       mimic-response: 3.1.0
 
-  deep-eql@5.0.2: {}
-
-  deep-equal@2.2.3:
-    dependencies:
-      array-buffer-byte-length: 1.0.1
-      call-bind: 1.0.7
-      es-get-iterator: 1.1.3
-      get-intrinsic: 1.2.4
-      is-arguments: 1.1.1
-      is-array-buffer: 3.0.4
-      is-date-object: 1.0.5
-      is-regex: 1.1.4
-      is-shared-array-buffer: 1.0.3
-      isarray: 2.0.5
-      object-is: 1.1.6
-      object-keys: 1.1.1
-      object.assign: 4.1.5
-      regexp.prototype.flags: 1.5.2
-      side-channel: 1.0.6
-      which-boxed-primitive: 1.0.2
-      which-collection: 1.0.2
-      which-typed-array: 1.1.15
+  deep-eql@5.0.2: {}
 
   deep-extend@0.6.0:
     optional: true
@@ -9599,11 +9820,6 @@ snapshots:
     dependencies:
       esutils: 2.0.3
 
-  dom-helpers@5.2.1:
-    dependencies:
-      '@babel/runtime': 7.25.6
-      csstype: 3.1.3
-
   dom-serializer@2.0.0:
     dependencies:
       domelementtype: 2.3.0
@@ -9644,8 +9860,6 @@ snapshots:
       react: 18.3.1
       size-sensor: 1.0.2
 
-  echarts-stat@1.2.0: {}
-
   echarts@5.5.1:
     dependencies:
       tslib: 2.3.0
@@ -9686,11 +9900,6 @@ snapshots:
       once: 1.4.0
     optional: true
 
-  enhanced-resolve@5.17.0:
-    dependencies:
-      graceful-fs: 4.2.11
-      tapable: 2.2.1
-
   enhanced-resolve@5.17.1:
     dependencies:
       graceful-fs: 4.2.11
@@ -9761,18 +9970,6 @@ snapshots:
 
   es-errors@1.3.0: {}
 
-  es-get-iterator@1.1.3:
-    dependencies:
-      call-bind: 1.0.7
-      get-intrinsic: 1.2.4
-      has-symbols: 1.0.3
-      is-arguments: 1.1.1
-      is-map: 2.0.3
-      is-set: 2.0.3
-      is-string: 1.0.7
-      isarray: 2.0.5
-      stop-iteration-iterator: 1.0.0
-
   es-iterator-helpers@1.0.19:
     dependencies:
       call-bind: 1.0.7
@@ -9814,6 +10011,20 @@ snapshots:
 
   es6-error@4.1.1: {}
 
+  esast-util-from-estree@2.0.0:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      devlop: 1.1.0
+      estree-util-visit: 2.0.0
+      unist-util-position-from-estree: 2.0.0
+
+  esast-util-from-js@2.0.1:
+    dependencies:
+      '@types/estree-jsx': 1.0.5
+      acorn: 8.13.0
+      esast-util-from-estree: 2.0.0
+      vfile-message: 4.0.2
+
   esbuild@0.21.5:
     optionalDependencies:
       '@esbuild/aix-ppc64': 0.21.5
@@ -9867,8 +10078,6 @@ snapshots:
       '@esbuild/win32-ia32': 0.23.1
       '@esbuild/win32-x64': 0.23.1
 
-  escalade@3.1.2: {}
-
   escalade@3.2.0: {}
 
   escape-html@1.0.3:
@@ -9882,13 +10091,13 @@ snapshots:
     dependencies:
       '@next/eslint-plugin-next': 14.2.14
       '@rushstack/eslint-patch': 1.10.3
-      '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
-      eslint-plugin-jsx-a11y: 6.10.0(eslint@9.13.0)
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
+      eslint-plugin-jsx-a11y: 6.10.2(eslint@9.13.0)
       eslint-plugin-react: 7.34.2(eslint@9.13.0)
       eslint-plugin-react-hooks: 4.6.2(eslint@9.13.0)
     optionalDependencies:
@@ -9910,58 +10119,47 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0):
+  eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       '@nolyfill/is-core-module': 1.0.39
-      debug: 4.3.5
-      enhanced-resolve: 5.17.0
+      debug: 4.3.7
+      enhanced-resolve: 5.17.1
       eslint: 9.13.0
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
       fast-glob: 3.3.2
       get-tsconfig: 4.7.5
       is-bun-module: 1.1.0
       is-glob: 4.0.3
     optionalDependencies:
-      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
     transitivePeerDependencies:
       - '@typescript-eslint/parser'
       - eslint-import-resolver-node
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
       '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
+      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
-    dependencies:
-      debug: 3.2.7
-    optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
-      eslint: 9.13.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
+  eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0)
     transitivePeerDependencies:
       - supports-color
 
-  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0):
     dependencies:
       '@rtsao/scc': 1.1.0
       array-includes: 3.1.8
@@ -9972,7 +10170,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0))(eslint@9.13.0))(eslint@9.13.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -9990,7 +10188,7 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0):
+  eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
     dependencies:
       '@rtsao/scc': 1.1.0
       array-includes: 3.1.8
@@ -10001,7 +10199,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 9.13.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0)
+      eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.13.0)
       hasown: 2.0.2
       is-core-module: 2.15.1
       is-glob: 4.0.3
@@ -10013,23 +10211,22 @@ snapshots:
       string.prototype.trimend: 1.0.8
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 8.8.1(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-jsx-a11y@6.10.0(eslint@9.13.0):
+  eslint-plugin-jsx-a11y@6.10.2(eslint@9.13.0):
     dependencies:
-      aria-query: 5.1.3
+      aria-query: 5.3.2
       array-includes: 3.1.8
       array.prototype.flatmap: 1.3.2
       ast-types-flow: 0.0.8
-      axe-core: 4.10.0
+      axe-core: 4.10.2
       axobject-query: 4.1.0
       damerau-levenshtein: 1.0.8
       emoji-regex: 9.2.2
-      es-iterator-helpers: 1.0.19
       eslint: 9.13.0
       hasown: 2.0.2
       jsx-ast-utils: 3.3.5
@@ -10039,6 +10236,10 @@ snapshots:
       safe-regex-test: 1.0.3
       string.prototype.includes: 2.0.1
 
+  eslint-plugin-promise@7.1.0(eslint@9.13.0):
+    dependencies:
+      eslint: 9.13.0
+
   eslint-plugin-react-hooks@4.6.2(eslint@9.13.0):
     dependencies:
       eslint: 9.13.0
@@ -10065,11 +10266,17 @@ snapshots:
       semver: 6.3.1
       string.prototype.matchall: 4.0.11
 
-  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
     dependencies:
       eslint: 9.13.0
     optionalDependencies:
-      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+
+  eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0):
+    dependencies:
+      eslint: 9.13.0
+    optionalDependencies:
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
 
   eslint-scope@5.1.1:
     dependencies:
@@ -10156,6 +10363,11 @@ snapshots:
 
   estree-util-is-identifier-name@3.0.0: {}
 
+  estree-util-scope@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      devlop: 1.1.0
+
   estree-util-to-js@2.0.0:
     dependencies:
       '@types/estree-jsx': 1.0.5
@@ -10182,6 +10394,8 @@ snapshots:
   expand-template@2.0.3:
     optional: true
 
+  expect-type@1.1.0: {}
+
   exponential-backoff@3.1.1: {}
 
   extend@3.0.2: {}
@@ -10275,7 +10489,7 @@ snapshots:
       react: 18.3.1
       react-fast-compare: 2.0.4
       tiny-warning: 1.0.3
-      tslib: 2.6.3
+      tslib: 2.7.0
 
   fs-constants@1.0.0:
     optional: true
@@ -10374,7 +10588,7 @@ snapshots:
     dependencies:
       foreground-child: 3.2.1
       jackspeak: 2.3.6
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       minipass: 7.1.2
       path-scurry: 1.11.1
 
@@ -10382,7 +10596,7 @@ snapshots:
     dependencies:
       foreground-child: 3.2.1
       jackspeak: 3.4.0
-      minimatch: 9.0.4
+      minimatch: 9.0.5
       minipass: 7.1.2
       path-scurry: 1.11.1
 
@@ -10428,9 +10642,9 @@ snapshots:
     dependencies:
       get-intrinsic: 1.2.4
 
-  got@14.4.2:
+  got@14.4.3:
     dependencies:
-      '@sindresorhus/is': 7.0.0
+      '@sindresorhus/is': 7.0.1
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
       cacheable-request: 12.0.1
@@ -10524,18 +10738,18 @@ snapshots:
     dependencies:
       react-is: 16.13.1
 
-  hosted-git-info@7.0.2:
+  hosted-git-info@8.0.0:
     dependencies:
       lru-cache: 10.2.2
 
-  hpke-js@1.4.3:
+  hpke-js@1.5.0:
     dependencies:
       '@hpke/chacha20poly1305': 1.4.3
       '@hpke/common': 1.4.3
       '@hpke/core': 1.4.3
       '@hpke/dhkem-x25519': 1.4.3
       '@hpke/dhkem-x448': 1.4.3
-      '@noble/hashes': 1.4.0
+      '@noble/hashes': 1.5.0
 
   html-escaper@2.0.2: {}
 
@@ -10604,9 +10818,9 @@ snapshots:
 
   i18next-resources-to-backend@1.2.1:
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
 
-  i18next@23.15.2:
+  i18next@23.16.4:
     dependencies:
       '@babel/runtime': 7.25.6
 
@@ -10624,9 +10838,9 @@ snapshots:
   ieee754@1.2.1:
     optional: true
 
-  ignore-walk@6.0.5:
+  ignore-walk@7.0.0:
     dependencies:
-      minimatch: 9.0.4
+      minimatch: 9.0.5
 
   ignore@5.3.1: {}
 
@@ -10655,6 +10869,8 @@ snapshots:
   ini@1.3.8:
     optional: true
 
+  ini@5.0.0: {}
+
   inline-style-parser@0.1.1: {}
 
   inline-style-parser@0.2.4: {}
@@ -10677,11 +10893,6 @@ snapshots:
       is-alphabetical: 2.0.1
       is-decimal: 2.0.1
 
-  is-arguments@1.1.1:
-    dependencies:
-      call-bind: 1.0.7
-      has-tostringtag: 1.0.2
-
   is-array-buffer@3.0.4:
     dependencies:
       call-bind: 1.0.7
@@ -10766,10 +10977,6 @@ snapshots:
 
   is-plain-object@5.0.0: {}
 
-  is-reference@3.0.2:
-    dependencies:
-      '@types/estree': 1.0.6
-
   is-regex@1.1.4:
     dependencies:
       call-bind: 1.0.7
@@ -10822,8 +11029,8 @@ snapshots:
 
   istanbul-lib-instrument@6.0.3:
     dependencies:
-      '@babel/core': 7.24.7
-      '@babel/parser': 7.25.6
+      '@babel/core': 7.25.8
+      '@babel/parser': 7.25.8
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 7.6.3
@@ -10886,7 +11093,7 @@ snapshots:
 
   jest-worker@27.5.1:
     dependencies:
-      '@types/node': 20.16.11
+      '@types/node': 22.8.2
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
@@ -10908,7 +11115,7 @@ snapshots:
 
   json-parse-even-better-errors@2.3.1: {}
 
-  json-parse-even-better-errors@3.0.2: {}
+  json-parse-even-better-errors@4.0.0: {}
 
   json-schema-traverse@0.4.1: {}
 
@@ -11018,6 +11225,8 @@ snapshots:
     dependencies:
       get-func-name: 2.0.2
 
+  loupe@3.1.2: {}
+
   lowercase-keys@3.0.0: {}
 
   lru-cache@10.2.2: {}
@@ -11037,14 +11246,14 @@ snapshots:
     dependencies:
       sourcemap-codec: 1.4.8
 
-  magic-string@0.30.11:
+  magic-string@0.30.12:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.0
 
-  magicast@0.3.4:
+  magicast@0.3.5:
     dependencies:
-      '@babel/parser': 7.25.6
-      '@babel/types': 7.25.6
+      '@babel/parser': 7.25.8
+      '@babel/types': 7.25.8
       source-map-js: 1.2.0
 
   make-dir@4.0.0:
@@ -11068,6 +11277,22 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  make-fetch-happen@14.0.3:
+    dependencies:
+      '@npmcli/agent': 3.0.0
+      cacache: 19.0.1
+      http-cache-semantics: 4.1.1
+      minipass: 7.1.2
+      minipass-fetch: 4.0.0
+      minipass-flush: 1.0.5
+      minipass-pipeline: 1.2.4
+      negotiator: 1.0.0
+      proc-log: 5.0.0
+      promise-retry: 2.0.1
+      ssri: 12.0.0
+    transitivePeerDependencies:
+      - supports-color
+
   make-fetch-happen@9.1.0:
     dependencies:
       agentkeepalive: 4.5.0
@@ -11468,6 +11693,10 @@ snapshots:
     dependencies:
       brace-expansion: 2.0.1
 
+  minimatch@9.0.5:
+    dependencies:
+      brace-expansion: 2.0.1
+
   minimist@1.2.8: {}
 
   minipass-collect@1.0.2:
@@ -11496,6 +11725,14 @@ snapshots:
     optionalDependencies:
       encoding: 0.1.13
 
+  minipass-fetch@4.0.0:
+    dependencies:
+      minipass: 7.1.2
+      minipass-sized: 1.0.3
+      minizlib: 3.0.1
+    optionalDependencies:
+      encoding: 0.1.13
+
   minipass-flush@1.0.5:
     dependencies:
       minipass: 3.3.6
@@ -11521,11 +11758,18 @@ snapshots:
       minipass: 3.3.6
       yallist: 4.0.0
 
+  minizlib@3.0.1:
+    dependencies:
+      minipass: 7.1.2
+      rimraf: 5.0.10
+
   mkdirp-classic@0.5.3:
     optional: true
 
   mkdirp@1.0.4: {}
 
+  mkdirp@3.0.1: {}
+
   moment-timezone@0.5.45:
     dependencies:
       moment: 2.30.1
@@ -11539,8 +11783,6 @@ snapshots:
   ms@2.0.0:
     optional: true
 
-  ms@2.1.2: {}
-
   ms@2.1.3: {}
 
   mustache@4.2.0: {}
@@ -11554,9 +11796,11 @@ snapshots:
 
   negotiator@0.6.3: {}
 
+  negotiator@1.0.0: {}
+
   neo-async@2.6.2: {}
 
-  next@14.2.14(@babel/core@7.24.7)(@playwright/test@1.48.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  next@14.2.14(@babel/core@7.25.8)(@playwright/test@1.48.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@next/env': 14.2.14
       '@swc/helpers': 0.5.5
@@ -11566,7 +11810,7 @@ snapshots:
       postcss: 8.4.31
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-      styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1)
+      styled-jsx: 5.1.1(@babel/core@7.25.8)(react@18.3.1)
     optionalDependencies:
       '@next/swc-darwin-arm64': 14.2.14
       '@next/swc-darwin-x64': 14.2.14
@@ -11577,7 +11821,7 @@ snapshots:
       '@next/swc-win32-arm64-msvc': 14.2.14
       '@next/swc-win32-ia32-msvc': 14.2.14
       '@next/swc-win32-x64-msvc': 14.2.14
-      '@playwright/test': 1.48.1
+      '@playwright/test': 1.48.2
     transitivePeerDependencies:
       - '@babel/core'
       - babel-plugin-macros
@@ -11637,53 +11881,56 @@ snapshots:
     dependencies:
       abbrev: 2.0.0
 
-  normalize-package-data@6.0.1:
+  nopt@8.0.0:
     dependencies:
-      hosted-git-info: 7.0.2
-      is-core-module: 2.15.1
+      abbrev: 2.0.0
+
+  normalize-package-data@7.0.0:
+    dependencies:
+      hosted-git-info: 8.0.0
       semver: 7.6.3
       validate-npm-package-license: 3.0.4
 
   normalize-url@8.0.1: {}
 
-  npm-bundled@3.0.1:
+  npm-bundled@4.0.0:
     dependencies:
-      npm-normalize-package-bin: 3.0.1
+      npm-normalize-package-bin: 4.0.0
 
-  npm-install-checks@6.3.0:
+  npm-install-checks@7.1.0:
     dependencies:
       semver: 7.6.3
 
-  npm-normalize-package-bin@3.0.1: {}
+  npm-normalize-package-bin@4.0.0: {}
 
-  npm-package-arg@11.0.2:
+  npm-package-arg@12.0.0:
     dependencies:
-      hosted-git-info: 7.0.2
-      proc-log: 4.2.0
+      hosted-git-info: 8.0.0
+      proc-log: 5.0.0
       semver: 7.6.3
-      validate-npm-package-name: 5.0.1
+      validate-npm-package-name: 6.0.0
 
-  npm-packlist@8.0.2:
+  npm-packlist@9.0.0:
     dependencies:
-      ignore-walk: 6.0.5
+      ignore-walk: 7.0.0
 
-  npm-pick-manifest@9.0.1:
+  npm-pick-manifest@10.0.0:
     dependencies:
-      npm-install-checks: 6.3.0
-      npm-normalize-package-bin: 3.0.1
-      npm-package-arg: 11.0.2
+      npm-install-checks: 7.1.0
+      npm-normalize-package-bin: 4.0.0
+      npm-package-arg: 12.0.0
       semver: 7.6.3
 
-  npm-registry-fetch@17.1.0:
+  npm-registry-fetch@18.0.2:
     dependencies:
-      '@npmcli/redact': 2.0.1
+      '@npmcli/redact': 3.0.0
       jsonparse: 1.3.1
-      make-fetch-happen: 13.0.1
+      make-fetch-happen: 14.0.3
       minipass: 7.1.2
-      minipass-fetch: 3.0.5
-      minizlib: 2.1.2
-      npm-package-arg: 11.0.2
-      proc-log: 4.2.0
+      minipass-fetch: 4.0.0
+      minizlib: 3.0.1
+      npm-package-arg: 12.0.0
+      proc-log: 5.0.0
     transitivePeerDependencies:
       - supports-color
 
@@ -11703,11 +11950,6 @@ snapshots:
 
   object-inspect@1.13.1: {}
 
-  object-is@1.1.6:
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-
   object-keys@1.1.1: {}
 
   object.assign@4.1.5:
@@ -11780,10 +12022,6 @@ snapshots:
       type-check: 0.4.0
       word-wrap: 1.2.5
 
-  otpauth@9.3.4:
-    dependencies:
-      '@noble/hashes': 1.5.0
-
   p-cancelable@4.0.1: {}
 
   p-limit@3.1.0:
@@ -11806,6 +12044,8 @@ snapshots:
     dependencies:
       aggregate-error: 3.1.0
 
+  p-map@7.0.2: {}
+
   p-retry@4.6.2:
     dependencies:
       '@types/retry': 0.12.0
@@ -11815,24 +12055,47 @@ snapshots:
 
   packageurl-js@1.0.2: {}
 
-  pacote@18.0.6:
+  pacote@19.0.1:
     dependencies:
-      '@npmcli/git': 5.0.7
-      '@npmcli/installed-package-contents': 2.1.0
-      '@npmcli/package-json': 5.2.0
-      '@npmcli/promise-spawn': 7.0.2
-      '@npmcli/run-script': 8.1.0
-      cacache: 18.0.3
+      '@npmcli/git': 6.0.1
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
+      '@npmcli/run-script': 9.0.1
+      cacache: 19.0.1
       fs-minipass: 3.0.3
       minipass: 7.1.2
-      npm-package-arg: 11.0.2
-      npm-packlist: 8.0.2
-      npm-pick-manifest: 9.0.1
-      npm-registry-fetch: 17.1.0
-      proc-log: 4.2.0
+      npm-package-arg: 12.0.0
+      npm-packlist: 9.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      proc-log: 5.0.0
       promise-retry: 2.0.1
-      sigstore: 2.3.1
-      ssri: 10.0.6
+      sigstore: 3.0.0
+      ssri: 12.0.0
+      tar: 6.2.1
+    transitivePeerDependencies:
+      - bluebird
+      - supports-color
+
+  pacote@20.0.0:
+    dependencies:
+      '@npmcli/git': 6.0.1
+      '@npmcli/installed-package-contents': 3.0.0
+      '@npmcli/package-json': 6.0.1
+      '@npmcli/promise-spawn': 8.0.2
+      '@npmcli/run-script': 9.0.1
+      cacache: 19.0.1
+      fs-minipass: 3.0.3
+      minipass: 7.1.2
+      npm-package-arg: 12.0.0
+      npm-packlist: 9.0.0
+      npm-pick-manifest: 10.0.0
+      npm-registry-fetch: 18.0.2
+      proc-log: 5.0.0
+      promise-retry: 2.0.1
+      sigstore: 3.0.0
+      ssri: 12.0.0
       tar: 6.2.1
     transitivePeerDependencies:
       - bluebird
@@ -11842,9 +12105,9 @@ snapshots:
     dependencies:
       callsites: 3.1.0
 
-  parse-conflict-json@3.0.1:
+  parse-conflict-json@4.0.0:
     dependencies:
-      json-parse-even-better-errors: 3.0.2
+      json-parse-even-better-errors: 4.0.0
       just-diff: 6.0.2
       just-diff-apply: 5.5.0
 
@@ -11861,7 +12124,7 @@ snapshots:
 
   parse-json@5.2.0:
     dependencies:
-      '@babel/code-frame': 7.24.7
+      '@babel/code-frame': 7.25.7
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
@@ -11908,17 +12171,9 @@ snapshots:
 
   pathval@2.0.0: {}
 
-  periscopic@3.1.0:
-    dependencies:
-      '@types/estree': 1.0.6
-      estree-walker: 3.0.3
-      is-reference: 3.0.2
-
   pg-connection-string@2.6.4:
     optional: true
 
-  picocolors@1.0.1: {}
-
   picocolors@1.1.0: {}
 
   picomatch@2.3.1: {}
@@ -11932,17 +12187,17 @@ snapshots:
       pvutils: 1.1.3
       tslib: 2.6.3
 
-  playwright-core@1.48.1: {}
+  playwright-core@1.48.2: {}
 
-  playwright@1.48.1:
+  playwright@1.48.2:
     dependencies:
-      playwright-core: 1.48.1
+      playwright-core: 1.48.2
     optionalDependencies:
       fsevents: 2.3.2
 
   possible-typed-array-names@1.0.0: {}
 
-  postcss-selector-parser@6.1.0:
+  postcss-selector-parser@6.1.2:
     dependencies:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
@@ -11956,7 +12211,7 @@ snapshots:
   postcss@8.4.38:
     dependencies:
       nanoid: 3.3.7
-      picocolors: 1.0.1
+      picocolors: 1.1.0
       source-map-js: 1.2.0
 
   preact@10.12.1: {}
@@ -11989,7 +12244,9 @@ snapshots:
 
   proc-log@4.2.0: {}
 
-  proggy@2.0.0: {}
+  proc-log@5.0.0: {}
+
+  proggy@3.0.0: {}
 
   promise-all-reject-late@1.0.1: {}
 
@@ -12065,18 +12322,18 @@ snapshots:
       react: 18.3.1
       scheduler: 0.23.2
 
-  react-error-boundary@4.0.13(react@18.3.1):
+  react-error-boundary@4.1.2(react@18.3.1):
     dependencies:
-      '@babel/runtime': 7.24.7
+      '@babel/runtime': 7.25.6
       react: 18.3.1
 
   react-fast-compare@2.0.4: {}
 
-  react-i18next@15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  react-i18next@15.1.0(i18next@23.16.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
-      '@babel/runtime': 7.25.4
+      '@babel/runtime': 7.25.6
       html-parse-stringify: 3.0.1
-      i18next: 23.15.2
+      i18next: 23.16.4
       react: 18.3.1
     optionalDependencies:
       react-dom: 18.3.1(react@18.3.1)
@@ -12096,36 +12353,27 @@ snapshots:
 
   react-is@18.3.1: {}
 
-  react-redux@9.1.2(@types/react@18.3.11)(react@18.3.1)(redux@5.0.1):
+  react-redux@9.1.2(@types/react@18.3.12)(react@18.3.1)(redux@5.0.1):
     dependencies:
       '@types/use-sync-external-store': 0.0.3
       react: 18.3.1
       use-sync-external-store: 1.2.2(react@18.3.1)
     optionalDependencies:
-      '@types/react': 18.3.11
+      '@types/react': 18.3.12
       redux: 5.0.1
 
   react-refresh@0.14.2: {}
 
-  react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
-    dependencies:
-      '@babel/runtime': 7.24.7
-      dom-helpers: 5.2.1
-      loose-envify: 1.4.0
-      prop-types: 15.8.1
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-
   react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
 
-  read-cmd-shim@4.0.0: {}
+  read-cmd-shim@5.0.0: {}
 
-  read-package-json-fast@3.0.2:
+  read-package-json-fast@4.0.0:
     dependencies:
-      json-parse-even-better-errors: 3.0.2
-      npm-normalize-package-bin: 3.0.1
+      json-parse-even-better-errors: 4.0.0
+      npm-normalize-package-bin: 4.0.0
 
   readable-stream@3.6.2:
     dependencies:
@@ -12134,6 +12382,36 @@ snapshots:
       util-deprecate: 1.0.2
     optional: true
 
+  recma-build-jsx@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      estree-util-build-jsx: 3.0.1
+      vfile: 6.0.3
+
+  recma-jsx@1.0.0(acorn@8.13.0):
+    dependencies:
+      acorn-jsx: 5.3.2(acorn@8.13.0)
+      estree-util-to-js: 2.0.0
+      recma-parse: 1.0.0
+      recma-stringify: 1.0.0
+      unified: 11.0.5
+    transitivePeerDependencies:
+      - acorn
+
+  recma-parse@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      esast-util-from-js: 2.0.1
+      unified: 11.0.5
+      vfile: 6.0.3
+
+  recma-stringify@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      estree-util-to-js: 2.0.0
+      unified: 11.0.5
+      vfile: 6.0.3
+
   redux@5.0.1: {}
 
   reflect.getprototypeof@1.0.6:
@@ -12178,6 +12456,14 @@ snapshots:
     dependencies:
       jsesc: 0.5.0
 
+  rehype-recma@1.0.0:
+    dependencies:
+      '@types/estree': 1.0.6
+      '@types/hast': 3.0.4
+      hast-util-to-estree: 3.1.0
+    transitivePeerDependencies:
+      - supports-color
+
   remark-mdx@3.0.1:
     dependencies:
       mdast-util-mdx: 3.0.0
@@ -12202,7 +12488,7 @@ snapshots:
       unified: 11.0.5
       vfile: 6.0.3
 
-  remeda@2.15.0:
+  remeda@2.16.0:
     dependencies:
       type-fest: 4.26.1
 
@@ -12246,6 +12532,10 @@ snapshots:
       glob: 7.2.3
     optional: true
 
+  rimraf@5.0.10:
+    dependencies:
+      glob: 10.4.1
+
   roarr@2.15.4:
     dependencies:
       boolean: 3.2.0
@@ -12328,7 +12618,7 @@ snapshots:
   sequelize-pool@7.1.0:
     optional: true
 
-  sequelize@6.37.3(sqlite3@5.1.7):
+  sequelize@6.37.5(sqlite3@5.1.7):
     dependencies:
       '@types/debug': 4.1.12
       '@types/validator': 13.12.0
@@ -12404,14 +12694,14 @@ snapshots:
 
   signal-exit@4.1.0: {}
 
-  sigstore@2.3.1:
+  sigstore@3.0.0:
     dependencies:
-      '@sigstore/bundle': 2.3.2
-      '@sigstore/core': 1.1.0
+      '@sigstore/bundle': 3.0.0
+      '@sigstore/core': 2.0.0
       '@sigstore/protobuf-specs': 0.3.2
-      '@sigstore/sign': 2.3.2
-      '@sigstore/tuf': 2.3.4
-      '@sigstore/verify': 1.2.1
+      '@sigstore/sign': 3.0.0
+      '@sigstore/tuf': 3.0.0
+      '@sigstore/verify': 2.0.0
     transitivePeerDependencies:
       - supports-color
 
@@ -12521,7 +12811,7 @@ snapshots:
     dependencies:
       minipass: 7.1.2
 
-  ssri@11.0.0:
+  ssri@12.0.0:
     dependencies:
       minipass: 7.1.2
 
@@ -12540,10 +12830,6 @@ snapshots:
 
   std-env@3.7.0: {}
 
-  stop-iteration-iterator@1.0.0:
-    dependencies:
-      internal-slot: 1.0.7
-
   streamsearch@1.1.0: {}
 
   string-width@4.2.3:
@@ -12639,12 +12925,12 @@ snapshots:
     dependencies:
       inline-style-parser: 0.2.4
 
-  styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.3.1):
+  styled-jsx@5.1.1(@babel/core@7.25.8)(react@18.3.1):
     dependencies:
       client-only: 0.0.1
       react: 18.3.1
     optionalDependencies:
-      '@babel/core': 7.24.7
+      '@babel/core': 7.25.8
 
   stylis@4.2.0: {}
 
@@ -12698,6 +12984,15 @@ snapshots:
       mkdirp: 1.0.4
       yallist: 4.0.0
 
+  tar@7.4.3:
+    dependencies:
+      '@isaacs/fs-minipass': 4.0.1
+      chownr: 3.0.0
+      minipass: 7.1.2
+      minizlib: 3.0.1
+      mkdirp: 3.0.1
+      yallist: 5.0.0
+
   temp-dir@2.0.0: {}
 
   tempy@0.6.0:
@@ -12744,13 +13039,13 @@ snapshots:
 
   tinybench@2.9.0: {}
 
-  tinyexec@0.3.0: {}
+  tinyexec@0.3.1: {}
 
   tinypool@1.0.1: {}
 
   tinyrainbow@1.2.0: {}
 
-  tinyspy@3.0.0: {}
+  tinyspy@3.0.2: {}
 
   to-fast-properties@2.0.0: {}
 
@@ -12797,18 +13092,20 @@ snapshots:
 
   tslib@2.6.3: {}
 
-  tsx@4.19.1:
+  tslib@2.7.0: {}
+
+  tsx@4.19.2:
     dependencies:
       esbuild: 0.23.1
       get-tsconfig: 4.7.5
     optionalDependencies:
       fsevents: 2.3.3
 
-  tuf-js@2.2.1:
+  tuf-js@3.0.1:
     dependencies:
-      '@tufjs/models': 2.0.1
+      '@tufjs/models': 3.0.1
       debug: 4.3.7
-      make-fetch-happen: 13.0.1
+      make-fetch-happen: 14.0.3
     transitivePeerDependencies:
       - supports-color
 
@@ -12865,11 +13162,11 @@ snapshots:
       is-typed-array: 1.1.13
       possible-typed-array-names: 1.0.0
 
-  typescript-eslint@8.10.0(eslint@9.13.0)(typescript@5.6.3):
+  typescript-eslint@8.11.0(eslint@9.13.0)(typescript@5.6.3):
     dependencies:
-      '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/parser': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
-      '@typescript-eslint/utils': 8.10.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3))(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
+      '@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
     optionalDependencies:
       typescript: 5.6.3
     transitivePeerDependencies:
@@ -12923,6 +13220,10 @@ snapshots:
     dependencies:
       unique-slug: 4.0.0
 
+  unique-filename@4.0.0:
+    dependencies:
+      unique-slug: 5.0.0
+
   unique-slug@2.0.2:
     dependencies:
       imurmurhash: 0.1.4
@@ -12932,6 +13233,10 @@ snapshots:
     dependencies:
       imurmurhash: 0.1.4
 
+  unique-slug@5.0.0:
+    dependencies:
+      imurmurhash: 0.1.4
+
   unique-string@2.0.0:
     dependencies:
       crypto-random-string: 2.0.0
@@ -12973,7 +13278,7 @@ snapshots:
   update-browserslist-db@1.0.16(browserslist@4.23.1):
     dependencies:
       browserslist: 4.23.1
-      escalade: 3.1.2
+      escalade: 3.2.0
       picocolors: 1.1.0
 
   update-browserslist-db@1.1.1(browserslist@4.24.0):
@@ -12990,7 +13295,7 @@ snapshots:
 
   url-template@3.1.1: {}
 
-  use-debounce@10.0.3(react@18.3.1):
+  use-debounce@10.0.4(react@18.3.1):
     dependencies:
       react: 18.3.1
 
@@ -13009,6 +13314,8 @@ snapshots:
 
   uuid@10.0.0: {}
 
+  uuid@11.0.1: {}
+
   uuid@8.3.2:
     optional: true
 
@@ -13023,7 +13330,7 @@ snapshots:
       spdx-correct: 3.2.0
       spdx-expression-parse: 3.0.1
 
-  validate-npm-package-name@5.0.1: {}
+  validate-npm-package-name@6.0.0: {}
 
   validator@13.12.0:
     optional: true
@@ -13041,12 +13348,12 @@ snapshots:
       '@types/unist': 3.0.3
       vfile-message: 4.0.2
 
-  vite-node@2.1.2(@types/node@20.16.11)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7
       pathe: 1.1.2
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -13057,120 +13364,51 @@ snapshots:
       - supports-color
       - terser
 
-  vite-node@2.1.2(@types/node@22.7.6)(terser@5.36.0):
+  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0)):
     dependencies:
-      cac: 6.7.14
       debug: 4.3.7
-      pathe: 1.1.2
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-    transitivePeerDependencies:
-      - '@types/node'
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-
-  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0)):
-    dependencies:
-      debug: 4.3.5
-      globrex: 0.1.2
-      tsconfck: 3.1.0(typescript@5.6.3)
-    optionalDependencies:
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-
-  vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0)):
-    dependencies:
-      debug: 4.3.5
       globrex: 0.1.2
       tsconfck: 3.1.0(typescript@5.6.3)
     optionalDependencies:
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  vite@5.3.1(@types/node@20.16.11)(terser@5.36.0):
-    dependencies:
-      esbuild: 0.21.5
-      postcss: 8.4.38
-      rollup: 4.18.0
-    optionalDependencies:
-      '@types/node': 20.16.11
-      fsevents: 2.3.3
-      terser: 5.36.0
-
-  vite@5.3.1(@types/node@22.7.6)(terser@5.36.0):
+  vite@5.3.1(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.38
       rollup: 4.18.0
     optionalDependencies:
-      '@types/node': 22.7.6
+      '@types/node': 20.17.1
       fsevents: 2.3.3
       terser: 5.36.0
 
-  vitest@2.1.2(@types/node@20.16.11)(terser@5.36.0):
-    dependencies:
-      '@vitest/expect': 2.1.2
-      '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@20.16.11)(terser@5.36.0))
-      '@vitest/pretty-format': 2.1.2
-      '@vitest/runner': 2.1.2
-      '@vitest/snapshot': 2.1.2
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
-      debug: 4.3.7
-      magic-string: 0.30.11
-      pathe: 1.1.2
-      std-env: 3.7.0
-      tinybench: 2.9.0
-      tinyexec: 0.3.0
-      tinypool: 1.0.1
-      tinyrainbow: 1.2.0
-      vite: 5.3.1(@types/node@20.16.11)(terser@5.36.0)
-      vite-node: 2.1.2(@types/node@20.16.11)(terser@5.36.0)
-      why-is-node-running: 2.3.0
-    optionalDependencies:
-      '@types/node': 20.16.11
-    transitivePeerDependencies:
-      - less
-      - lightningcss
-      - msw
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-
-  vitest@2.1.2(@types/node@22.7.6)(terser@5.36.0):
+  vitest@2.1.4(@types/node@20.17.1)(terser@5.36.0):
     dependencies:
-      '@vitest/expect': 2.1.2
-      '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.3.1(@types/node@22.7.6)(terser@5.36.0))
-      '@vitest/pretty-format': 2.1.2
-      '@vitest/runner': 2.1.2
-      '@vitest/snapshot': 2.1.2
-      '@vitest/spy': 2.1.2
-      '@vitest/utils': 2.1.2
-      chai: 5.1.1
+      '@vitest/expect': 2.1.4
+      '@vitest/mocker': 2.1.4(vite@5.3.1(@types/node@20.17.1)(terser@5.36.0))
+      '@vitest/pretty-format': 2.1.4
+      '@vitest/runner': 2.1.4
+      '@vitest/snapshot': 2.1.4
+      '@vitest/spy': 2.1.4
+      '@vitest/utils': 2.1.4
+      chai: 5.1.2
       debug: 4.3.7
-      magic-string: 0.30.11
+      expect-type: 1.1.0
+      magic-string: 0.30.12
       pathe: 1.1.2
       std-env: 3.7.0
       tinybench: 2.9.0
-      tinyexec: 0.3.0
+      tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.3.1(@types/node@22.7.6)(terser@5.36.0)
-      vite-node: 2.1.2(@types/node@22.7.6)(terser@5.36.0)
+      vite: 5.3.1(@types/node@20.17.1)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@20.17.1)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
-      '@types/node': 22.7.6
+      '@types/node': 20.17.1
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -13195,7 +13433,7 @@ snapshots:
   webpack-bundle-analyzer@4.10.1:
     dependencies:
       '@discoveryjs/json-ext': 0.5.7
-      acorn: 8.12.0
+      acorn: 8.13.0
       acorn-walk: 8.3.3
       commander: 7.2.0
       debounce: 1.2.1
@@ -13307,6 +13545,10 @@ snapshots:
     dependencies:
       isexe: 3.1.1
 
+  which@5.0.0:
+    dependencies:
+      isexe: 3.1.1
+
   why-is-node-running@2.3.0:
     dependencies:
       siginfo: 2.0.0
@@ -13319,7 +13561,7 @@ snapshots:
 
   wkx@0.5.0:
     dependencies:
-      '@types/node': 20.16.11
+      '@types/node': 22.7.6
     optional: true
 
   word-wrap@1.2.5: {}
@@ -13336,10 +13578,10 @@ snapshots:
   workbox-build@7.1.0(@types/babel__core@7.20.5):
     dependencies:
       '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
-      '@babel/core': 7.24.7
-      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/preset-env': 7.24.7(@babel/core@7.25.8)
       '@babel/runtime': 7.25.6
-      '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)
+      '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)
       '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
       '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
       '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
@@ -13379,10 +13621,10 @@ snapshots:
   workbox-build@7.1.1(@types/babel__core@7.20.5):
     dependencies:
       '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
-      '@babel/core': 7.24.7
-      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      '@babel/core': 7.25.8
+      '@babel/preset-env': 7.24.7(@babel/core@7.25.8)
       '@babel/runtime': 7.25.6
-      '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.7)(@types/babel__core@7.20.5)(rollup@2.79.1)
+      '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1)
       '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
       '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
       '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
@@ -13506,7 +13748,7 @@ snapshots:
 
   wrappy@1.0.2: {}
 
-  write-file-atomic@5.0.1:
+  write-file-atomic@6.0.0:
     dependencies:
       imurmurhash: 0.1.4
       signal-exit: 4.1.0
@@ -13523,6 +13765,8 @@ snapshots:
 
   yallist@4.0.0: {}
 
+  yallist@5.0.0: {}
+
   yaml@1.10.2: {}
 
   yargs-parser@21.1.1: {}
@@ -13530,7 +13774,7 @@ snapshots:
   yargs@17.7.2:
     dependencies:
       cliui: 8.0.1
-      escalade: 3.1.2
+      escalade: 3.2.0
       get-caller-file: 2.0.5
       require-directory: 2.1.1
       string-width: 4.2.3
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index c4f83d975..3fe9f7735 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -12,3 +12,73 @@ packages:
   - "e2e-api"
   - "lib-portal"
   - "performance-test"
+
+catalogs:
+  next:
+    "next": "14.2.14"
+    "@next/bundle-analyzer": "14.2.14"
+    "eslint-config-next": "14.2.14"
+
+  react:
+    "react": "18.3.1"
+    "@types/react": "18.3.12"
+    "react-dom": "18.3.1"
+    "@types/react-dom": "18.3.1"
+
+  joy:
+    "@mui/joy": "5.0.0-beta.48"
+    "@mui/icons-material": "5.16.7"
+    "@mui/material": "npm:@mui/joy@5.0.0-beta.48"
+    "@emotion/cache": "11.13.1"
+    "@emotion/react": "11.13.3"
+    "@emotion/styled": "11.13.0"
+    "@fontsource/poppins": "5.1.0"
+
+  i18next:
+    "i18next": "23.16.4"
+    "i18next-resources-to-backend": "1.2.1"
+    "react-i18next": "15.1.0"
+
+  fullcalendar:
+    "@fullcalendar/core": "6.1.15"
+    "@fullcalendar/daygrid": "6.1.15"
+    "@fullcalendar/interaction": "6.1.15"
+    "@fullcalendar/list": "6.1.15"
+    "@fullcalendar/react": "6.1.15"
+    "@fullcalendar/timegrid": "6.1.15"
+
+  eslint:
+    "eslint": "9.13.0"
+    "typescript-eslint": "8.11.0"
+    "@eslint/compat": "1.2.1"
+    "@eslint/eslintrc": "3.1.0"
+    "@eslint/js": "9.13.0"
+    "eslint-config-prettier": "9.1.0"
+    "eslint-plugin-import": "2.31.0"
+    "eslint-plugin-unused-imports": "4.1.4"
+    "eslint-plugin-promise": "7.1.0"
+
+  prettier:
+    "prettier": "3.3.3"
+    "@trivago/prettier-plugin-sort-imports": "4.3.0"
+
+  vitest:
+    "vitest": "2.1.4"
+    "@vitest/coverage-istanbul": "2.1.4"
+    "@vitejs/plugin-react": "4.3.3"
+    "vite-tsconfig-paths": "5.0.1"
+
+  common:
+    "typescript": "5.6.3"
+    "@types/node": "20.17.1"
+    "@tanstack/react-query": "5.59.16"
+    "@tanstack/eslint-plugin-query": "5.59.7"
+    "@tanstack/react-table": "8.20.5"
+    "date-fns": "4.1.0"
+    "formik": "2.4.6"
+    "react-error-boundary": "4.1.2"
+    "remeda": "2.16.0"
+    "server-only": "0.0.1"
+    "valibot": "0.42.1"
+    "use-debounce": "10.0.4"
+    "uuid": "11.0.1"
diff --git a/reverse-proxy/citizen-portal.conf b/reverse-proxy/citizen-portal.conf
index 4f538326a..6a929873b 100644
--- a/reverse-proxy/citizen-portal.conf
+++ b/reverse-proxy/citizen-portal.conf
@@ -32,6 +32,11 @@ server {
         proxy_pass http://host.docker.internal:8080/department/;
     }
 
+    # No authorization required for opening-hours in citizen api
+    location /api/school-entry/citizen/school-entries/opening-hours {
+        proxy_pass http://host.docker.internal:8082/citizen/school-entries/opening-hours;
+    }
+
     # No authorization required for the privacy documents in citizen api
     location /api/school-entry/citizen/school-entries/documents/ {
         proxy_pass http://host.docker.internal:8082/citizen/school-entries/documents/;
@@ -57,11 +62,22 @@ server {
         proxy_pass http://host.docker.internal:8085/citizen/auth/;
     }
 
+    # No authorization required for the base feature toggles endpoint
+    location = /api/base/feature-toggles {
+        proxy_pass http://host.docker.internal:8080/feature-toggles;
+    }
+
     # No authorization required for the travel medicine feature toggles endpoint
     location = /api/travel-medicine/feature-toggles {
         proxy_pass http://host.docker.internal:8085/feature-toggles;
     }
 
+    # handle disabled backends as 404
+    # note: all /api/ routes must appear before this
+    location /api/ {
+      return 404;
+    }
+
     # Needed to support hot-reloading of the local Next.js dev server
     # see https://nextjs.org/docs/pages/building-your-application/upgrading/version-12#hmr-connection-now-uses-a-websocket
     location /_next/webpack-hmr {
diff --git a/reverse-proxy/employee-portal.conf b/reverse-proxy/employee-portal.conf
index 066ef3175..6ca1b3eb3 100644
--- a/reverse-proxy/employee-portal.conf
+++ b/reverse-proxy/employee-portal.conf
@@ -85,6 +85,11 @@ server {
         proxy_pass http://host.docker.internal:8095/;
     }
 
+    location /api/medical-registry/ {
+        include auth_api_request.conf;
+        proxy_pass http://host.docker.internal:8097/;
+    }
+
     location /api/synapse/ {
         rewrite ^/api/synapse/(.*) /$1 break;
 
@@ -109,6 +114,12 @@ server {
         return 302 /?synapseError=true;
     }
 
+    # handle disabled backends as 404
+    # note: all /api/ routes must appear before this
+    location /api/ {
+      return 404;
+    }
+
     # Needed to support hot-reloading of the local Next.js dev server
     # see https://nextjs.org/docs/pages/building-your-application/upgrading/version-12#hmr-connection-now-uses-a-websocket
     location /_next/webpack-hmr {
diff --git a/settings.gradle b/settings.gradle
index a9e2aba39..3ef817428 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,4 @@
-rootProject.name = 'eshg-frontend'
+rootProject.name = 'ga-lotse-code'
 
 includeBuild 'backend'
 
-- 
GitLab