From 46dd7326fe2dffacadf29bc507cf1720bab3a09b Mon Sep 17 00:00:00 2001 From: cronn Bot <no-reply@cronn.de> Date: Wed, 12 Feb 2025 12:24:59 +0000 Subject: [PATCH] Update v1.6.4 --- .../src/lib/components/table/Filter.tsx | 17 +- .../table/cell/ActorSelectorCell.tsx | 2 +- .../table/cell/StaticActorSelectorCell.tsx | 17 + .../lib/components/table/cell/StringCell.tsx | 2 +- backend/api-commons/build.gradle | 5 + backend/api-commons/gradle.lockfile | 63 +- .../validation/constraints/DateOfBirth.java | 31 + .../constraints/DateOfBirthValidator.java | 38 + .../constraints}/EmailAddressConstraint.java | 2 +- .../MandatoryEmailAddressConstraint.java | 2 +- backend/auditlog-api/gradle.lockfile | 25 +- .../auditlog/AuditLogClientTestHelperApi.java | 20 + ...java => AuditLogServiceTestHelperApi.java} | 8 +- .../eshg/auditlog/AuditLogTestHelperApi.java | 16 - backend/auditlog/build.gradle | 1 + backend/auditlog/gradle.lockfile | 29 +- backend/auditlog/openApi.json | 2 +- .../de/eshg/auditlog/AuditLogController.java | 4 +- ...AuditLogInternalSecurityConfiguration.java | 3 +- ... AuditLogServiceTestHelperController.java} | 25 +- .../migrations/0008_add_auditlog_entry.xml | 54 + .../main/resources/migrations/changelog.xml | 1 + backend/auth/gradle.lockfile | 25 +- .../de/eshg/security/auth/AuthProperties.java | 8 +- .../eshg/security/auth/UserAgentFilter.java | 2 - .../auth/login/AccessCodeLoginMethod.java | 40 +- .../auth/login/AccessCodeLoginType.java | 12 + .../eshg/security/auth/login/LoginMethod.java | 5 +- .../application-citizen-portal.properties | 6 +- backend/base-api/gradle.lockfile | 25 +- .../facility/AddFacilityFileStateRequest.java | 2 +- .../AddFacilityFileStateResponse.java | 2 +- .../ExternalAddFacilityFileStateRequest.java | 2 +- .../api/facility/FacilityDetailsDto.java | 2 +- .../GetFacilityFileStateResponse.java | 2 +- .../GetReferenceFacilityResponse.java | 2 +- .../api/person/AddPersonFileStateRequest.java | 61 +- .../person/AddPersonFileStateResponse.java | 2 +- .../ExternalAddPersonFileStateRequest.java | 33 +- .../person/GetPersonFileStateResponse.java | 2 +- .../person/GetReferencePersonResponse.java | 2 +- .../api/person/PersonDetailsDto.java | 2 +- .../api/person/UpdatePersonInBulkRequest.java | 6 +- .../api/person/UpdatePersonRequest.java | 43 +- .../person/UpdateReferencePersonRequest.java | 6 +- .../api/AddInstitutionContactRequest.java | 2 +- .../contact/api/AddPersonContactRequest.java | 2 +- .../contact/api/InstitutionContactDto.java | 2 +- .../base/contact/api/PersonContactDto.java | 2 +- .../api/UpdateInstitutionContactRequest.java | 2 +- .../api/UpdatePersonContactRequest.java | 2 +- .../de/eshg/base/gdpr/GdprProcedureApi.java | 10 +- .../base/gdpr/api/GdprProcedureStatusDto.java | 1 - .../base/statistics/api/BaseAttribute.java | 4 +- .../base/testhelper/BaseTestHelperApi.java | 8 +- .../de/eshg/base/user/api/AddUserRequest.java | 2 +- .../java/de/eshg/base/user/api/UserDto.java | 2 +- backend/base/build.gradle | 1 + backend/base/gradle.lockfile | 31 +- backend/base/openApi.json | 174 +- .../base/centralfile/mapper/PersonMapper.java | 2 +- .../base/gdpr/GdprProcedureController.java | 5 +- .../eshg/base/gdpr/GdprProcedureMapper.java | 3 +- .../eshg/base/gdpr/GdprProcedureService.java | 10 +- .../gdpr/persistence/GdprProcedureStatus.java | 1 - .../keycloak/CitizenKeycloakProvisioning.java | 3 +- .../de/eshg/base/keycloak/ModuleClient.java | 3 +- .../config/BaseInternalSecurityConfig.java | 3 +- .../base/statistics/StatisticsController.java | 13 +- .../testhelper/BaseTestHelperController.java | 28 +- .../migrations/0040_add_auditlog_entry.xml | 55 + .../0041_remove_gdpr_status_open.xml | 11 + .../main/resources/migrations/changelog.xml | 2 + backend/build.gradle | 8 +- .../src/main/groovy/eshg.service.gradle | 4 +- .../business-module-commons/gradle.lockfile | 29 +- .../gradle.lockfile | 27 +- .../resources/common-persistence.properties | 2 + backend/central-repository/gradle.lockfile | 27 +- .../CentralRepositorySecurityConfig.java | 7 +- backend/chat-management/gradle.lockfile | 27 +- backend/compliance-test/gradle.lockfile | 32 +- backend/dental/gradle.lockfile | 27 +- backend/dental/openApi.json | 34 +- .../java/de/eshg/dental/ChildController.java | 2 +- .../java/de/eshg/dental/ChildService.java | 21 +- .../de/eshg/dental/ExaminationService.java | 95 +- .../dental/ProphylaxisSessionService.java | 45 +- .../main/java/de/eshg/dental/Validator.java | 36 + .../eshg/dental/api/CreateChildRequest.java | 3 +- .../de/eshg/dental/api/ExaminationDto.java | 2 +- .../de/eshg/dental/api/ExistingUserDto.java | 3 +- .../api/FluoridationExaminationResultDto.java | 7 +- .../api/IsFluorideVarnishApplicable.java | 10 +- .../eshg/dental/api/NonExistingUserDto.java | 4 +- .../eshg/dental/api/PerformingPersonDto.java | 9 +- .../dental/api/ProphylaxisSessionRequest.java | 8 + .../api/ScreeningExaminationResultDto.java | 8 +- .../java/de/eshg/dental/api/ToothDto.java | 69 +- ...ProphylaxisSessionParticipantsRequest.java | 3 +- .../api/UpdateProphylaxisSessionRequest.java | 18 +- .../de/eshg/dental/domain/model/Child.java | 16 +- .../eshg/dental/domain/model/Examination.java | 4 + .../domain/model/ExaminationResult.java | 4 +- .../domain/model/FluoridationConsent.java | 51 +- .../model/FluoridationExaminationResult.java | 6 +- .../model/ScreeningExaminationResult.java | 6 +- .../repository/ExaminationRepository.java | 42 +- .../eshg/dental/importer/ChildRowReader.java | 7 +- .../de/eshg/dental/mapper/ChildMapper.java | 17 +- .../eshg/dental/mapper/ExaminationMapper.java | 2 +- .../mapper/ProphylaxisSessionMapper.java | 6 +- .../dental/testhelper/ChildrenPopulator.java | 6 +- .../ProphylaxisSessionsPopulator.java | 23 +- .../migrations/0029_add_auditlog_entry.xml | 55 + .../0030_add_modified_at_fluoridation.xml | 19 + ...0031_fluoride_varnish_applied_optional.xml | 12 + ...te_examination_result_to_use_sequences.xml | 11 + .../main/resources/migrations/changelog.xml | 4 + backend/docker-compose.yaml | 2 + backend/file-commons/gradle.lockfile | 19 +- backend/gradle.lockfile | 14 +- backend/gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- backend/gradlew | 3 +- backend/gradlew.bat | Bin 2872 -> 2966 bytes backend/inspection/build.gradle | 1 + backend/inspection/gradle.lockfile | 27 +- backend/inspection/openApi.json | 13 +- .../InspectionProcedureRowReader.java | 4 +- .../InspectionTestHelperController.java | 14 +- .../migrations/0064_add_auditlog_entry.xml | 55 + .../main/resources/migrations/changelog.xml | 1 + backend/keycloak-api/gradle.lockfile | 19 +- backend/keycloak/gradle.lockfile | 21 +- .../login/date-of-birth-access-code.ftl | 61 + .../login/messages/messages_de.properties | 15 +- .../login/messages/messages_en.properties | 13 +- .../{access-code.ftl => pin-access-code.ftl} | 30 +- .../login/resources/css/custom-login.css | 7 + ...essCodeAwareAuthenticationContextBean.java | 30 + .../authenticator/AccessCodeForm.java | 92 +- .../authenticator/AccessCodeFormFactory.java | 23 +- .../DateOfBirthAccessCodeForm.java | 82 + .../DateOfBirthAccessCodeFormFactory.java | 30 + .../DeprecatedAccessCodeFormFactory.java | 82 + .../authenticator/PinAccessCodeForm.java | 59 + .../PinAccessCodeFormFactory.java | 30 + .../PinCredentialProvider.java | 7 +- ...ycloak.authentication.AuthenticatorFactory | 4 +- backend/lib-aggregation/gradle.lockfile | 25 +- backend/lib-appointmentblock/gradle.lockfile | 29 +- backend/lib-appointmentblock/openApi.json | 8 +- .../AppointmentBlockService.java | 39 +- .../AppointmentBlockSlotUtil.java | 17 +- .../AppointmentBlockValidator.java | 6 +- .../AppointmentTypeMapper.java | 2 +- .../AppointmentTypeService.java | 5 +- .../api/AppointmentTypeConfigDto.java | 2 +- .../api/UpdateAppointmentTypeRequest.java | 2 +- .../CreateAppointmentTypeTask.java | 22 +- .../entity/AppointmentBlockGroup.java | 12 +- .../entity/AppointmentTypeConfig.java | 12 +- backend/lib-auditlog/build.gradle | 10 + backend/lib-auditlog/gradle.lockfile | 352 +- .../eshg/lib/auditlog/AuditLogArchiving.java | 154 +- .../eshg/lib/auditlog/AuditLogFormatter.java | 35 + .../auditlog/AuditLogTestHelperService.java | 29 +- .../de/eshg/lib/auditlog/AuditLogger.java | 75 +- .../lib/auditlog/config/AuditLogConfig.java | 18 - .../lib/auditlog/domain/AuditLogEntry.java | 80 + .../domain/AuditLogEntryRepository.java | 17 + .../spring/AuditLogAutoConfiguration.java | 3 + ...ogLibraryDomainModelAutoConfiguration.java | 15 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../resources/auditlog-default.properties | 1 - backend/lib-base-client/gradle.lockfile | 25 +- backend/lib-calendar-api/gradle.lockfile | 19 +- backend/lib-calendar/gradle.lockfile | 19 +- .../gradle.lockfile | 25 +- .../gradle.lockfile | 25 +- backend/lib-commons/gradle.lockfile | 17 +- backend/lib-contact-api/gradle.lockfile | 17 +- backend/lib-contact/gradle.lockfile | 25 +- .../lib-document-generator/gradle.lockfile | 25 +- backend/lib-editor-api/gradle.lockfile | 23 +- backend/lib-editor/gradle.lockfile | 27 +- .../gradle.lockfile | 25 +- backend/lib-four-eyes-principle/build.gradle | 1 + .../lib-four-eyes-principle/gradle.lockfile | 27 +- backend/lib-keycloak/gradle.lockfile | 15 +- .../lib/keycloak/EmployeePermissionRole.java | 1 + .../eshg/lib/keycloak/EmployeeTestUser.java | 16 +- backend/lib-lsd-api/gradle.lockfile | 21 +- backend/lib-mutex/gradle.lockfile | 27 +- backend/lib-notification-api/gradle.lockfile | 25 +- backend/lib-notification/gradle.lockfile | 27 +- backend/lib-procedures-api/gradle.lockfile | 25 +- backend/lib-procedures/build.gradle | 1 + backend/lib-procedures/gradle.lockfile | 29 +- .../gdpr/GdprValidationTaskController.java | 5 +- .../procedure/procedures/ProcedureQuery.java | 28 + backend/lib-relay/gradle.lockfile | 19 +- backend/lib-scheduling/gradle.lockfile | 17 +- .../lib-security-config-urls/gradle.lockfile | 15 +- .../service/security/config/BaseUrls.java | 2 + backend/lib-security-config/gradle.lockfile | 21 +- .../StiProtectionPublicSecurityConfig.java | 2 + .../gradle.lockfile | 19 +- .../lib-service-directory-api/gradle.lockfile | 19 +- backend/lib-statistics-api/gradle.lockfile | 17 +- .../de/eshg/lib/statistics/api/Attribute.java | 3 +- .../statistics/api/DataPrivacyCategory.java | 12 + backend/lib-statistics/README.md | 2 +- backend/lib-statistics/gradle.lockfile | 27 +- .../lib/statistics/StatisticsService.java | 3 +- .../statistics/attributes/AttributeData.java | 30 +- .../attributes/ProcedureAttribute.java | 4 +- .../statistics/attributes/TextAttribute.java | 17 +- .../attributes/ValueWithOptionsAttribute.java | 2 +- backend/lib-xdomea/gradle.lockfile | 15 +- backend/lib-xlsx-import/gradle.lockfile | 25 +- .../de/eshg/lib/xlsximport/RowReader.java | 53 +- .../local-service-directory/gradle.lockfile | 27 +- backend/logging-commons/gradle.lockfile | 15 +- backend/measles-protection/build.gradle | 1 + backend/measles-protection/gradle.lockfile | 27 +- backend/measles-protection/openApi.json | 21 +- ...otectionProcedureTestHelperController.java | 14 +- .../migrations/0046_add_auditlog_entry.xml | 55 + ...7_convert_duration_columns_to_interval.xml | 30 + .../main/resources/migrations/changelog.xml | 2 + backend/medical-registry/build.gradle | 1 + backend/medical-registry/gradle.lockfile | 27 +- backend/medical-registry/openApi.json | 13 +- .../MedicalRegistryController.java | 2 +- .../MedicalRegistryImportController.java | 2 +- .../eshg/medicalregistry/PersonService.java | 8 +- .../medicalregistry/api/ApplicantDto.java | 2 +- .../api/CreateApplicantDto.java | 2 +- .../api/CreatePracticeDto.java | 2 +- .../eshg/medicalregistry/api/PracticeDto.java | 2 +- .../api/PracticeReferenceFacilityDto.java | 2 +- .../api/ProfessionalReferencePersonDto.java | 2 +- .../importer/MedicalRegistryImporter.java | 7 +- .../importer/MedicalRegistryRowReader.java | 5 +- .../MedicalRegistryTestHelperController.java | 14 +- .../migrations/0008_add_auditlog_entry.xml | 55 + .../main/resources/migrations/changelog.xml | 1 + backend/official-medical-service/build.gradle | 1 + .../official-medical-service/gradle.lockfile | 27 +- backend/official-medical-service/openApi.json | 763 ++++- .../OfficialMedicalServiceApplication.java | 7 +- .../appointment/OmsAppointmentService.java | 28 +- .../persistence/entity/AppointmentState.java | 15 +- .../document/OmsDocumentController.java | 82 + .../document/OmsDocumentMapper.java | 61 + .../document/OmsDocumentService.java | 295 ++ .../document/api/DocumentDto.java | 29 + .../document/api/DocumentStatusDto.java | 16 + .../document/api/GetDocumentsResponse.java | 12 + .../api/PatchDocumentInformationRequest.java | 16 + .../api/PatchDocumentNoteRequest.java | 8 + .../api/PatchDocumentReviewRequest.java | 11 + .../document/api/PostDocumentRequest.java | 16 + .../document/api/ReviewResultDto.java | 14 + .../persistence/entity/OmsDocument.java | 184 ++ .../entity/OmsDocumentRepository.java | 11 + .../persistence/entity/OmsDocumentStatus.java | 13 + .../file/OmsFileController.java | 48 + .../file/OmsFileMapper.java | 38 + .../file/OmsFileService.java | 60 + .../file/api/OmsFileDto.java | 20 + .../file/persistence/entity/OmsFile.java | 93 + .../persistence/entity/OmsFileRepository.java | 11 + .../notification/MailClient.java | 32 + .../notification/NotificationProperties.java | 14 + .../notification/NotificationService.java | 138 + .../notification/NotificationText.java | 52 + .../person/PersonClient.java | 24 + .../EmployeeOmsProcedureController.java | 70 +- .../EmployeeOmsProcedureService.java | 128 +- .../procedure/OmsProcedureOverviewMapper.java | 6 + .../procedure/OmsProgressEntryType.java | 15 +- .../procedure/ProgressEntryService.java | 204 +- .../api/AcceptDraftProcedureResponse.java | 12 + .../api/EmployeeOmsProcedureDetailsDto.java | 7 +- .../api/EmployeeOmsProcedureOverviewDto.java | 1 + .../procedure/api/FacilityDto.java | 2 +- .../api/MedicalOpinionStatusDto.java | 14 + ...OmsProcedureEmailNotificationsRequest.java | 11 + .../entity/MedicalOpinionStatus.java | 11 + .../persistence/entity/OmsProcedure.java | 77 + .../entity/OmsProcedureRepository.java | 44 +- .../testhelper/OmsDocumentTestHelperFile.java | 65 + .../testhelper/OmsTestHelperController.java | 14 +- .../TestPopulateProcedureService.java | 137 +- .../testhelper/api/DocumentPopulationDto.java | 23 + .../testhelper/api/FileTestDataConfig.java | 22 + .../api/PostPopulateProcedureRequest.java | 9 +- .../api/PostPopulateProcedureResponse.java | 5 +- .../user/CitizenAccessCodeUserClient.java | 35 + .../PagedWaitingRoomProcedures.java | 17 + .../waitingroom/WaitingRoomController.java | 46 + .../waitingroom/WaitingRoomMapper.java | 59 + .../waitingroom/WaitingRoomService.java | 385 +++ .../waitingroom/WaitingRoomSpecification.java | 110 + .../api/GetWaitingRoomProceduresResponse.java | 17 + .../waitingroom/api/WaitingRoomDto.java | 11 + .../api/WaitingRoomProcedureDto.java | 24 + ...mProcedurePaginationAndSortParameters.java | 18 + .../waitingroom/api/WaitingRoomSortKey.java | 18 + .../waitingroom/api/WaitingStatusDto.java | 15 + .../persistence/entity/WaitingRoom.java | 66 + .../persistence/entity/WaitingStatus.java | 12 + .../waitingroom/util/WaitingRoomPageSpec.java | 12 + ...ion-health-department-frankfurt.properties | 5 + .../src/main/resources/application.properties | 12 +- .../documents/testHelperSampleDocument.pdf | Bin 0 -> 21731 bytes .../documents/testHelperSampleImage.jpg | Bin 0 -> 730 bytes .../documents/testHelperSampleImage.png | Bin 0 -> 345 bytes .../default/de/new_citizen_user.txt | 17 + .../ga_frankfurt/de/new_citizen_user.txt | 16 + backend/opendata/gradle.lockfile | 29 +- backend/relay-server/gradle.lockfile | 19 +- backend/rest-client-commons/gradle.lockfile | 19 +- .../rest-oauth-client-commons/gradle.lockfile | 17 +- backend/rest-service-commons/gradle.lockfile | 17 +- backend/rest-service-errors/gradle.lockfile | 17 +- backend/school-entry/build.gradle | 1 + backend/school-entry/gradle.lockfile | 29 +- backend/school-entry/openApi.json | 21 +- .../schoolentry/SchoolEntryController.java | 2 +- .../eshg/schoolentry/api/CreatePersonDto.java | 3 +- .../schoolentry/api/UpdatePersonRequest.java | 3 +- .../importer/CitizenListRowReader.java | 9 +- .../schoolentry/importer/ImportService.java | 10 +- .../importer/PastProcedureListRowReader.java | 8 +- .../importer/SchoolListRowReader.java | 7 +- .../SchoolEntryTestHelperController.java | 14 +- .../migrations/0077_add_auditlog_entry.xml | 55 + ...8_convert_duration_columns_to_interval.xml | 30 + .../main/resources/migrations/changelog.xml | 2 + backend/service-commons/gradle.lockfile | 15 +- backend/service-directory/gradle.lockfile | 27 +- backend/spatz-dns/gradle.lockfile | 15 +- backend/spatz/build.gradle | 2 +- backend/spatz/gradle.lockfile | 21 +- backend/statistics/build.gradle | 8 + backend/statistics/gradle.lockfile | 32 +- .../lib/de/linearbits/jhpl/jhpl-0.0.1.jar | Bin 0 -> 75235 bytes .../org/deidentifier/arx/libarx-3.9.1-min.jar | Bin 0 -> 1065407 bytes backend/statistics/openApi.json | 44 +- .../aggregation/AnalysisService.java | 46 +- .../aggregation/DataAggregationService.java | 18 +- .../DataSourceAggregationService.java | 20 +- .../aggregation/EvaluationCopyService.java | 3 + .../aggregation/EvaluationService.java | 12 +- .../statistics/api/TableColumnHeader.java | 4 +- .../chart/HistogramChartConfigurationDto.java | 5 +- .../datasource/BaseDataSourceAttribute.java | 7 +- .../BusinessDataSourceAttribute.java | 2 + .../api/evaluation/GetEvaluationRequest.java | 4 +- .../api/evaluation/GetEvaluationResponse.java | 4 + .../centralrepository/RepoMapper.java | 8 +- .../RepoHistogramChart.java | 5 +- .../AggregationResultExportService.java | 2 +- .../export/DiagramExportService.java | 2 +- .../statistics/mapper/AnalysisMapper.java | 6 +- .../mapper/AttributeSelectionMapper.java | 13 + .../statistics/mapper/EvaluationMapper.java | 19 +- .../persistence/entity/TableColumn.java | 12 + .../TableColumnDataPrivacyCategory.java | 12 + .../chart/HistogramChartConfiguration.java | 23 + .../testhelper/StatisticsPopulator.java | 12 +- .../StatisticsTestHelperController.java | 14 +- .../migrations/0044_add_auditlog_entry.xml | 55 + .../migrations/0045_data_privacy_category.xml | 16 + .../migrations/0046_add_minBin_and_maxBin.xml | 16 + .../0047_migrate_minBin_and_maxBin.xml | 30 + .../main/resources/migrations/changelog.xml | 4 + backend/sti-protection/build.gradle | 1 + backend/sti-protection/gradle.lockfile | 27 +- backend/sti-protection/openApi.json | 356 +- .../stiprotection/DepartmentInfoService.java | 86 + .../FollowUpProcedureService.java | 14 +- .../StiProtectionApplication.java | 3 + .../StiProtectionCitizenController.java | 96 + .../StiProtectionProcedureController.java | 5 +- ...StiProtectionProcedureDeletionService.java | 8 - .../StiProtectionProcedureService.java | 24 +- .../api/GetProcedureResponse.java | 3 +- .../GetStiProtectionProceduresSortByDto.java | 7 +- .../StiProtectionProcedureOverviewDto.java | 6 +- .../api/TextTemplatePopulationRequest.java | 10 + .../api/TextTemplatePopulationResponse.java | 14 + .../api/citizen/GetDepartmentInfoRequest.java | 10 + .../api/citizen/GetOpeningHoursRequest.java | 11 + .../api/citizen/GetOpeningHoursResponse.java | 11 + .../api/citizen/StiAppointmentTypeDto.java | 14 + .../api/diagnosis/DiagnosisDto.java | 29 +- .../api/diagnosis/MedicationDto.java | 8 +- .../CreateTextTemplateRequest.java | 15 +- .../api/texttemplate/TextTemplateDto.java | 22 +- .../mapper/StiProtectionProcedureMapper.java | 7 +- .../mapper/diagnosis/DiagnosisMapper.java | 15 - .../config/DepartmentInfoConfig.java | 25 + .../config/DepartmentInfoProperties.java | 22 + .../config/OpeningHoursProperties.java | 10 + .../data/StiProtectionProcedureData.java | 8 + .../db/StiProtectionProcedure.java | 48 +- .../StiProtectionTestHelperController.java | 28 +- .../testhelper/TextTemplatePopulator.java | 94 + ...ion-health-department-frankfurt.properties | 39 + .../src/main/resources/application.properties | 1 + .../migrations/0045_add_sample_bar_code.xml | 20 + .../migrations/0046_add_appointment_start.xml | 21 + .../migrations/0047_add_auditlog_entry.xml | 55 + ...8_convert_duration_columns_to_interval.xml | 30 + .../main/resources/migrations/changelog.xml | 4 + backend/synapse/gradle.lockfile | 15 +- backend/test-commons/gradle.lockfile | 19 +- .../java/de/eshg/base/AuditLogTraits.java | 37 - .../de/eshg/base/StaticLogDirExtension.java | 33 - .../src/main/java/de/eshg/base/ZipUtil.java | 58 +- .../test-helper-commons-api/gradle.lockfile | 17 +- .../gradle.lockfile | 15 +- backend/test-helper-commons/gradle.lockfile | 27 +- .../testhelper/DefaultTestHelperService.java | 10 +- backend/travel-medicine/build.gradle | 1 + backend/travel-medicine/gradle.lockfile | 27 +- backend/travel-medicine/openApi.json | 21 +- .../TravelMedicineTestHelperController.java | 14 +- .../vaccinationconsultation/PersonClient.java | 12 +- .../VaccinationConsultationController.java | 2 +- .../migrations/0057_add_auditlog_entry.xml | 55 + ...8_convert_duration_columns_to_interval.xml | 30 + .../main/resources/migrations/changelog.xml | 2 + backend/util-commons/gradle.lockfile | 17 +- build.gradle | 2 +- buildSrc/src/main/groovy/node.gradle | 2 +- .../src/main/groovy/openapi-generator.gradle | 116 - citizen-portal-api/.gitignore | 1 - citizen-portal-api/build.gradle | 14 - citizen-portal-api/package.json | 9 - citizen-portal-api/tsconfig.json | 3 - citizen-portal/gradleDependencies.json | 5 +- citizen-portal/package.json | 5 +- .../src/lib/baseModule/api/queries/gdpr.ts | 8 +- .../gdpr/GdprProcedureStatusChip.tsx | 1 - .../reportCase/subforms/MinimalPersonForm.tsx | 6 +- .../medicalRegistry/api/clients.ts | 4 +- .../api/mutations/medicalRegistryEntries.ts | 2 +- .../api/queries/featureTogglesApi.ts | 8 +- .../ProfessionalRegistrationStepper.tsx | 2 +- .../ProfessionalRegistrationFormStepFour.tsx | 5 +- .../ProfessionalRegistrationFormStepOne.tsx | 2 +- .../ProfessionalRegistrationFormStepTwo.tsx | 10 +- .../shared/navigationItems.tsx | 2 +- .../officialMedicalService/api/clients.ts | 4 +- .../businessModules/opendata/api/clients.ts | 5 +- .../opendata/api/queries/citizenPublicApi.ts | 48 +- .../components/OpenDataDetailsContent.tsx | 2 +- .../opendata/components/OpenDataFilters.tsx | 2 +- .../components/OpenDataOverviewContent.tsx | 116 +- .../components/useOpenDataFilterValues.ts | 2 +- .../opendata/locales/de/overview.json | 9 +- .../opendata/locales/en/overview.json | 9 +- .../opendata/shared/constants.ts | 2 +- citizen-portal/src/lib/shared/api/clients.ts | 10 +- config/eslint.next.js | 5 + config/vitest.base.ts | 9 +- docs/frontend.adoc | 5 +- employee-portal-api/.gitignore | 1 - employee-portal-api/build.gradle | 20 - employee-portal-api/package.json | 9 - employee-portal-api/tsconfig.json | 3 - employee-portal/gradleDependencies.json | 11 +- employee-portal/package.json | 11 +- .../[businessModule]/overview/page.tsx | 10 +- .../(baseModule)/inbox-procedures/page.tsx | 2 +- .../archiving-admin/inspection/page.tsx | 2 +- .../measles-protection/page.tsx | 2 +- .../archiving-admin/medical-registry/page.tsx | 2 +- .../archiving-admin/school-entry/page.tsx | 2 +- .../archiving-admin/sti-protection/page.tsx | 2 +- .../archiving-admin/travel-medicine/page.tsx | 2 +- .../(archiving)/archiving/inspection/page.tsx | 2 +- .../archiving/measles-protection/page.tsx | 2 +- .../archiving/medical-registry/page.tsx | 2 +- .../archiving/school-entry/page.tsx | 2 +- .../archiving/sti-protection/page.tsx | 2 +- .../archiving/travel-medicine/page.tsx | 2 +- .../examinations/[examinationId]/page.tsx | 102 +- .../[prophylaxisSessionId]/details/page.tsx | 5 +- .../examinations/[participantIndex]/page.tsx | 93 +- .../[prophylaxisSessionId]/layout.tsx | 2 +- .../reportresult/edit/[reportId]/layout.tsx | 2 + .../procedures/[id]/execution/page.tsx | 2 +- .../procedures/[id]/reportresult/page.tsx | 2 +- .../inspection/textblocks/page.tsx | 2 +- .../procedures/[id]/documents/page.tsx | 15 + .../waiting-room/page.tsx | 19 + .../evaluations/[id]/table/page.tsx | 16 +- .../sti-protection/text-templates/page.tsx | 19 + .../src/app/playground/charts/page.tsx | 21 +- .../app/playground/filter-settings/page.tsx | 1 + employee-portal/src/app/playground/page.tsx | 3 + .../src/app/playground/teeth/page.tsx | 180 + .../src/lib/auditlog/api/clients.ts | 2 +- .../src/lib/auditlog/api/models/auditlog.ts | 2 +- .../components/AuditLogDecryptSidebar.tsx | 2 +- .../lib/auditlog/components/AuditLogSheet.tsx | 2 +- .../components/auditLogAccessibleColumns.tsx | 2 +- .../authorize/AuditLogAuthorizeSidebar.tsx | 4 +- .../useAuditLogAdminFilterSettings.ts | 2 +- .../src/lib/auditlog/components/crypto.ts | 2 +- .../src/lib/auditlog/mutations/auditlog.ts | 2 +- .../src/lib/auditlog/queries/auditlog.ts | 2 +- .../lib/baseModule/api/mutations/facility.ts | 2 +- .../src/lib/baseModule/api/mutations/gdpr.ts | 6 +- .../contacts/forms/merge/MergeStringField.tsx | 1 + .../baseModule/components/gdpr/constants.ts | 1 - .../lib/baseModule/components/gdpr/helpers.ts | 2 +- .../lib/baseModule/components/gdpr/i18n.ts | 1 - .../overview/CreateGDPRProcedureSidebar.tsx | 6 +- .../procedure/MatterOfConcernDisplayField.tsx | 37 + .../tiles/GdprDownloadPackagesTile.tsx | 2 +- .../procedure/tiles/GdprFacilityDataTile.tsx | 46 +- .../procedure/tiles/GdprPersonDataTile.tsx | 55 +- .../procedure/tiles/ProcedureDetailsTile.tsx | 39 +- .../UseCloseValidationTaskDialog.tsx | 2 +- .../ValidationTaskProceduresTable.tsx | 4 +- .../validationTasks/ValidationTasksTable.tsx | 2 +- .../inboxProcedures/ContactForm.tsx | 9 +- .../CreateInboxProcedureForm.tsx | 2 +- .../InboxProgressEntryForm.tsx | 2 +- .../components/inboxProcedures/mapper.ts | 8 +- .../NavigationListCollapsed.tsx | 58 +- .../ProcedureMetricsDisplay.tsx | 2 +- .../baseModule/components/task/Teamview.tsx | 6 +- .../src/lib/baseModule/theme/theme.ts | 45 +- .../lib/businessModules/chat/api/clients.ts | 2 +- .../chat/api/mutations/userSettingsApi.ts | 2 +- .../chat/api/queries/featureTogglesApi.ts | 2 +- .../chat/components/ChatAvatar.tsx | 2 +- .../chat/shared/ChatProvider.tsx | 2 +- .../children/ChildrenFilterSettings.tsx | 1 + .../features/children/ChildrenTable.tsx | 2 +- .../children/details/ChildDetails.tsx | 26 +- .../children/details/ChildExaminationForm.tsx | 94 +- .../children/new/CreateChildSidebar.tsx | 1 + .../AdditionalInformationFormSection.tsx | 48 +- .../examinations/ExaminationFormLayout.tsx | 11 +- .../examinations/ExaminationStatusChip.tsx | 1 + .../features/examinations/translations.ts | 1 + .../ChangeReasonForAbsenceModal.tsx | 101 + .../CreateProphylaxisSessionSidebar.tsx | 258 +- .../prophylaxisSessions/FluoridationField.tsx | 13 +- .../prophylaxisSessions/ParticipantFilter.tsx | 4 +- .../ProphylaxisSessionDetails.tsx | 29 +- .../ProphylaxisSessionFilterSettings.tsx | 1 + .../ProphylaxisSessionForm.tsx | 146 + .../ProphylaxisSessionParticipantsTable.tsx | 112 +- .../prophylaxisSessions/SearchGroupField.tsx | 4 +- .../UpdateProphylaxisSessionSidebar.tsx | 109 + .../dentalExamination/AddToothButton.tsx | 33 + .../DentalExaminationFormSection.tsx | 24 + .../DentalExaminationJawTabs.tsx | 70 + .../FullDentitionOverview.tsx | 129 + .../dentalExamination/GeneralJawForm.tsx | 93 + .../dentalExamination/JawWithHeading.tsx | 39 + .../dentalExamination/Legend.tsx | 138 + .../dentalExamination/LowerJawForm.tsx | 26 + .../dentalExamination/Quadrant.tsx | 31 + .../dentalExamination/QuadrantHeading.tsx | 47 + .../dentalExamination/ResultInputField.tsx | 75 + .../dentalExamination/Teeth.tsx | 262 ++ .../dentalExamination/ToothNumber.tsx | 31 + .../dentalExamination/UpperJawForm.tsx | 26 + .../DentalExaminationStoreProvider.tsx | 59 + .../dentalExaminationStore/actions.ts | 243 ++ .../dentalExaminationStore/constants.ts | 141 + .../dentalExaminationStore.ts | 106 + .../dentalExaminationStore/factories.ts | 135 + .../dentalExaminationStore/types.ts | 79 + .../examination/formComponents.tsx | 42 - .../useProphylaxisSessionExaminationForm.ts | 64 - .../ParticipantExaminationBottomBar.tsx} | 6 +- .../ParticipantExaminationForm.tsx} | 7 +- .../ParticipantExaminationPage.tsx | 118 + .../ParticipantExaminationToolbar.tsx} | 6 +- .../useParticipantExaminationForm.ts | 93 + .../useParticipantNavigation.ts} | 4 +- .../ProphylaxisSessionStoreProvider.tsx | 8 +- .../prophylaxisSessionStore/actions.ts | 57 + .../participantFilters.ts | 0 .../participantSorting.ts | 2 +- .../prophylaxisSessionStore.ts | 49 +- ...seSyncIncomingProphylaxisSessionChanges.ts | 31 + ...seSyncOutgoingProphylaxisSessionChanges.ts | 117 + .../features/prophylaxisSessions/staff.ts | 25 + .../store/replaceExaminationResult.ts | 24 - .../dental/import/ImportChildrenSidebar.tsx | 1 + .../businessModules/inspection/api/clients.ts | 2 +- .../inspection/api/mutations/checklist.ts | 2 +- .../api/mutations/checklistDefinition.ts | 2 +- .../inspection/api/mutations/facility.ts | 2 +- .../inspection/api/mutations/incidents.ts | 2 +- .../inspection/api/mutations/inspection.ts | 2 +- .../inspection/api/mutations/inventory.ts | 2 +- .../inspection/api/mutations/objectTypes.ts | 2 +- .../inspection/api/mutations/packlist.ts | 2 +- .../api/mutations/packlistDefinition.ts | 2 +- .../inspection/api/mutations/processImport.ts | 2 +- .../inspection/api/mutations/resources.ts | 2 +- .../inspection/api/mutations/textblocks.ts | 2 +- .../inspection/api/mutations/webSearch.ts | 2 +- .../inspection/api/queries/archiving.ts | 2 +- .../inspection/api/queries/checklist.ts | 2 +- .../api/queries/checklistDefinition.ts | 2 +- .../inspection/api/queries/facility.ts | 5 +- .../inspection/api/queries/feature.ts | 2 +- .../inspection/api/queries/geo.ts | 2 +- .../inspection/api/queries/incidents.ts | 2 +- .../inspection/api/queries/inspection.ts | 2 +- .../api/queries/inspectionReport.ts | 2 +- .../inspection/api/queries/objectTypes.ts | 2 +- .../api/queries/packlistDefinition.ts | 2 +- .../inspection/api/queries/textblocks.ts | 2 +- .../inspection/api/queries/webSearch.ts | 2 +- .../editor/EditChecklistDefinition.tsx | 3 +- .../elements/ChecklistDefinitionElement.tsx | 2 +- .../ChecklistDefinitionElementsList.tsx | 85 +- .../editor/elements/NoteAndHelpTextInput.tsx | 2 +- .../inner/ChecklistDefinitionAnswerItem.tsx | 163 +- ...hecklistDefinitionElementCheckboxInner.tsx | 2 +- .../inner/ChecklistDefinitionElementInner.tsx | 2 +- .../ChecklistDefinitionElementMultiInner.tsx | 10 +- .../header/ChecklistDefinitionHeaderCard.tsx | 2 +- .../header/ChecklistDefinitionHeaderRow.tsx | 2 +- .../sections/ChecklistDefinitionSection.tsx | 2 +- .../helpers/OptionalInputField.tsx | 45 +- .../checklistDefinition/helpers/helpers.ts | 2 +- .../ChecklistDefinitionOverviewTable.tsx | 2 +- .../checklistDefinition/overview/columns.tsx | 2 +- .../readOnly/CLDInfoCard.tsx | 2 +- .../readOnly/ReadOnlyCLDContent.tsx | 2 +- .../readOnly/ReadOnlyCLDPage.tsx | 2 +- .../readOnly/ReadOnlyCLDSection.tsx | 2 +- .../readOnly/elements/ReadOnlyCLDElement.tsx | 2 +- .../inner/ReadOnlyCLDElementCheckbox.tsx | 2 +- .../elements/inner/ReadOnlyCLDElementFile.tsx | 5 +- .../inner/ReadOnlyCLDElementMultiSelect.tsx | 2 +- .../elements/inner/ReadOnlyCLDElementText.tsx | 2 +- .../sidebars/UploadChecklistToRepoSidebar.tsx | 2 +- .../history/ChecklistVersionsSidebar.tsx | 2 +- .../sidebars/history/VersionSheet.tsx | 2 +- .../pending/FacilityDuplicateTile.tsx | 2 +- .../pending/InspectionDuplicateTile.tsx | 2 +- .../facility/pending/NewFacilityButton.tsx | 2 +- .../PendingFacilitiesIncidentsSidebar.tsx | 2 +- .../pending/PendingFacilitiesTable.tsx | 4 +- .../components/facility/pending/columns.tsx | 2 +- .../search/FacilityWebSearchImportSidebar.tsx | 2 +- .../search/FacilityWebSearchTable.tsx | 2 +- .../results/FacilityWebSearchResultsTable.tsx | 2 +- .../facility/search/results/columns.tsx | 2 +- .../InspectionInboxProcedureCreateSidebar.tsx | 2 +- .../inspection/EditFacilitySidebar.tsx | 2 +- .../inspection/InspectionLockInfo.tsx | 2 +- .../inspection/InspectionPhaseSelect.tsx | 2 +- .../inspection/InspectionTabHeader.tsx | 2 +- .../components/inspection/OfflineSwitch.tsx | 2 +- .../basedata/InspectionTabBasedata.tsx | 2 +- .../basedata/InspectionTypeCard.tsx | 13 +- .../contactperson/ContactPersonTile.tsx | 29 +- .../common/appointment/AppointmentSidebar.tsx | 2 +- .../common/appointment/appointmentUtils.ts | 2 +- .../common/facility/FacilityTile.tsx | 10 +- .../execution/ExecutionSidePanel.tsx | 74 +- .../FinalizeInspectionModalContent.tsx | 2 +- .../execution/InspectionTabExecution.tsx | 2 +- .../execution/checklist/Checklist.tsx | 2 +- .../execution/checklist/ChecklistSection.tsx | 2 +- .../checklist/ChecklistValidateContext.tsx | 2 +- .../checklist/form/ChecklistFileElement.tsx | 2 +- .../form/ChecklistSectionElement.tsx | 2 +- .../execution/checklist/form/helpers.ts | 2 +- .../execution/incident/IncidentSidebar.tsx | 23 +- .../execution/incident/IncidentsPanel.tsx | 2 +- .../execution/incident/IncidentsTable.tsx | 2 +- .../history/InspectionHistoryTable.tsx | 2 +- .../components/inspection/history/columns.tsx | 2 +- .../inspection/new/AddInspectionTiles.tsx | 5 +- .../inspection/new/AdditionalInfoTile.tsx | 2 +- .../planning/InspectionTabPlanning.tsx | 2 +- .../announcement/AnnouncementSidebar.tsx | 2 +- .../announcement/AnnouncementTile.tsx | 9 +- .../planning/appointment/AppointmentTile.tsx | 11 +- .../checklist/ChecklistSelectSidebar.tsx | 2 +- .../checklist/ChecklistSelectSidebarForm.tsx | 2 +- .../planning/checklist/ChecklistTile.tsx | 2 +- .../planning/inventory/InventorySidebar.tsx | 2 +- .../planning/inventory/InventoryTable.tsx | 2 +- .../planning/inventory/InventoryTile.tsx | 2 +- .../inspection/planning/packlist/Packlist.tsx | 2 +- .../packlist/PacklistSelectSidebar.tsx | 2 +- .../packlist/PacklistSelectSidebarForm.tsx | 2 +- .../planning/packlist/PacklistTile.tsx | 5 +- .../planning/resource/ResourceSidebar.tsx | 2 +- .../planning/resource/ResourceTile.tsx | 2 +- .../planning/resource/ResourcesTable.tsx | 4 +- .../planning/traveltime/TravelTimeSidebar.tsx | 2 +- .../planning/traveltime/TravelTimeTile.tsx | 28 +- .../InspectionResultSidePanel.tsx | 19 +- .../reportresult/InspectionResultSidebar.tsx | 2 +- .../reportresult/ReportApprovalButtons.tsx | 5 +- .../editor/InspectionReportEditor.tsx | 2 +- .../inspection/reportresult/reportutils.ts | 5 +- .../objectType/EditObjectTypeSidebar.tsx | 2 +- .../objectType/ObjectTypesTable.tsx | 2 +- .../CreateOrEditPacklistDefinitionSidebar.tsx | 2 +- .../PacklistDefinitionOverviewTable.tsx | 2 +- .../elements/PacklistDefinitionElement.tsx | 2 +- .../header/PacklistDefinitionHeaderCard.tsx | 2 +- .../header/PacklistDefinitionHeaderRow.tsx | 2 +- .../PacklistDefinitionRevisionTile.tsx | 2 +- .../ChecklistDefinitionRepoOverviewTable.tsx | 2 +- .../repository/MetadataDetailsSidebar.tsx | 28 +- .../components/repository/RepoCLDInfoCard.tsx | 2 +- .../repository/overviewTableColumns.tsx | 2 +- .../components/textBlock/TextBlocksTable.tsx | 2 +- .../inspection/shared/constants.ts | 5 +- .../inspection/shared/enums.ts | 2 +- .../inspection/shared/isUnknownUser.ts | 2 +- .../offline/useIsOfflineFeatureEnabled.ts | 2 +- .../shared/offline/usePrecacheInspections.ts | 2 +- .../inspection/shared/types.ts | 2 +- .../api/queries/archiving.ts | 2 +- .../proceduresTable/ProceduresTable.tsx | 2 +- .../medicalRegistry/api/clients.ts | 4 +- .../medicalRegistry/api/mutations/import.ts | 2 +- .../api/mutations/medicalRegistryEntries.ts | 8 +- .../medicalRegistry/api/queries/archiving.ts | 2 +- .../medicalRegistry/api/queries/draft.ts | 2 +- .../api/queries/medicalRegistryEntries.ts | 4 +- .../useGetMedicalProceduresTablePage.ts | 48 + .../MedicalRegistryProcedureChip.tsx | 2 +- .../create/OccupationalInformationForm.tsx | 2 +- .../create/PersonalInformationForm.tsx | 6 +- .../create/ProfessionalismInformationForm.tsx | 8 +- .../create/RequiredDocumentsForm.tsx | 2 +- .../MedicalRegistryProcedureDetails.tsx | 4 +- .../details/PracticesDetailsSection.tsx | 4 +- .../details/ProfessionalDetailsSection.tsx | 2 +- .../details/TypeOfChangeSection.tsx | 2 +- .../details/WrittenConfirmationSection.tsx | 2 +- .../finalize/FacilitySidebarContent.tsx | 2 +- .../finalize/FinalizeDraftSidebar.tsx | 2 +- .../finalize/PartialEntryErrorSidebar.tsx | 2 +- .../finalize/PersonSidebarContent.tsx | 2 +- .../finalize/ProcedureSidebarContent.tsx | 2 +- .../components/procedures/finalize/helper.ts | 2 +- .../finalize/useConfirmDraftDialog.tsx | 4 +- .../import/ImportDataFormSidebar.tsx | 25 +- .../import/ImportDataSummarySidebar.tsx | 2 +- .../MedicalRegistryEntryOverviewControls.tsx | 97 +- .../MedicalRegistryProceduresTable.tsx | 109 +- .../medicalRegistry/shared/constants.ts | 2 +- .../hooks/useMedicalRegistryFilterSettings.ts | 7 +- .../officialMedicalService/api/clients.ts | 22 +- .../api/models/AppointmentBlockGroup.ts | 10 +- .../api/models/AppointmentTypeConfig.ts | 8 +- .../api/mutations/appointmentApi.ts | 2 +- .../api/mutations/appointmentBlocksApi.ts | 2 +- .../api/mutations/employeeOmsProcedureApi.ts | 66 +- .../api/mutations/omsDocumentApi.ts | 75 + .../api/queries/apiQueryKeys.ts | 4 + .../api/queries/appointmentBlocksApi.ts | 6 +- .../api/queries/concerns.ts | 2 +- .../api/queries/employeeOmsProcedureApi.ts | 22 +- .../api/queries/waitingRoomApi.ts | 24 + .../AppointmentBlockGroupForm.tsx | 6 +- .../CreateAppointmentBlockGroupForm.tsx | 8 +- .../AppointmentBlockGroupTable.tsx | 4 +- .../components/appointmentBlocks/constants.ts | 2 +- .../components/appointmentBlocks/options.ts | 5 +- .../details/AdditionalInfoPanel.tsx | 29 +- .../details/AffectedPersonPanel.tsx | 73 +- .../procedures/details/AppointmentSidebar.tsx | 313 +- .../procedures/details/AppointmentsPanel.tsx | 2 +- .../procedures/details/AppointmentsTable.tsx | 20 +- .../procedures/details/ConcernSidebar.tsx | 6 +- .../details/EmailNotificationSidebar.tsx | 57 + .../details/EmailNotificationsForm.tsx | 62 + .../procedures/details/FacilityPanel.tsx | 73 +- .../details/MedicalOpinionStatusPanel.tsx | 111 + .../procedures/details/PhysicianForm.tsx | 2 +- .../procedures/details/PhysicianSidebar.tsx | 2 +- .../details/ProcedureActionsPanel.tsx | 4 +- .../details/ProcedureDetailsTab.tsx | 12 +- .../details/ProcedureDetailsTabHeader.tsx | 2 +- .../details/ProceduresDetailsToolbar.tsx | 11 +- .../details/UpdateAffectedPersonSidebar.tsx | 37 +- .../details/UpdateFacilitySidebar.tsx | 2 +- .../procedures/details/WaitingRoomPanel.tsx | 102 + .../details/documents/AddDocumentForm.tsx | 215 ++ .../details/documents/AddDocumentSidebar.tsx | 73 + .../procedures/details/documents/Columns.tsx | 166 + .../details/documents/DocumentForm.tsx | 156 + .../details/documents/DocumentFormContent.tsx | 192 ++ .../details/documents/DocumentSidebar.tsx | 194 ++ .../details/documents/DocumentsTable.tsx | 136 + .../documents/EditDocumentInformationForm.tsx | 88 + .../documents/EditDocumentNoteForm.tsx | 55 + .../details/documents/FilesSection.tsx | 143 + .../details/documents/RejectDocumentForm.tsx | 78 + .../details/documents/SwitchField.tsx | 49 + .../procedures/overview/CreateProcedure.tsx | 2 +- .../overview/ProceduresOverviewTable.tsx | 4 +- .../overview/procedureOverviewColumns.tsx | 22 +- .../waitingRoom/WaitingRoomTable.tsx | 92 + .../waitingRoom/waitingRoomColumns.tsx | 113 + ...lineEdit.tsx => DetailsItemInlineEdit.tsx} | 15 +- .../shared/constants.ts | 41 + .../officialMedicalService/shared/helpers.ts | 12 +- .../officialMedicalService/shared/routes.ts | 2 + .../shared/sideNavigationItem.tsx | 5 + .../shared/translations.ts | 30 + .../schoolEntry/api/queries/archiving.ts | 2 +- .../AppointmentBlockGroupForm.tsx | 11 +- .../UpdateProcedureSidebar.tsx | 2 + .../ProcedureFilterSettings.tsx | 1 + .../proceduresTable/ProceduresTable.tsx | 2 +- .../api/mapper/getActiveFilterLabels.ts | 9 +- .../mapAttributesToFilterDefinitions.ts | 8 + .../mapEvaluationFilterToFilterValue.ts | 10 +- .../mapFilterValuesToEvaluationFilters.ts | 7 + ....ts => supportedEvaluationFilterValues.ts} | 5 +- .../api/models/evaluationDetailsTableView.ts | 4 + .../api/models/evaluationDetailsViewTypes.ts | 2 + .../api/mutations/useAddAnalysis.ts | 14 +- .../queries/useGetDetailPageInformation.ts | 17 +- .../api/queries/useGetEvaluation.ts | 5 +- .../useGetEvaluationDetailsTablePage.ts | 44 +- .../ChooseAttributesStep.tsx | 131 +- .../chooseAttributesStepFormModel.ts | 2 +- .../validateChooseAttributeStep.ts | 2 +- .../CreateEvaluationFromScratchSidebar.tsx | 6 +- .../ChartsSamplePreview.tsx | 281 ++ .../ConfigureBarChartStep.tsx | 91 +- .../ConfigureHistogramChartStep.tsx | 120 +- .../configureHistogramChartFormModel.ts | 4 + .../validateConfigureHistogramChartStep.ts | 27 + .../ConfigureLineChartStep.tsx | 88 +- .../ConfigurePieChartStep.tsx | 26 +- .../ConfigureScatterChartStep.tsx | 98 +- .../CreateAnalysisSidebar.tsx | 4 + .../details/table/EvaluationDetailsTable.tsx | 6 +- .../AnalysisAccordionDetails.tsx | 12 + .../AnalysisChartDiagram.tsx | 21 +- .../AnalysisAccordion/AnalysisDiagramBox.tsx | 55 +- .../components/shared/charts/BarChart.tsx | 28 +- .../components/shared/charts/LineChart.tsx | 12 +- .../components/shared/charts/ScatterChart.tsx | 12 +- .../components/shared/charts/dataHelper.ts | 123 +- .../components/shared/charts/types.ts | 19 +- .../stiProtection/api/clients.ts | 10 +- .../api/models/AppointmentBlockGroup.ts | 10 +- .../api/models/AppointmentTypeConfig.ts | 8 +- .../api/mutations/appointmentBlocks.ts | 4 +- .../api/mutations/appointmentTypes.ts | 4 +- .../api/mutations/consultation.ts | 55 +- .../stiProtection/api/mutations/diagnosis.ts | 53 +- .../api/mutations/examination.ts | 101 +- .../api/mutations/medicalHistory.ts | 2 +- .../stiProtection/api/mutations/procedures.ts | 56 +- .../api/mutations/textTemplates.ts | 56 + .../api/mutations/waitingRoomApi.ts | 2 +- .../stiProtection/api/queries/apiQueryKeys.ts | 3 + .../api/queries/appointmentBlocks.ts | 6 +- .../api/queries/appointmentTypes.ts | 2 +- .../stiProtection/api/queries/archiving.ts | 2 +- .../stiProtection/api/queries/identity.ts | 2 + .../api/queries/medicalHistoryDocument.ts | 2 +- .../stiProtection/api/queries/procedures.ts | 10 +- .../api/queries/textTemplates.ts | 28 + .../api/queries/waitingRoomApi.ts | 2 +- .../AppointmentBlockGroupForm.tsx | 14 +- .../AppointmentBlockGroupsTable.tsx | 4 +- .../CreateAppointmentBlockGroupForm.tsx | 6 +- .../components/appointmentBlocks/options.ts | 5 +- .../AppointmentTypeEditForm.tsx | 2 +- .../AppointmentTypeOverviewTable.tsx | 2 +- .../components/appointmentTypes/columns.tsx | 2 +- .../StiProtectionProceduresTable.tsx | 58 +- .../textTemplates/TextTemplateAccordion.tsx | 23 +- .../textTemplates/TextTemplateEditSidebar.tsx | 103 + .../textTemplates/TextTemplateFields.tsx | 48 + .../TextTemplatesOverviewTable.tsx | 128 + .../TextTemplatesOverviewTableColumns.tsx | 78 + .../textTemplates/TextTemplatesSidebar.tsx | 151 +- .../TextTemplatesSidebarProvider.tsx | 81 - .../TextareaFieldWithTextTemplates.tsx | 51 +- .../components/textTemplates/constants.ts | 106 +- .../textTemplates/nextInsertPoint.ts | 45 + .../textTemplates/useFieldHandle.tsx | 62 - .../useSidebarFromSearchParam.ts | 98 + .../procedures/TabStickyBottomButtonBar.tsx | 2 +- .../AddNewProcedureSidebar.tsx | 134 +- .../addNewProcedure/SummaryForm.tsx | 10 +- .../consultation/ConsultationForm.tsx | 86 +- .../consultation/GeneralSection.tsx | 8 +- .../procedures/consultation/helpers.ts | 2 +- .../details/AdditionalDataSection.tsx | 14 +- .../details/AnonIdentityDocumentCard.tsx | 2 +- .../procedures/details/AppointmentDetails.tsx | 6 +- .../procedures/details/CheckPinSection.tsx | 2 +- .../details/CloseAndReopenDialogs.tsx | 4 +- .../details/CreateAppointmentSidebar.tsx | 30 +- .../CreateFollowUpProcedureSidebar.tsx | 219 ++ .../details/EditPersonalDataSidebar.tsx | 8 +- ...anel.tsx => FinalProcedureActionPanel.tsx} | 25 +- .../procedures/details/PersonDetails.tsx | 2 +- .../procedures/details/ProcedureDetails.tsx | 6 +- .../procedures/details/WaitingRoomSection.tsx | 12 +- .../procedures/diagnosis/DiagnosisForm.tsx | 73 +- .../features/procedures/diagnosis/helpers.ts | 8 +- .../LaboratoryTestExamination.tsx | 292 +- .../LaboratoryTestTemplates.tsx | 184 +- .../examination/laboratoryTest/helpers.ts | 397 +++ .../rapidTest/RapidTestExamination.tsx | 143 +- .../rapidTest/RapidTestTemplates.tsx | 47 +- .../examination/rapidTest/helpers.ts | 150 + .../MedicalHistoryForm.config.ts | 4 +- .../medicalHistory/MedicalHistoryForm.tsx | 13 +- .../procedures/medicalHistory/helpers.ts | 12 +- .../procedures/medicalHistory/options.ts | 4 +- .../features/procedures/translations.ts | 2 +- .../features/waitingRoom/StatusChip.tsx | 2 +- .../features/waitingRoom/WaitingRoomTable.tsx | 4 +- .../stiProtection/shared/constants.ts | 11 +- .../stiProtection/shared/helpers.ts | 4 +- .../procedure}/AppointmentForm.tsx | 44 +- .../procedure}/SharePinModal.tsx | 0 .../shared/procedure/helpers.tsx | 15 + .../stiProtection/shared/procedure/mappers.ts | 94 + .../shared/procedure/useFormWithSteps.ts | 59 + .../shared/sideNavigationItem.tsx | 5 + .../travelMedicine/api/queries/archiving.ts | 2 +- .../AppointmentBlockGroupForm.tsx | 11 +- .../VaccinationConsultationsOverviewTable.tsx | 2 +- .../baseData/PatientDetails.tsx | 58 +- .../baseData/ProcedureCreatedByTile.tsx | 34 +- .../baseData/TravelDataTile.tsx | 121 +- .../sidebarForms/InformationStatementForm.tsx | 6 +- .../sidebarForms/OtherServiceAppliedForm.tsx | 5 +- .../sidebarForms/OtherServicesFields.tsx | 5 +- .../sidebarForms/ServiceAppliedForm.tsx | 8 +- .../sidebarForms/VaccinationFields.tsx | 5 +- .../src/lib/opendata/api/clients.ts | 2 +- .../components/EditEntrySidebarContent.tsx | 2 +- .../components/EntryDetailsSidebar.tsx | 2 +- .../lib/opendata/components/OpenDataTable.tsx | 2 +- .../opendata/components/VersionFileCard.tsx | 2 +- .../opendata/components/openDataColumns.tsx | 2 +- employee-portal/src/lib/opendata/helper.ts | 5 +- .../hooks/useOpenDataFilterSettings.ts | 2 +- .../src/lib/opendata/mutations/opendata.ts | 6 +- .../src/lib/opendata/queries/opendata.ts | 2 +- employee-portal/src/lib/shared/api/clients.ts | 16 +- .../src/lib/shared/api/mutations/archiving.ts | 5 +- .../src/lib/shared/api/mutations/gdpr.ts | 2 +- .../shared/api/mutations/inboxProcedures.ts | 4 +- .../src/lib/shared/api/mutations/libEditor.ts | 2 +- .../src/lib/shared/api/queries/archiving.ts | 6 +- .../src/lib/shared/api/queries/gdpr.ts | 8 +- .../lib/shared/api/queries/inboxProcedures.ts | 2 +- .../queries/statisticsProcedureReference.ts | 4 +- .../src/lib/shared/api/queries/tasks.ts | 4 +- .../src/lib/shared/components/FileCard.tsx | 2 +- .../components/address/BaseAddressDetails.tsx | 43 +- .../AppointmentStaffField.tsx | 70 +- .../AppointmentStaffSelection.tsx | 16 +- .../components/archiving/ArchiveView.tsx | 2 +- .../api/models/archivableProcedure.ts | 2 +- .../components/ArchivingRelevanceChip.tsx | 2 +- .../archiveAdminView/ArchiveAdminTable.tsx | 6 +- .../archiveAdminTableColumns.tsx | 8 +- .../components/archiveView/ArchiveTable.tsx | 2 +- .../archiveView/ArchiveTableTitle.tsx | 2 +- .../archiveView/archiveTableColumns.tsx | 2 +- .../hooks/useArchiveAdminFilterSettings.ts | 2 +- .../hooks/useArchiveFilterSettings.ts | 2 +- .../shared/components/auditlog/constants.ts | 2 +- .../display/CentralFileFacilityDetails.tsx | 23 +- .../display/CentralFilePersonDetails.tsx | 46 +- .../centralFile/sync/SyncBarrier.tsx | 2 +- .../contentEditor/ContentDisplay.tsx | 2 +- .../contentEditor/ContentEditor.tsx | 2 +- .../contentEditor/ContentElement.tsx | 2 +- .../contentEditor/ContentElementAudio.tsx | 2 +- .../ContentElementFullTextEditor.tsx | 2 +- .../contentEditor/ContentElementImages.tsx | 4 +- .../ContentElementProperties.tsx | 2 +- .../ContentElementPropertySheet.tsx | 2 +- .../contentEditor/ContentElementQA.tsx | 2 +- .../contentEditor/ContentElementQAEditor.tsx | 2 +- .../ContentElementTextEditor.tsx | 2 +- .../components/detailsSection/DetailsCell.tsx | 10 + .../detailsSection/items/DetailsItem.tsx | 96 + .../items/ExternalLinkDetailsItem.tsx | 69 + .../filterSettings/FilterSettings.tsx | 14 + .../SearchInstitutionFilter.tsx | 5 +- .../components/filterSettings/TextFilter.tsx | 40 + .../filterSettings/models/FilterDefinition.ts | 4 +- .../filterSettings/models/FilterValue.ts | 7 +- .../filterSettings/models/TextFilter.ts | 16 + .../useSearchParamStateProvider.ts | 2 + .../components/form/MultiFormButtonBar.tsx | 6 + .../components/formDialog/FormDialog.tsx | 4 +- .../components/formFields/CheckboxField.tsx | 68 +- .../formFields/SearchContactField.tsx | 2 - .../SearchMultipleContactsField.tsx | 2 - .../formFields/SelectContactField.tsx | 2 + .../components/formFields/TextareaField.tsx | 2 + .../gdpr/useGdprValidationTasksAlert.ts | 30 +- .../components/icons/AddTextTemplate.tsx | 4 +- .../components/infoTile/InformationSheet.tsx | 14 +- .../LegacyPersonSidebar.tsx | 2 +- .../form/LegacyBasePersonForm.tsx | 6 +- .../search/LegacyPersonSearchForm.tsx | 2 + .../personSearch/PersonSearchForm.tsx | 2 + .../personSidebar/form/DefaultPersonForm.tsx | 6 +- .../components/personSidebar/helpers.ts | 1 + .../search/DefaultSearchPersonFormFields.tsx | 6 +- .../shared/components/procedures/helper.ts | 2 +- .../inbox/InboxProcedureDetailsSidebar.tsx | 6 +- .../inbox/InboxProcedureStatusChip.tsx | 2 +- .../procedures/inbox/InboxProceduresPage.tsx | 2 +- .../procedures/inbox/InboxProceduresTable.tsx | 2 +- .../components/procedures/inbox/columns.tsx | 2 +- .../components/procedures/inbox/constants.ts | 2 +- .../useCloseInboxProcedureStatusTemplate.ts | 6 +- .../procedures/progress-entries/EntryFile.tsx | 2 +- .../progress-entries/FileCardWithActions.tsx | 2 +- .../progress-entries/FileOrDeletionNote.tsx | 2 +- .../ProgressEntriesContext.tsx | 2 +- .../progress-entries/SortSelect.tsx | 2 +- .../buildTimelineEntryProps.tsx | 6 +- .../procedures/progress-entries/constants.tsx | 4 +- .../procedures/progress-entries/helper.ts | 4 +- .../hooks/useHasEditRights.tsx | 2 +- .../hooks/useProgressEntriesFilterSettings.ts | 2 +- .../procedures/progress-entries/mapper.ts | 4 +- .../mutations/approvalRequestApi.ts | 2 +- .../progress-entries/mutations/fileApi.ts | 2 +- .../mutations/progressEntryApi.ts | 6 +- .../queries/progressEntryApi.ts | 2 +- .../sidebars/CreateProgressEntrySidebar.tsx | 2 +- .../ApprovalRequestCard.tsx | 5 +- .../FileAsApprovalRequestEntity.tsx | 2 +- .../ProgressEntryAsApprovalRequestEntity.tsx | 2 +- .../DetailsContentWrapper.tsx | 4 +- .../DetailsHistory.tsx | 2 +- .../InboxProgressEntryDetails.tsx | 2 +- .../ManualProgressEntryDetails.tsx | 4 +- .../SystemProgressEntryDetails.tsx | 2 +- .../procedures/progress-entries/types.ts | 6 +- .../shared/components/table/TableSheet.tsx | 3 +- .../shared/components/users/userFormatter.ts | 8 +- .../src/lib/shared/helpers/facilityUtils.ts | 2 +- .../src/lib/shared/helpers/guards.ts | 2 +- .../hooks/searchParams/useSearchParam.ts | 13 +- .../src/serviceWorker/common/common.ts | 2 +- .../controller/getPendingFacilities.ts | 2 +- .../inspection/controller/updateChecklist.ts | 2 +- .../sw/inspection/controller/updateFile.ts | 2 +- .../inspection/controller/updateIncidents.ts | 2 +- .../inspection/controller/updateInspection.ts | 2 +- .../inspection/controller/updatePacklists.ts | 2 +- .../sw/inspection/service/getFacilities.ts | 5 +- .../sw/inspection/service/updateChecklist.ts | 2 +- .../sw/inspection/service/updateFile.ts | 2 +- .../sw/inspection/service/updateIncidents.ts | 2 +- .../sw/inspection/service/updateInspection.ts | 2 +- .../service/updatePacklistElement.ts | 2 +- .../sw/inspection/test/cacheMock.ts | 2 +- .../src/serviceWorker/sw/replaceRecursive.ts | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 +- lib-portal/gradleDependencies.json | 2 +- lib-portal/package.json | 10 +- .../src/api/errorInterceptionMiddleware.ts | 7 +- lib-portal/src/api/useHandledMutation.ts | 42 +- .../medicalRegistry/api/mapper.ts | 2 +- .../medicalRegistry/constants.ts | 2 +- ...edicalRegistryCreateProcedureFormValues.ts | 2 +- .../medicalRegistry/sections.ts | 2 +- .../src/components/formFields/InputField.tsx | 62 +- .../src/components/formFields/SelectField.tsx | 64 +- .../AppointmentListForDate.tsx | 16 +- .../autocomplete/SingleAutocompleteField.tsx | 6 +- .../navigation/InternalLinkIconButton.tsx | 12 +- .../components/snackbar/SnackbarProvider.tsx | 20 +- lib-portal/src/helpers/validators.ts | 30 +- package.json | 6 +- packages/auditlog-api/.gitignore | 2 + packages/auditlog-api/build.gradle | 9 + .../auditlog-api}/buildscript-gradle.lockfile | 0 packages/auditlog-api/package.json | 10 + packages/auditlog-api/tsconfig.json | 3 + packages/auditlog-api/tsup.config.ts | 8 + packages/central-repository-api/.gitignore | 2 + packages/central-repository-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 0 packages/central-repository-api/package.json | 10 + packages/central-repository-api/tsconfig.json | 3 + .../central-repository-api/tsup.config.ts | 8 + packages/chat-management-api/.gitignore | 2 + packages/chat-management-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/chat-management-api/package.json | 10 + packages/chat-management-api/tsconfig.json | 3 + packages/chat-management-api/tsup.config.ts | 8 + .../dental/src/api/models/ChildExamination.ts | 4 + packages/dental/src/api/models/Examination.ts | 17 +- .../src/api/models/ExaminationResult.ts | 84 +- .../src/api/models/ExaminationStatus.ts | 5 +- .../dental/src/api/models/ToothDiagnosis.ts | 27 + .../api/mutations/prophylaxisSessionApi.ts | 51 + packages/inspection-api/.gitignore | 2 + packages/inspection-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/inspection-api/package.json | 10 + packages/inspection-api/tsconfig.json | 3 + packages/inspection-api/tsup.config.ts | 8 + packages/lib-editor-api/.gitignore | 2 + packages/lib-editor-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/lib-editor-api/package.json | 10 + packages/lib-editor-api/tsconfig.json | 3 + packages/lib-editor-api/tsup.config.ts | 8 + .../gradleDependencies.json | 2 +- packages/lib-employee-portal/package.json | 3 +- packages/lib-procedures-api/.gitignore | 2 + packages/lib-procedures-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/lib-procedures-api/package.json | 10 + packages/lib-procedures-api/tsconfig.json | 3 + packages/lib-procedures-api/tsup.config.ts | 8 + packages/lib-statistics-api/.gitignore | 2 + packages/lib-statistics-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/lib-statistics-api/package.json | 10 + packages/lib-statistics-api/tsconfig.json | 3 + packages/lib-statistics-api/tsup.config.ts | 8 + packages/medical-registry-api/.gitignore | 2 + packages/medical-registry-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/medical-registry-api/package.json | 10 + packages/medical-registry-api/tsconfig.json | 3 + packages/medical-registry-api/tsup.config.ts | 8 + .../official-medical-service-api/.gitignore | 2 + .../official-medical-service-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + .../official-medical-service-api/package.json | 10 + .../tsconfig.json | 3 + .../tsup.config.ts | 8 + packages/opendata-api/.gitignore | 2 + packages/opendata-api/build.gradle | 9 + .../opendata-api/buildscript-gradle.lockfile | 4 + packages/opendata-api/package.json | 10 + packages/opendata-api/tsconfig.json | 3 + packages/opendata-api/tsup.config.ts | 8 + packages/sti-protection-api/.gitignore | 2 + packages/sti-protection-api/build.gradle | 9 + .../buildscript-gradle.lockfile | 4 + packages/sti-protection-api/package.json | 10 + packages/sti-protection-api/tsconfig.json | 3 + packages/sti-protection-api/tsup.config.ts | 8 + pnpm-lock.yaml | 2885 +++++------------ pnpm-workspace.yaml | 32 +- reverse-proxy/citizen-portal.conf | 3 +- settings.gradle | 3 - vitest.config.ts | 13 +- vitest.workspace.ts | 13 - 1189 files changed, 21759 insertions(+), 8403 deletions(-) create mode 100644 backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirth.java create mode 100644 backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirthValidator.java rename backend/api-commons/src/main/java/de/eshg/{CustomValidations => validation/constraints}/EmailAddressConstraint.java (95%) rename backend/api-commons/src/main/java/de/eshg/{CustomValidations => validation/constraints}/MandatoryEmailAddressConstraint.java (93%) create mode 100644 backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogClientTestHelperApi.java rename backend/auditlog-api/src/main/java/de/eshg/auditlog/{SharedAuditLogTestHelperApi.java => AuditLogServiceTestHelperApi.java} (64%) delete mode 100644 backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogTestHelperApi.java rename backend/auditlog/src/main/java/de/eshg/auditlog/{AuditLogTestHelperController.java => AuditLogServiceTestHelperController.java} (59%) create mode 100644 backend/auditlog/src/main/resources/migrations/0008_add_auditlog_entry.xml create mode 100644 backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginType.java create mode 100644 backend/base/src/main/resources/migrations/0040_add_auditlog_entry.xml create mode 100644 backend/base/src/main/resources/migrations/0041_remove_gdpr_status_open.xml create mode 100644 backend/dental/src/main/resources/migrations/0029_add_auditlog_entry.xml create mode 100644 backend/dental/src/main/resources/migrations/0030_add_modified_at_fluoridation.xml create mode 100644 backend/dental/src/main/resources/migrations/0031_fluoride_varnish_applied_optional.xml create mode 100644 backend/dental/src/main/resources/migrations/0032_migrate_examination_result_to_use_sequences.xml create mode 100644 backend/inspection/src/main/resources/migrations/0064_add_auditlog_entry.xml create mode 100644 backend/keycloak/resources/themes/custom-keycloak/login/date-of-birth-access-code.ftl rename backend/keycloak/resources/themes/custom-keycloak/login/{access-code.ftl => pin-access-code.ftl} (67%) create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeAwareAuthenticationContextBean.java create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeForm.java create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeFormFactory.java create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DeprecatedAccessCodeFormFactory.java create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeForm.java create mode 100644 backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeFormFactory.java create mode 100644 backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogFormatter.java create mode 100644 backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntry.java create mode 100644 backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntryRepository.java create mode 100644 backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogLibraryDomainModelAutoConfiguration.java create mode 100644 backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/DataPrivacyCategory.java create mode 100644 backend/measles-protection/src/main/resources/migrations/0046_add_auditlog_entry.xml create mode 100644 backend/measles-protection/src/main/resources/migrations/0047_convert_duration_columns_to_interval.xml create mode 100644 backend/medical-registry/src/main/resources/migrations/0008_add_auditlog_entry.xml create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentController.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentMapper.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentService.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentStatusDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/GetDocumentsResponse.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentInformationRequest.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentNoteRequest.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentReviewRequest.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PostDocumentRequest.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/ReviewResultDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocument.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentRepository.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentStatus.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileController.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileMapper.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileService.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/api/OmsFileDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFile.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFileRepository.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/MailClient.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationProperties.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationService.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationText.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/AcceptDraftProcedureResponse.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/MedicalOpinionStatusDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/PatchEmployeeOmsProcedureEmailNotificationsRequest.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/MedicalOpinionStatus.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsDocumentTestHelperFile.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/DocumentPopulationDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/FileTestDataConfig.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/user/CitizenAccessCodeUserClient.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/PagedWaitingRoomProcedures.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomController.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomMapper.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomService.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomSpecification.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/GetWaitingRoomProceduresResponse.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedureDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedurePaginationAndSortParameters.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomSortKey.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingStatusDto.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingRoom.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingStatus.java create mode 100644 backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/util/WaitingRoomPageSpec.java create mode 100644 backend/official-medical-service/src/main/resources/documents/testHelperSampleDocument.pdf create mode 100644 backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.jpg create mode 100644 backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.png create mode 100644 backend/official-medical-service/src/main/resources/notifications/default/de/new_citizen_user.txt create mode 100644 backend/official-medical-service/src/main/resources/notifications/ga_frankfurt/de/new_citizen_user.txt create mode 100644 backend/school-entry/src/main/resources/migrations/0077_add_auditlog_entry.xml create mode 100644 backend/school-entry/src/main/resources/migrations/0078_convert_duration_columns_to_interval.xml create mode 100644 backend/statistics/lib/de/linearbits/jhpl/jhpl-0.0.1.jar create mode 100644 backend/statistics/lib/org/deidentifier/arx/libarx-3.9.1-min.jar create mode 100644 backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumnDataPrivacyCategory.java create mode 100644 backend/statistics/src/main/resources/migrations/0044_add_auditlog_entry.xml create mode 100644 backend/statistics/src/main/resources/migrations/0045_data_privacy_category.xml create mode 100644 backend/statistics/src/main/resources/migrations/0046_add_minBin_and_maxBin.xml create mode 100644 backend/statistics/src/main/resources/migrations/0047_migrate_minBin_and_maxBin.xml create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/DepartmentInfoService.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionCitizenController.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationRequest.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationResponse.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetDepartmentInfoRequest.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursRequest.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursResponse.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/StiAppointmentTypeDto.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoConfig.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoProperties.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/OpeningHoursProperties.java create mode 100644 backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/TextTemplatePopulator.java create mode 100644 backend/sti-protection/src/main/resources/application-health-department-frankfurt.properties create mode 100644 backend/sti-protection/src/main/resources/migrations/0045_add_sample_bar_code.xml create mode 100644 backend/sti-protection/src/main/resources/migrations/0046_add_appointment_start.xml create mode 100644 backend/sti-protection/src/main/resources/migrations/0047_add_auditlog_entry.xml create mode 100644 backend/sti-protection/src/main/resources/migrations/0048_convert_duration_columns_to_interval.xml delete mode 100644 backend/test-commons/src/main/java/de/eshg/base/AuditLogTraits.java delete mode 100644 backend/test-commons/src/main/java/de/eshg/base/StaticLogDirExtension.java create mode 100644 backend/travel-medicine/src/main/resources/migrations/0057_add_auditlog_entry.xml create mode 100644 backend/travel-medicine/src/main/resources/migrations/0058_convert_duration_columns_to_interval.xml delete mode 100644 buildSrc/src/main/groovy/openapi-generator.gradle delete mode 100644 citizen-portal-api/.gitignore delete mode 100644 citizen-portal-api/build.gradle delete mode 100644 citizen-portal-api/package.json delete mode 100644 citizen-portal-api/tsconfig.json delete mode 100644 employee-portal-api/.gitignore delete mode 100644 employee-portal-api/build.gradle delete mode 100644 employee-portal-api/package.json delete mode 100644 employee-portal-api/tsconfig.json create mode 100644 employee-portal/src/app/(businessModules)/official-medical-service/procedures/[id]/documents/page.tsx create mode 100644 employee-portal/src/app/(businessModules)/official-medical-service/waiting-room/page.tsx create mode 100644 employee-portal/src/app/(businessModules)/sti-protection/text-templates/page.tsx create mode 100644 employee-portal/src/app/playground/teeth/page.tsx create mode 100644 employee-portal/src/lib/baseModule/components/gdpr/procedure/MatterOfConcernDisplayField.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ChangeReasonForAbsenceModal.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/UpdateProphylaxisSessionSidebar.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/AddToothButton.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationJawTabs.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/FullDentitionOverview.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Legend.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/LowerJawForm.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ResultInputField.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/UpperJawForm.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider.tsx create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/actions.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/constants.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/dentalExaminationStore.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/factories.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types.ts delete mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/formComponents.tsx delete mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationForm.ts rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{examination/ProphylaxisSessionExaminationBottomBar.tsx => participantExamination/ParticipantExaminationBottomBar.tsx} (90%) rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{examination/ProphylaxisSessionExaminationForm.tsx => participantExamination/ParticipantExaminationForm.tsx} (84%) create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationPage.tsx rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{examination/ProphylaxisSessionExaminationToolbar.tsx => participantExamination/ParticipantExaminationToolbar.tsx} (87%) create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantExaminationForm.ts rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{examination/useProphylaxisSessionExaminationNavigation.ts => participantExamination/useParticipantNavigation.ts} (91%) rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{store => prophylaxisSessionStore}/ProphylaxisSessionStoreProvider.tsx (89%) create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/actions.ts rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{store => prophylaxisSessionStore}/participantFilters.ts (100%) rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{store => prophylaxisSessionStore}/participantSorting.ts (97%) rename employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/{store => prophylaxisSessionStore}/prophylaxisSessionStore.ts (63%) create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncIncomingProphylaxisSessionChanges.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncOutgoingProphylaxisSessionChanges.ts create mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/staff.ts delete mode 100644 employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/replaceExaminationResult.ts create mode 100644 employee-portal/src/lib/businessModules/medicalRegistry/api/queries/useGetMedicalProceduresTablePage.ts create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi.ts create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/api/queries/waitingRoomApi.ts create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationSidebar.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationsForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/MedicalOpinionStatusPanel.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/WaitingRoomPanel.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentSidebar.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/Columns.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentFormContent.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentSidebar.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentsTable.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentInformationForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentNoteForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/RejectDocumentForm.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/WaitingRoomTable.tsx create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/waitingRoomColumns.tsx rename employee-portal/src/lib/businessModules/officialMedicalService/shared/{DetailsCellInlineEdit.tsx => DetailsItemInlineEdit.tsx} (50%) create mode 100644 employee-portal/src/lib/businessModules/officialMedicalService/shared/translations.ts rename employee-portal/src/lib/businessModules/statistics/api/mapper/{suppertedEvaluationFilterValues.ts => supportedEvaluationFilterValues.ts} (64%) create mode 100644 employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview.tsx create mode 100644 employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/validateConfigureHistogramChartStep.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/api/mutations/textTemplates.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/api/queries/textTemplates.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateEditSidebar.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateFields.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTable.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTableColumns.tsx delete mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebarProvider.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/nextInsertPoint.ts delete mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useFieldHandle.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useSidebarFromSearchParam.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateFollowUpProcedureSidebar.tsx rename employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/{CloseProcedurePanel.tsx => FinalProcedureActionPanel.tsx} (68%) create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/helpers.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/helpers.ts rename employee-portal/src/lib/businessModules/stiProtection/{features/procedures/addNewProcedure => shared/procedure}/AppointmentForm.tsx (87%) rename employee-portal/src/lib/businessModules/stiProtection/{features/procedures/addNewProcedure => shared/procedure}/SharePinModal.tsx (100%) create mode 100644 employee-portal/src/lib/businessModules/stiProtection/shared/procedure/helpers.tsx create mode 100644 employee-portal/src/lib/businessModules/stiProtection/shared/procedure/mappers.ts create mode 100644 employee-portal/src/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps.ts create mode 100644 employee-portal/src/lib/shared/components/detailsSection/items/DetailsItem.tsx create mode 100644 employee-portal/src/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem.tsx create mode 100644 employee-portal/src/lib/shared/components/filterSettings/TextFilter.tsx create mode 100644 employee-portal/src/lib/shared/components/filterSettings/models/TextFilter.ts create mode 100644 packages/auditlog-api/.gitignore create mode 100644 packages/auditlog-api/build.gradle rename {citizen-portal-api => packages/auditlog-api}/buildscript-gradle.lockfile (100%) create mode 100644 packages/auditlog-api/package.json create mode 100644 packages/auditlog-api/tsconfig.json create mode 100644 packages/auditlog-api/tsup.config.ts create mode 100644 packages/central-repository-api/.gitignore create mode 100644 packages/central-repository-api/build.gradle rename {employee-portal-api => packages/central-repository-api}/buildscript-gradle.lockfile (100%) create mode 100644 packages/central-repository-api/package.json create mode 100644 packages/central-repository-api/tsconfig.json create mode 100644 packages/central-repository-api/tsup.config.ts create mode 100644 packages/chat-management-api/.gitignore create mode 100644 packages/chat-management-api/build.gradle create mode 100644 packages/chat-management-api/buildscript-gradle.lockfile create mode 100644 packages/chat-management-api/package.json create mode 100644 packages/chat-management-api/tsconfig.json create mode 100644 packages/chat-management-api/tsup.config.ts create mode 100644 packages/dental/src/api/models/ToothDiagnosis.ts create mode 100644 packages/inspection-api/.gitignore create mode 100644 packages/inspection-api/build.gradle create mode 100644 packages/inspection-api/buildscript-gradle.lockfile create mode 100644 packages/inspection-api/package.json create mode 100644 packages/inspection-api/tsconfig.json create mode 100644 packages/inspection-api/tsup.config.ts create mode 100644 packages/lib-editor-api/.gitignore create mode 100644 packages/lib-editor-api/build.gradle create mode 100644 packages/lib-editor-api/buildscript-gradle.lockfile create mode 100644 packages/lib-editor-api/package.json create mode 100644 packages/lib-editor-api/tsconfig.json create mode 100644 packages/lib-editor-api/tsup.config.ts create mode 100644 packages/lib-procedures-api/.gitignore create mode 100644 packages/lib-procedures-api/build.gradle create mode 100644 packages/lib-procedures-api/buildscript-gradle.lockfile create mode 100644 packages/lib-procedures-api/package.json create mode 100644 packages/lib-procedures-api/tsconfig.json create mode 100644 packages/lib-procedures-api/tsup.config.ts create mode 100644 packages/lib-statistics-api/.gitignore create mode 100644 packages/lib-statistics-api/build.gradle create mode 100644 packages/lib-statistics-api/buildscript-gradle.lockfile create mode 100644 packages/lib-statistics-api/package.json create mode 100644 packages/lib-statistics-api/tsconfig.json create mode 100644 packages/lib-statistics-api/tsup.config.ts create mode 100644 packages/medical-registry-api/.gitignore create mode 100644 packages/medical-registry-api/build.gradle create mode 100644 packages/medical-registry-api/buildscript-gradle.lockfile create mode 100644 packages/medical-registry-api/package.json create mode 100644 packages/medical-registry-api/tsconfig.json create mode 100644 packages/medical-registry-api/tsup.config.ts create mode 100644 packages/official-medical-service-api/.gitignore create mode 100644 packages/official-medical-service-api/build.gradle create mode 100644 packages/official-medical-service-api/buildscript-gradle.lockfile create mode 100644 packages/official-medical-service-api/package.json create mode 100644 packages/official-medical-service-api/tsconfig.json create mode 100644 packages/official-medical-service-api/tsup.config.ts create mode 100644 packages/opendata-api/.gitignore create mode 100644 packages/opendata-api/build.gradle create mode 100644 packages/opendata-api/buildscript-gradle.lockfile create mode 100644 packages/opendata-api/package.json create mode 100644 packages/opendata-api/tsconfig.json create mode 100644 packages/opendata-api/tsup.config.ts create mode 100644 packages/sti-protection-api/.gitignore create mode 100644 packages/sti-protection-api/build.gradle create mode 100644 packages/sti-protection-api/buildscript-gradle.lockfile create mode 100644 packages/sti-protection-api/package.json create mode 100644 packages/sti-protection-api/tsconfig.json create mode 100644 packages/sti-protection-api/tsup.config.ts delete mode 100644 vitest.workspace.ts diff --git a/admin-portal/src/lib/components/table/Filter.tsx b/admin-portal/src/lib/components/table/Filter.tsx index 9a0bb2017..93d3c2eb5 100644 --- a/admin-portal/src/lib/components/table/Filter.tsx +++ b/admin-portal/src/lib/components/table/Filter.tsx @@ -35,6 +35,10 @@ import { HeaderButtons, } from "@/lib/components/table/addEditColumns"; import { getActiveLabel } from "@/lib/components/table/cell/ActiveCell"; +import { + formatActorSelector, + isActorSelector, +} from "@/lib/components/table/cell/StaticActorSelectorCell"; import { Actor } from "@/lib/components/view/actors/ActorTable"; import { UniqueEntity } from "@/lib/helpers/entities"; import { entityToString } from "@/lib/helpers/entityToString"; @@ -140,7 +144,7 @@ export function Filter<TData extends UniqueEntity, TValue>({ function getColumnValues<TData extends UniqueEntity, TValue>( column: Column<TData, TValue>, table: Table<TData>, -) { +): string[] { return unique( table .getCenterRows() @@ -166,6 +170,9 @@ function getLabels(value: unknown): string[] { if (isEntity(value)) { return [entityToString(value, true)]; } + if (isActorSelector(value)) { + return [formatActorSelector(value)]; + } return [String(value)]; } @@ -299,10 +306,10 @@ export function getActorSelectorFilterFn( return thisOrParentOrChildApplies( row, (orig) => - orig[columnId] !== undefined && - Object.values(orig[columnId]).some((v: string) => - v.toLowerCase().includes(lowerFilterValue), - ), + !isNullish(orig[columnId]) && + formatActorSelector(orig[columnId]) + .toLowerCase() + .includes(lowerFilterValue), ); }; } diff --git a/admin-portal/src/lib/components/table/cell/ActorSelectorCell.tsx b/admin-portal/src/lib/components/table/cell/ActorSelectorCell.tsx index 17c3c4724..594c8ab93 100644 --- a/admin-portal/src/lib/components/table/cell/ActorSelectorCell.tsx +++ b/admin-portal/src/lib/components/table/cell/ActorSelectorCell.tsx @@ -173,7 +173,7 @@ function SingleSelect( ); const color = serverError ? "danger" : undefined; - const value = props.value ? props.value : "*"; + const value = props.value ?? "*"; const selectProps: SelectProps<string, false> = { value, multiple: false, diff --git a/admin-portal/src/lib/components/table/cell/StaticActorSelectorCell.tsx b/admin-portal/src/lib/components/table/cell/StaticActorSelectorCell.tsx index 5d57497bb..b5aa354b1 100644 --- a/admin-portal/src/lib/components/table/cell/StaticActorSelectorCell.tsx +++ b/admin-portal/src/lib/components/table/cell/StaticActorSelectorCell.tsx @@ -47,6 +47,23 @@ export function formatActorSelector(s: ApiAdminActorSelector) { return `${format(s.federalState)}/${format(s.orgUnitType)}/${format(s.orgUnitName)}/${format(s.actorType)}/${format(s.actorName)}`; } +export function isActorSelector(s: unknown): s is ApiAdminActorSelector { + return ( + typeof s === "object" && + s !== null && + "federalState" in s && + ["string", "undefined"].includes(typeof s.federalState) && + "orgUnitType" in s && + ["string", "undefined"].includes(typeof s.orgUnitType) && + "orgUnitName" in s && + ["string", "undefined"].includes(typeof s.orgUnitName) && + "actorType" in s && + ["string", "undefined"].includes(typeof s.actorType) && + "actorName" in s && + ["string", "undefined"].includes(typeof s.actorName) + ); +} + function format(value: string | undefined): string { if (value == null) { return "*"; diff --git a/admin-portal/src/lib/components/table/cell/StringCell.tsx b/admin-portal/src/lib/components/table/cell/StringCell.tsx index 2ac9d5e6a..8641f0227 100644 --- a/admin-portal/src/lib/components/table/cell/StringCell.tsx +++ b/admin-portal/src/lib/components/table/cell/StringCell.tsx @@ -44,7 +44,7 @@ function EditableStringCell( [props.column.id, props.row, props.table.options.meta], ); - const value = props.getValue(); + const value = props.getValue() ?? ""; const serverError = !!errorMessage?.ids.includes(props.row.original.id) && !!errorMessage?.columns?.includes(props.column.id); diff --git a/backend/api-commons/build.gradle b/backend/api-commons/build.gradle index a611b99d7..1b1a36461 100644 --- a/backend/api-commons/build.gradle +++ b/backend/api-commons/build.gradle @@ -7,4 +7,9 @@ dependencies { api 'com.fasterxml.jackson.core:jackson-annotations' api 'io.swagger.core.v3:swagger-annotations-jakarta:latest.release' api 'org.springframework:spring-web' + + testImplementation project(':test-commons') + testRuntimeOnly 'org.hibernate.validator:hibernate-validator' + testRuntimeOnly 'jakarta.el:jakarta.el-api:latest.release' + testRuntimeOnly 'org.glassfish:jakarta.el:latest.release' } diff --git a/backend/api-commons/gradle.lockfile b/backend/api-commons/gradle.lockfile index f7af9e94f..b63032b03 100644 --- a/backend/api-commons/gradle.lockfile +++ b/backend/api-commons/gradle.lockfile @@ -4,30 +4,70 @@ ch.qos.logback:logback-classic:1.5.12=testCompileClasspath,testRuntimeClasspath ch.qos.logback:logback-core:1.5.12=testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.18.2=testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.18.2=testRuntimeClasspath com.fasterxml.jackson:jackson-bom:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml:classmate:1.7.0=testRuntimeClasspath +com.github.docker-java:docker-java-api:3.4.0=testRuntimeClasspath +com.github.docker-java:docker-java-transport-zerodep:3.4.0=testRuntimeClasspath +com.github.docker-java:docker-java-transport:3.4.0=testRuntimeClasspath +com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.10.0=testRuntimeClasspath +com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0=testRuntimeClasspath +com.google.code.findbugs:jsr305:3.0.2=testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.28.0=testRuntimeClasspath +com.google.guava:failureaccess:1.0.2=testRuntimeClasspath +com.google.guava:guava:33.3.1-jre=testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=testRuntimeClasspath +com.google.j2objc:j2objc-annotations:3.0.0=testRuntimeClasspath +com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,testRuntimeClasspath com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine:1.3.0=testRuntimeClasspath +com.tngtech.archunit:archunit-junit5:1.3.0=testRuntimeClasspath +com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.18.0=testRuntimeClasspath +de.cronn:commons-lang:1.3=testCompileClasspath,testRuntimeClasspath +de.cronn:postgres-snapshot-util:1.4=testRuntimeClasspath +de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath +de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath +jakarta.el:jakarta.el-api:6.0.1=testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=testCompileClasspath,testRuntimeClasspath +junit:junit:4.13.2=testRuntimeClasspath net.bytebuddy:byte-buddy-agent:1.15.11=testCompileClasspath,testRuntimeClasspath net.bytebuddy:byte-buddy:1.15.11=testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna:5.13.0=testRuntimeClasspath net.minidev:accessors-smart:2.5.1=testCompileClasspath,testRuntimeClasspath net.minidev:json-smart:2.5.1=testCompileClasspath,testRuntimeClasspath +net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath +org.apache.commons:commons-compress:1.24.0=testRuntimeClasspath +org.apache.commons:commons-lang3:3.17.0=testRuntimeClasspath org.apache.logging.log4j:log4j-api:2.24.3=testCompileClasspath,testRuntimeClasspath org.apache.logging.log4j:log4j-to-slf4j:2.24.3=testCompileClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath +org.bouncycastle:bcpkix-jdk18on:1.80=testRuntimeClasspath +org.bouncycastle:bcprov-jdk18on:1.80=testRuntimeClasspath +org.bouncycastle:bcutil-jdk18on:1.80=testRuntimeClasspath +org.checkerframework:checker-qual:3.43.0=testRuntimeClasspath +org.glassfish:jakarta.el:4.0.2=testRuntimeClasspath +org.hamcrest:hamcrest-core:2.2=testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.hibernate.validator:hibernate-validator:8.0.2.Final=testRuntimeClasspath +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt +org.jboss.logging:jboss-logging:3.6.1.Final=testRuntimeClasspath +org.jetbrains:annotations:17.0.0=testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -40,19 +80,24 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt +org.postgresql:postgresql:42.7.4=testRuntimeClasspath +org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-dependencies:3.3.5=testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-test-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-test:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot:3.4.1=testCompileClasspath,testRuntimeClasspath +org.springframework.data:spring-data-commons:3.4.1=testRuntimeClasspath org.springframework:spring-aop:6.2.1=testCompileClasspath,testRuntimeClasspath org.springframework:spring-beans:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework:spring-context:6.2.1=testCompileClasspath,testRuntimeClasspath @@ -61,6 +106,8 @@ org.springframework:spring-expression:6.2.1=testCompileClasspath,testRuntimeClas org.springframework:spring-jcl:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework:spring-test:6.2.1=testCompileClasspath,testRuntimeClasspath org.springframework:spring-web:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-webmvc:6.2.1=testRuntimeClasspath +org.testcontainers:testcontainers:1.20.4=testRuntimeClasspath org.xmlunit:xmlunit-core:2.10.0=testCompileClasspath,testRuntimeClasspath org.yaml:snakeyaml:2.3=testCompileClasspath,testRuntimeClasspath empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath diff --git a/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirth.java b/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirth.java new file mode 100644 index 000000000..402ca0039 --- /dev/null +++ b/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirth.java @@ -0,0 +1,31 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.validation.constraints; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {DateOfBirthValidator.class}) +public @interface DateOfBirth { + + String message() default "Age must be between {minAgeInclusive} and {maxAgeInclusive} years"; + + int minAgeInclusive() default -1; + + int maxAgeInclusive() default 150; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; +} diff --git a/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirthValidator.java b/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirthValidator.java new file mode 100644 index 000000000..a1a434188 --- /dev/null +++ b/backend/api-commons/src/main/java/de/eshg/validation/constraints/DateOfBirthValidator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.validation.constraints; + +import jakarta.validation.ClockProvider; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.time.Clock; +import java.time.LocalDate; +import java.time.Period; + +public class DateOfBirthValidator implements ConstraintValidator<DateOfBirth, LocalDate> { + + private Period minAge; + private Period maxAge; + + @Override + public void initialize(DateOfBirth dateOfBirthAnnotation) { + minAge = Period.ofYears(dateOfBirthAnnotation.minAgeInclusive()); + maxAge = Period.ofYears(dateOfBirthAnnotation.maxAgeInclusive()); + } + + @Override + public boolean isValid(LocalDate date, ConstraintValidatorContext context) { + if (date == null) { + return true; + } + ClockProvider clockProvider = context.getClockProvider(); + Clock clock = clockProvider.getClock(); + LocalDate today = LocalDate.now(clock); + LocalDate latestAllowedDate = today.minus(minAge); + LocalDate earliestAllowedDate = today.minus(maxAge); + return !latestAllowedDate.isBefore(date) && !date.isBefore(earliestAllowedDate); + } +} diff --git a/backend/api-commons/src/main/java/de/eshg/CustomValidations/EmailAddressConstraint.java b/backend/api-commons/src/main/java/de/eshg/validation/constraints/EmailAddressConstraint.java similarity index 95% rename from backend/api-commons/src/main/java/de/eshg/CustomValidations/EmailAddressConstraint.java rename to backend/api-commons/src/main/java/de/eshg/validation/constraints/EmailAddressConstraint.java index 1c358b787..cda7b9129 100644 --- a/backend/api-commons/src/main/java/de/eshg/CustomValidations/EmailAddressConstraint.java +++ b/backend/api-commons/src/main/java/de/eshg/validation/constraints/EmailAddressConstraint.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package de.eshg.CustomValidations; +package de.eshg.validation.constraints; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/backend/api-commons/src/main/java/de/eshg/CustomValidations/MandatoryEmailAddressConstraint.java b/backend/api-commons/src/main/java/de/eshg/validation/constraints/MandatoryEmailAddressConstraint.java similarity index 93% rename from backend/api-commons/src/main/java/de/eshg/CustomValidations/MandatoryEmailAddressConstraint.java rename to backend/api-commons/src/main/java/de/eshg/validation/constraints/MandatoryEmailAddressConstraint.java index b7358b246..4a3d891f0 100644 --- a/backend/api-commons/src/main/java/de/eshg/CustomValidations/MandatoryEmailAddressConstraint.java +++ b/backend/api-commons/src/main/java/de/eshg/validation/constraints/MandatoryEmailAddressConstraint.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package de.eshg.CustomValidations; +package de.eshg.validation.constraints; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/backend/auditlog-api/gradle.lockfile b/backend/auditlog-api/gradle.lockfile index 76b87c3ef..77ac307ea 100644 --- a/backend/auditlog-api/gradle.lockfile +++ b/backend/auditlog-api/gradle.lockfile @@ -14,10 +14,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,13 +49,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogClientTestHelperApi.java b/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogClientTestHelperApi.java new file mode 100644 index 000000000..a7eef5687 --- /dev/null +++ b/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogClientTestHelperApi.java @@ -0,0 +1,20 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.auditlog; + +import static de.eshg.auditlog.AuditLogClientTestHelperApi.BASE_URL; + +import org.springframework.web.service.annotation.HttpExchange; +import org.springframework.web.service.annotation.PostExchange; + +@HttpExchange(BASE_URL) +public interface AuditLogClientTestHelperApi { + + String BASE_URL = "/test-helper"; + + @PostExchange("archiving-job") + void runAuditLogArchivingJob(); +} diff --git a/backend/auditlog-api/src/main/java/de/eshg/auditlog/SharedAuditLogTestHelperApi.java b/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperApi.java similarity index 64% rename from backend/auditlog-api/src/main/java/de/eshg/auditlog/SharedAuditLogTestHelperApi.java rename to backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperApi.java index c422e0c4d..7f57bf49f 100644 --- a/backend/auditlog-api/src/main/java/de/eshg/auditlog/SharedAuditLogTestHelperApi.java +++ b/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperApi.java @@ -5,21 +5,17 @@ package de.eshg.auditlog; -import static de.eshg.auditlog.SharedAuditLogTestHelperApi.BASE_URL; +import static de.eshg.auditlog.AuditLogClientTestHelperApi.BASE_URL; import java.io.IOException; import org.springframework.web.service.annotation.DeleteExchange; import org.springframework.web.service.annotation.HttpExchange; -import org.springframework.web.service.annotation.PostExchange; @HttpExchange(BASE_URL) -public interface SharedAuditLogTestHelperApi { +public interface AuditLogServiceTestHelperApi { String BASE_URL = "/test-helper"; @DeleteExchange("audit-log-storage") void clearAuditLogStorageDirectory() throws IOException; - - @PostExchange("archiving-job") - void runArchivingJob(); } diff --git a/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogTestHelperApi.java b/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogTestHelperApi.java deleted file mode 100644 index 8db01d03f..000000000 --- a/backend/auditlog-api/src/main/java/de/eshg/auditlog/AuditLogTestHelperApi.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: Apache-2.0 - */ - -package de.eshg.auditlog; - -import static de.eshg.auditlog.SharedAuditLogTestHelperApi.BASE_URL; - -import org.springframework.web.service.annotation.HttpExchange; - -@HttpExchange(BASE_URL) -public interface AuditLogTestHelperApi extends SharedAuditLogTestHelperApi { - - String BASE_URL = "/test-helper"; -} diff --git a/backend/auditlog/build.gradle b/backend/auditlog/build.gradle index fd79517ce..200cc8264 100644 --- a/backend/auditlog/build.gradle +++ b/backend/auditlog/build.gradle @@ -20,6 +20,7 @@ dependencies { testImplementation project(':test-commons') testImplementation testFixtures(project(':business-module-commons')) testImplementation testFixtures(project(':business-module-persistence-commons')) + testImplementation testFixtures(project(':lib-auditlog')) testImplementation 'org.springdoc:springdoc-openapi-starter-common:latest.release' } diff --git a/backend/auditlog/gradle.lockfile b/backend/auditlog/gradle.lockfile index 5a0bee602..ae8bbefda 100644 --- a/backend/auditlog/gradle.lockfile +++ b/backend/auditlog/gradle.lockfile @@ -63,10 +63,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -89,7 +89,7 @@ org.antlr:antlr4-runtime:4.13.0=compileClasspath,productionRuntimeClasspath,runt org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-text:1.12.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.commons:commons-text:1.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.httpcomponents.client5:httpclient5:5.4.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5-h2:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -119,10 +119,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntime org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -141,16 +141,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/auditlog/openApi.json b/backend/auditlog/openApi.json index 7f33ad0a4..32950d94a 100644 --- a/backend/auditlog/openApi.json +++ b/backend/auditlog/openApi.json @@ -302,7 +302,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" diff --git a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java index b51269e5c..df2d3d573 100644 --- a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java +++ b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogController.java @@ -28,6 +28,7 @@ import de.eshg.base.user.api.GetUsersResponse; import de.eshg.base.user.api.UserDto; import de.eshg.base.user.api.UserFilterParameters; import de.eshg.lib.auditlog.AuditLogger; +import de.eshg.rest.service.error.AlreadyExistsException; import de.eshg.rest.service.error.BadRequestException; import de.eshg.rest.service.error.ErrorCode; import de.eshg.rest.service.error.ErrorResponse; @@ -146,6 +147,7 @@ public class AuditLogController implements AuditLogApi, AuditLogArchivingApi { } @Override + @Transactional public ResponseEntity<Resource> readAuditLogFile( String key, ReadAuditLogFileRequest readAuditLogFileRequest) { UserDto selfUser = userApi.getSelfUser(); @@ -784,7 +786,7 @@ public class AuditLogController implements AuditLogApi, AuditLogArchivingApi { private void throwBadRequestExceptionBecauseFileAlreadyExists( AddAuditLogFileRequest addAuditLogFileRequest) { - throw new BadRequestException( + throw new AlreadyExistsException( "Audit log for %s of %s already exists" .formatted( addAuditLogFileRequest.date().format(DateTimeFormatter.ISO_LOCAL_DATE), diff --git a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogInternalSecurityConfiguration.java b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogInternalSecurityConfiguration.java index 62ec05c5e..226af543d 100644 --- a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogInternalSecurityConfiguration.java +++ b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogInternalSecurityConfiguration.java @@ -39,6 +39,7 @@ public class AuditLogInternalSecurityConfiguration { @ConditionalOnTestHelperEnabled public AuthorizationCustomizer testHelperAuthorizationCustomizer() { return auth -> - auth.requestMatchers(HttpMethod.DELETE, AuditLogTestHelperApi.BASE_URL + "/**").permitAll(); + auth.requestMatchers(HttpMethod.DELETE, AuditLogServiceTestHelperApi.BASE_URL + "/**") + .permitAll(); } } diff --git a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogTestHelperController.java b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperController.java similarity index 59% rename from backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogTestHelperController.java rename to backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperController.java index b85d403eb..6d5646551 100644 --- a/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogTestHelperController.java +++ b/backend/auditlog/src/main/java/de/eshg/auditlog/AuditLogServiceTestHelperController.java @@ -6,9 +6,10 @@ package de.eshg.auditlog; import de.eshg.lib.auditlog.AuditLogArchiving; -import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; -import io.swagger.v3.oas.annotations.tags.Tag; +import de.eshg.testhelper.TestHelperController; +import de.eshg.testhelper.TestHelperWithDatabaseService; +import de.eshg.testhelper.environment.EnvironmentConfig; import java.io.IOException; import java.nio.file.Files; import org.apache.commons.io.FileUtils; @@ -18,22 +19,23 @@ import org.springframework.web.bind.annotation.RestController; @RestController @ConditionalOnTestHelperEnabled -@Tag(name = "TestHelper") -public class AuditLogTestHelperController implements AuditLogTestHelperApi { +public class AuditLogServiceTestHelperController extends TestHelperController + implements AuditLogServiceTestHelperApi, AuditLogClientTestHelperApi { - private static final Logger log = LoggerFactory.getLogger(AuditLogTestHelperController.class); + private static final Logger log = + LoggerFactory.getLogger(AuditLogServiceTestHelperController.class); private final AuditLogArchiving auditLogArchiving; private final AuditLogServiceConfig auditLogServiceConfig; - private final AuditLogTestHelperService auditLogTestHelperService; - public AuditLogTestHelperController( + public AuditLogServiceTestHelperController( + TestHelperWithDatabaseService testHelperWithDatabaseService, + EnvironmentConfig environmentConfig, AuditLogArchiving auditLogArchiving, - AuditLogServiceConfig auditLogServiceConfig, - AuditLogTestHelperService auditLogTestHelperService) { + AuditLogServiceConfig auditLogServiceConfig) { + super(testHelperWithDatabaseService, environmentConfig); this.auditLogArchiving = auditLogArchiving; this.auditLogServiceConfig = auditLogServiceConfig; - this.auditLogTestHelperService = auditLogTestHelperService; } @Override @@ -42,11 +44,10 @@ public class AuditLogTestHelperController implements AuditLogTestHelperApi { if (Files.exists(auditLogServiceConfig.getLogStorageDir())) { FileUtils.cleanDirectory(auditLogServiceConfig.getLogStorageDir().toFile()); } - auditLogTestHelperService.clearAuditLogStorageDirectory(); } @Override - public void runArchivingJob() { + public void runAuditLogArchivingJob() { auditLogArchiving.runArchivingJob(); } } diff --git a/backend/auditlog/src/main/resources/migrations/0008_add_auditlog_entry.xml b/backend/auditlog/src/main/resources/migrations/0008_add_auditlog_entry.xml new file mode 100644 index 000000000..e739070ad --- /dev/null +++ b/backend/auditlog/src/main/resources/migrations/0008_add_auditlog_entry.xml @@ -0,0 +1,54 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> diff --git a/backend/auditlog/src/main/resources/migrations/changelog.xml b/backend/auditlog/src/main/resources/migrations/changelog.xml index 2a4da4847..c1b070c84 100644 --- a/backend/auditlog/src/main/resources/migrations/changelog.xml +++ b/backend/auditlog/src/main/resources/migrations/changelog.xml @@ -15,5 +15,6 @@ <include file="migrations/0005_drop_default_revision_entity_created_by_not_null_contraint.xml"/> <include file="migrations/0006_add_dental_as_audit_log_source.xml"/> <include file="migrations/0007_add_official_medical_service_audit_log_source.xml"/> + <include file="migrations/0008_add_auditlog_entry.xml"/> </databaseChangeLog> diff --git a/backend/auth/gradle.lockfile b/backend/auth/gradle.lockfile index 6bb1340a1..ab5aa9220 100644 --- a/backend/auth/gradle.lockfile +++ b/backend/auth/gradle.lockfile @@ -57,10 +57,10 @@ io.netty:netty-resolver:4.1.116.Final=compileClasspath,productionRuntimeClasspat io.netty:netty-transport-native-unix-common:4.1.116.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-transport:4.1.116.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.projectreactor:reactor-core:3.7.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -96,10 +96,10 @@ org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.jsoup:jsoup:1.18.3=testCompileClasspath,testRuntimeClasspath @@ -116,16 +116,17 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=compileClasspath,jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/auth/src/main/java/de/eshg/security/auth/AuthProperties.java b/backend/auth/src/main/java/de/eshg/security/auth/AuthProperties.java index d4906afe8..88b2ada48 100644 --- a/backend/auth/src/main/java/de/eshg/security/auth/AuthProperties.java +++ b/backend/auth/src/main/java/de/eshg/security/auth/AuthProperties.java @@ -5,12 +5,14 @@ package de.eshg.security.auth; +import de.eshg.security.auth.login.AccessCodeLoginType; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import java.net.URI; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,8 +39,8 @@ public record AuthProperties( } } - public List<String> getAccessCodeUrlPatterns() { - return auth().accessCodeUrlPatterns(); + public Map<AccessCodeLoginType, List<String>> getAccessCodeLoginProperties() { + return Optional.ofNullable(auth().accessCodeUrlPatterns()).orElseGet(Map::of); } public List<String> getMukUrlPatterns() { @@ -55,7 +57,7 @@ public record AuthProperties( record Auth( List<String> languagePathPrefixes, - List<String> accessCodeUrlPatterns, + Map<AccessCodeLoginType, @NotEmpty List<String>> accessCodeUrlPatterns, List<String> mukUrlPatterns, List<String> bundIdUrlPatterns, @Valid UserAgentFilter userAgentFilter) {} diff --git a/backend/auth/src/main/java/de/eshg/security/auth/UserAgentFilter.java b/backend/auth/src/main/java/de/eshg/security/auth/UserAgentFilter.java index 0d8ba26ba..a5c1eb62e 100644 --- a/backend/auth/src/main/java/de/eshg/security/auth/UserAgentFilter.java +++ b/backend/auth/src/main/java/de/eshg/security/auth/UserAgentFilter.java @@ -20,7 +20,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; -import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; @@ -30,7 +29,6 @@ public class UserAgentFilter extends OncePerRequestFilter { private final AuthProperties authProperties; private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - private final AntPathMatcher antPathMatcher = new AntPathMatcher(); public UserAgentFilter(AuthProperties authProperties) { this.authProperties = authProperties; diff --git a/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginMethod.java b/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginMethod.java index 4ad9baa29..3b722f3b6 100644 --- a/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginMethod.java +++ b/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginMethod.java @@ -8,6 +8,7 @@ package de.eshg.security.auth.login; import com.google.common.annotations.VisibleForTesting; import de.cronn.commons.lang.StreamUtil; import de.eshg.security.auth.AuthProperties; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -18,7 +19,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; @Component -public final class AccessCodeLoginMethod extends LoginMethod { +public class AccessCodeLoginMethod extends LoginMethod { private static final Logger log = LoggerFactory.getLogger(AccessCodeLoginMethod.class); public static final String ACCESS_CODE_QUERY_PARAMETER = "access_code"; @@ -29,13 +30,21 @@ public final class AccessCodeLoginMethod extends LoginMethod { } @Override - public List<String> getPathPatterns() { - return authProperties.getAccessCodeUrlPatterns(); + protected List<String> getPathPatterns() { + return authProperties.getAccessCodeLoginProperties().values().stream() + .flatMap(Collection::stream) + .toList(); } @Override protected void applyParameters(Map<String, Object> params, String redirectUrl) { - params.put("prompt", "access_code"); + AccessCodeLoginType accessCodeLoginType = getAccessCodeLoginType(redirectUrl); + params.put("prompt", determineCredentialProviderId(accessCodeLoginType)); + + // See de.eshg.keycloak.authenticator.DateOfBirthAccessCodeForm + determineContextInfoKey(accessCodeLoginType) + .ifPresent(contextInfoKey -> params.put("context_info", contextInfoKey)); + getAndValidateAccessCode(redirectUrl) .ifPresent( validAccessCode -> { @@ -44,6 +53,29 @@ public final class AccessCodeLoginMethod extends LoginMethod { }); } + private AccessCodeLoginType getAccessCodeLoginType(String redirectUrl) { + return authProperties.getAccessCodeLoginProperties().entrySet().stream() + .filter(entry -> isApplicable(redirectUrl, entry.getValue())) + .map(Map.Entry::getKey) + .collect(StreamUtil.toSingleElement()); + } + + private static String determineCredentialProviderId(AccessCodeLoginType variant) { + return switch (variant) { + case SCHOOL_ENTRY, TRAVEL_MEDICINE -> "date-of-birth"; // DateOfBirthCredentialProvider + case STI_PROTECTION -> "pin"; // PinCredentialProvider + }; + } + + private static Optional<String> determineContextInfoKey(AccessCodeLoginType variant) { + return Optional.ofNullable( + switch (variant) { + case SCHOOL_ENTRY -> "esu"; + case TRAVEL_MEDICINE -> "tm"; + case STI_PROTECTION -> null; + }); + } + private static Optional<String> getAndValidateAccessCode(String url) { return UriComponentsBuilder.fromUriString(url) .build() diff --git a/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginType.java b/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginType.java new file mode 100644 index 000000000..4241be594 --- /dev/null +++ b/backend/auth/src/main/java/de/eshg/security/auth/login/AccessCodeLoginType.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.security.auth.login; + +public enum AccessCodeLoginType { + SCHOOL_ENTRY, + TRAVEL_MEDICINE, + STI_PROTECTION +} diff --git a/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java b/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java index be7dc0453..6b8e15239 100644 --- a/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java +++ b/backend/auth/src/main/java/de/eshg/security/auth/login/LoginMethod.java @@ -42,7 +42,10 @@ public abstract class LoginMethod { } public final boolean isApplicable(String url) { - List<String> patterns = getPathPatterns(); + return isApplicable(url, getPathPatterns()); + } + + protected final boolean isApplicable(String url, List<String> patterns) { if (patterns == null) { return false; } diff --git a/backend/auth/src/main/resources/application-citizen-portal.properties b/backend/auth/src/main/resources/application-citizen-portal.properties index 44fb9e532..5616d82c7 100644 --- a/backend/auth/src/main/resources/application-citizen-portal.properties +++ b/backend/auth/src/main/resources/application-citizen-portal.properties @@ -5,6 +5,10 @@ eshg.reverse-proxy.url=http://localhost:4001 spring.security.oauth2.client.registration.keycloak.client-secret=tstj3RgLtqF4Kbh3hVNRRXTwxLkhmq eshg.auth.language-path-prefixes=/de, /en -eshg.auth.access-code-url-patterns=/einschulungsuntersuchung/termin, /impfberatung/meine-termine + +eshg.auth.access-code-url-patterns[SCHOOL_ENTRY]=/einschulungsuntersuchung/termin +eshg.auth.access-code-url-patterns[TRAVEL_MEDICINE]=/impfberatung/meine-termine +eshg.auth.access-code-url-patterns[STI_PROTECTION]=/sexuellegesundheit/hiv-sti-beratung/termin + eshg.auth.muk-url-patterns=/unternehmen/** eshg.auth.bund-id-url-patterns=/mein-bereich/** diff --git a/backend/base-api/gradle.lockfile b/backend/base-api/gradle.lockfile index 4c898172a..481c9ef95 100644 --- a/backend/base-api/gradle.lockfile +++ b/backend/base-api/gradle.lockfile @@ -14,10 +14,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,13 +49,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateRequest.java index 7de4e9992..9db3b3ea5 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateRequest.java @@ -5,9 +5,9 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateResponse.java index 3a5b82ba1..bd8602dc1 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/AddFacilityFileStateResponse.java @@ -5,9 +5,9 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/ExternalAddFacilityFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/ExternalAddFacilityFileStateRequest.java index 34f33c9f2..3a17fa409 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/ExternalAddFacilityFileStateRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/ExternalAddFacilityFileStateRequest.java @@ -5,8 +5,8 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/FacilityDetailsDto.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/FacilityDetailsDto.java index bce133826..1fdf5e3e2 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/FacilityDetailsDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/FacilityDetailsDto.java @@ -5,8 +5,8 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetFacilityFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetFacilityFileStateResponse.java index bbf6bb0a9..1be53b17f 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetFacilityFileStateResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetFacilityFileStateResponse.java @@ -5,9 +5,9 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetReferenceFacilityResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetReferenceFacilityResponse.java index 98e4e8a7b..c5ae67276 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetReferenceFacilityResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/facility/GetReferenceFacilityResponse.java @@ -5,9 +5,9 @@ package de.eshg.base.centralfile.api.facility; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java index 47e312cca..c3a7d7af6 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateRequest.java @@ -5,12 +5,13 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -31,7 +32,7 @@ public record AddPersonFileStateRequest( GenderDto gender, @NotNull @Size(min = 1, max = 80) String firstName, @NotNull @Size(min = 1, max = 120) String lastName, - @NotNull LocalDate dateOfBirth, + @NotNull @DateOfBirth LocalDate dateOfBirth, @Size(min = 1, max = 40) String nameAtBirth, @Size(min = 1, max = 50) String placeOfBirth, CountryCode countryOfBirth, @@ -47,42 +48,42 @@ public record AddPersonFileStateRequest( this(new PersonDetailsDto(firstName, lastName, dateOfBirth), dataOrigin); } - public AddPersonFileStateRequest(PersonDetailsDto personDetailsDto, DataOriginDto dataOrigin) { + public AddPersonFileStateRequest(PersonDetails personDetails, DataOriginDto dataOrigin) { this( null, - personDetailsDto.title(), - personDetailsDto.salutation(), - personDetailsDto.gender(), - personDetailsDto.firstName(), - personDetailsDto.lastName(), - personDetailsDto.dateOfBirth(), - personDetailsDto.nameAtBirth(), - personDetailsDto.placeOfBirth(), - personDetailsDto.countryOfBirth(), - personDetailsDto.emailAddresses(), - personDetailsDto.phoneNumbers(), - personDetailsDto.contactAddress(), - personDetailsDto.differentBillingAddress(), + personDetails.title(), + personDetails.salutation(), + personDetails.gender(), + personDetails.firstName(), + personDetails.lastName(), + personDetails.dateOfBirth(), + personDetails.nameAtBirth(), + personDetails.placeOfBirth(), + personDetails.countryOfBirth(), + personDetails.emailAddresses(), + personDetails.phoneNumbers(), + personDetails.contactAddress(), + personDetails.differentBillingAddress(), dataOrigin); } public AddPersonFileStateRequest( - UUID referencePersonId, PersonDetailsDto personDetailsDto, DataOriginDto dataOrigin) { + UUID referencePersonId, PersonDetails personDetails, DataOriginDto dataOrigin) { this( referencePersonId, - personDetailsDto.title(), - personDetailsDto.salutation(), - personDetailsDto.gender(), - personDetailsDto.firstName(), - personDetailsDto.lastName(), - personDetailsDto.dateOfBirth(), - personDetailsDto.nameAtBirth(), - personDetailsDto.placeOfBirth(), - personDetailsDto.countryOfBirth(), - personDetailsDto.emailAddresses(), - personDetailsDto.phoneNumbers(), - personDetailsDto.contactAddress(), - personDetailsDto.differentBillingAddress(), + personDetails.title(), + personDetails.salutation(), + personDetails.gender(), + personDetails.firstName(), + personDetails.lastName(), + personDetails.dateOfBirth(), + personDetails.nameAtBirth(), + personDetails.placeOfBirth(), + personDetails.countryOfBirth(), + personDetails.emailAddresses(), + personDetails.phoneNumbers(), + personDetails.contactAddress(), + personDetails.differentBillingAddress(), dataOrigin); } } diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java index 582f68297..2cd8575c2 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/AddPersonFileStateResponse.java @@ -5,12 +5,12 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java index e2c93e9c5..ea3a31493 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/ExternalAddPersonFileStateRequest.java @@ -5,11 +5,12 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -24,7 +25,7 @@ public record ExternalAddPersonFileStateRequest( GenderDto gender, @NotNull @Size(min = 1, max = 80) String firstName, @NotNull @Size(min = 1, max = 120) String lastName, - @NotNull LocalDate dateOfBirth, + @NotNull @DateOfBirth LocalDate dateOfBirth, @Size(min = 1, max = 40) String nameAtBirth, @Size(min = 1, max = 50) String placeOfBirth, CountryCode countryOfBirth, @@ -34,20 +35,20 @@ public record ExternalAddPersonFileStateRequest( @Valid AddressDto differentBillingAddress) implements PersonDetails { - public ExternalAddPersonFileStateRequest(PersonDetailsDto personDetailsDto) { + public ExternalAddPersonFileStateRequest(PersonDetails personDetails) { this( - personDetailsDto.title(), - personDetailsDto.salutation(), - personDetailsDto.gender(), - personDetailsDto.firstName(), - personDetailsDto.lastName(), - personDetailsDto.dateOfBirth(), - personDetailsDto.nameAtBirth(), - personDetailsDto.placeOfBirth(), - personDetailsDto.countryOfBirth(), - personDetailsDto.emailAddresses(), - personDetailsDto.phoneNumbers(), - personDetailsDto.contactAddress(), - personDetailsDto.differentBillingAddress()); + personDetails.title(), + personDetails.salutation(), + personDetails.gender(), + personDetails.firstName(), + personDetails.lastName(), + personDetails.dateOfBirth(), + personDetails.nameAtBirth(), + personDetails.placeOfBirth(), + personDetails.countryOfBirth(), + personDetails.emailAddresses(), + personDetails.phoneNumbers(), + personDetails.contactAddress(), + personDetails.differentBillingAddress()); } } diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java index 26ce7cf59..133a75c4e 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetPersonFileStateResponse.java @@ -5,12 +5,12 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java index 0957e9948..9fa7bac70 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/GetReferencePersonResponse.java @@ -5,12 +5,12 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.DataOriginDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java index 4bf62afd0..1dea796b7 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/PersonDetailsDto.java @@ -5,11 +5,11 @@ package de.eshg.base.centralfile.api.person; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonInBulkRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonInBulkRequest.java index 3fddd966e..809cfbc00 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonInBulkRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonInBulkRequest.java @@ -16,4 +16,8 @@ public record UpdatePersonInBulkRequest( example = "be9831d4-dc25-48d8-9bfe-4c0b54bfb2c1") @NotNull UUID fileStateId, - @NotNull @Valid PersonDetailsDto updatedPerson) {} + @NotNull @Valid UpdatePersonRequest updatedPerson) { + public UpdatePersonInBulkRequest(UUID fileStateId, PersonDetails personDetails) { + this(fileStateId, new UpdatePersonRequest(personDetails)); + } +} diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonRequest.java index 767c13338..c5a3b965d 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdatePersonRequest.java @@ -5,11 +5,52 @@ package de.eshg.base.centralfile.api.person; +import de.eshg.base.GenderDto; +import de.eshg.base.SalutationDto; +import de.eshg.base.address.AddressDto; +import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import java.util.List; @Schema( description = "Request used for performing a consistent update of a person file state and its associated reference person") -public record UpdatePersonRequest(@NotNull @Valid PersonDetailsDto updatedPerson) {} +public record UpdatePersonRequest( + @Size(min = 1, max = 119) String title, + SalutationDto salutation, + GenderDto gender, + @NotNull @Size(min = 1, max = 80) String firstName, + @NotNull @Size(min = 1, max = 120) String lastName, + @NotNull @DateOfBirth LocalDate dateOfBirth, + @Size(min = 1, max = 40) String nameAtBirth, + @Size(min = 1, max = 50) String placeOfBirth, + CountryCode countryOfBirth, + List<@MandatoryEmailAddressConstraint String> emailAddresses, + List<@NotNull @Size(min = 1, max = 23) String> phoneNumbers, + @Valid AddressDto contactAddress, + @Valid AddressDto differentBillingAddress) + implements PersonDetails { + + public UpdatePersonRequest(PersonDetails personDetails) { + this( + personDetails.title(), + personDetails.salutation(), + personDetails.gender(), + personDetails.firstName(), + personDetails.lastName(), + personDetails.dateOfBirth(), + personDetails.nameAtBirth(), + personDetails.placeOfBirth(), + personDetails.countryOfBirth(), + personDetails.emailAddresses(), + personDetails.phoneNumbers(), + personDetails.contactAddress(), + personDetails.differentBillingAddress()); + } +} diff --git a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdateReferencePersonRequest.java b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdateReferencePersonRequest.java index 2f9239377..8ebf3dec5 100644 --- a/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdateReferencePersonRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/centralfile/api/person/UpdateReferencePersonRequest.java @@ -16,4 +16,8 @@ import jakarta.validation.constraints.NotNull; and the expected current version number. """) public record UpdateReferencePersonRequest( - @NotNull @Valid PersonDetailsDto personDetails, @NotNull long version) {} + @NotNull @Valid UpdatePersonRequest personDetails, @NotNull long version) { + public UpdateReferencePersonRequest(PersonDetails personDetails, long version) { + this(new UpdatePersonRequest(personDetails), version); + } +} diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/AddInstitutionContactRequest.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/AddInstitutionContactRequest.java index 2d6b7aed2..d83e1ee37 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/AddInstitutionContactRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/AddInstitutionContactRequest.java @@ -5,8 +5,8 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/AddPersonContactRequest.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/AddPersonContactRequest.java index d525035f7..130de3e51 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/AddPersonContactRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/AddPersonContactRequest.java @@ -5,10 +5,10 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/InstitutionContactDto.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/InstitutionContactDto.java index e76781828..a90523986 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/InstitutionContactDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/InstitutionContactDto.java @@ -5,8 +5,8 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/PersonContactDto.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/PersonContactDto.java index 93f118431..08d2ee8ff 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/PersonContactDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/PersonContactDto.java @@ -5,10 +5,10 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdateInstitutionContactRequest.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdateInstitutionContactRequest.java index 1734d091d..6c96d5534 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdateInstitutionContactRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdateInstitutionContactRequest.java @@ -5,8 +5,8 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; diff --git a/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdatePersonContactRequest.java b/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdatePersonContactRequest.java index 079f5fc4c..8bf1d326b 100644 --- a/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdatePersonContactRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/contact/api/UpdatePersonContactRequest.java @@ -5,10 +5,10 @@ package de.eshg.base.contact.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java index 6eb9ef77a..3edf26f00 100644 --- a/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java +++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/GdprProcedureApi.java @@ -11,6 +11,7 @@ import de.eshg.base.gdpr.api.AddGdprDownloadsRequest; import de.eshg.base.gdpr.api.AddGdprProcedureFromCitizenPortalRequest; import de.eshg.base.gdpr.api.AddGdprProcedureRequest; import de.eshg.base.gdpr.api.CancelGdprProcedureRequest; +import de.eshg.base.gdpr.api.CitizenUsersGdprProcedureDto; import de.eshg.base.gdpr.api.CloseGdprProcedureRequest; import de.eshg.base.gdpr.api.DeleteGdprDownloadsRequest; import de.eshg.base.gdpr.api.GdprProcedureFilterParameters; @@ -42,6 +43,7 @@ public interface GdprProcedureApi { String SELF_LINKED_GDPR_PROCEDURES = "/self/linked-gdpr-procedures"; String CITIZEN_PORTAL_URL = Base.GDPR_PROCEDURE_CITIZEN_PORTAL_URL; String DOWNLOADS = Base.Gdpr.DOWNLOADS; + String DELETE_DOWNLOADS = Base.Gdpr.DELETE_DOWNLOADS; String FILE_STATE_IDS = Base.Gdpr.FILE_STATE_IDS; String BY_ID = Base.Gdpr.BY_ID; String DETAILS_PAGE = Base.Gdpr.DETAILS_PAGE; @@ -64,10 +66,10 @@ public interface GdprProcedureApi { summary = """ This endpoint allows authenticated MUK and BundID users to initiate a GDPR - procedure via the citizen portal initiate a GDPR procedure. A matter of - concern can be added to the request if desired. + procedure via the citizen portal. A matter of concern can be added to the + request if desired. """) - GetGdprProcedureResponse addGdprProcedureFromCitizenPortal( + CitizenUsersGdprProcedureDto addGdprProcedureFromCitizenPortal( @RequestBody @Valid AddGdprProcedureFromCitizenPortalRequest request); @GetExchange(CITIZEN_PORTAL_URL + SELF_LINKED_GDPR_PROCEDURES) @@ -163,7 +165,7 @@ public interface GdprProcedureApi { "Get list of download ids of GDPR-related documents or data of this GDPR procedure.") GetGdprDownloadsResponse getDownloads(@PathVariable("id") UUID id); - @DeleteExchange(DOWNLOADS) + @PostExchange(DELETE_DOWNLOADS) @ApiResponse(responseCode = "200") @Operation( summary = diff --git a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureStatusDto.java b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureStatusDto.java index 0961e6db6..5a6d962a0 100644 --- a/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureStatusDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/gdpr/api/GdprProcedureStatusDto.java @@ -10,7 +10,6 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "GdprProcedureStatus", description = "A list of statuses a GDPR procedure can have.") public enum GdprProcedureStatusDto { DRAFT, - OPEN, IN_PROGRESS, CLOSED, ABORTED; diff --git a/backend/base-api/src/main/java/de/eshg/base/statistics/api/BaseAttribute.java b/backend/base-api/src/main/java/de/eshg/base/statistics/api/BaseAttribute.java index e960e27a3..83d746267 100644 --- a/backend/base-api/src/main/java/de/eshg/base/statistics/api/BaseAttribute.java +++ b/backend/base-api/src/main/java/de/eshg/base/statistics/api/BaseAttribute.java @@ -5,6 +5,7 @@ package de.eshg.base.statistics.api; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.ValueOptionInternal; import de.eshg.lib.statistics.api.ValueType; import jakarta.validation.Valid; @@ -19,4 +20,5 @@ public record BaseAttribute( @NotNull ValueType valueType, String unit, @Size(min = 1) @Valid List<ValueOptionInternal> valueOptions, - @NotNull boolean mandatory) {} + @NotNull boolean mandatory, + @NotNull DataPrivacyCategory dataPrivacyCategory) {} 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 8ed731056..c35be37ff 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 @@ -83,6 +83,9 @@ public interface BaseTestHelperApi extends TestHelperApi, LoginProvider { void disableBusinessModule( @PathVariable("businessModuleToDisable") BusinessModule businessModuleToDisable); + @PostExchange("/business-modules/reset") + void resetActivatedBusinessModules(); + @PostExchange("/setup-admin") void createSetupAdmin(@Valid @RequestBody CreateSetupAdminRequest request); @@ -92,6 +95,9 @@ public interface BaseTestHelperApi extends TestHelperApi, LoginProvider { @GetExchange("/idp-user/{nameId}") CitizenUserDto getIdpUser(@PathVariable("nameId") String nameId); - @PostExchange("auditlog-notification-job") + @PostExchange("/auditlog-notification-job") void runAuditlogNotificationJob(); + + @PostExchange("/gdpr-cleanup-job") + void runGdprCleanupJob(); } diff --git a/backend/base-api/src/main/java/de/eshg/base/user/api/AddUserRequest.java b/backend/base-api/src/main/java/de/eshg/base/user/api/AddUserRequest.java index 7d3bd9a21..fb5fc7cee 100644 --- a/backend/base-api/src/main/java/de/eshg/base/user/api/AddUserRequest.java +++ b/backend/base-api/src/main/java/de/eshg/base/user/api/AddUserRequest.java @@ -5,8 +5,8 @@ package de.eshg.base.user.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.SalutationDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/backend/base-api/src/main/java/de/eshg/base/user/api/UserDto.java b/backend/base-api/src/main/java/de/eshg/base/user/api/UserDto.java index db63a97fc..0947d8ed6 100644 --- a/backend/base-api/src/main/java/de/eshg/base/user/api/UserDto.java +++ b/backend/base-api/src/main/java/de/eshg/base/user/api/UserDto.java @@ -5,7 +5,7 @@ package de.eshg.base.user.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; diff --git a/backend/base/build.gradle b/backend/base/build.gradle index a41040d87..96fb03446 100644 --- a/backend/base/build.gradle +++ b/backend/base/build.gradle @@ -48,6 +48,7 @@ dependencies { testImplementation testFixtures(project(':lib-service-directory-admin-api')) testImplementation testFixtures(project(":base-api")) testImplementation testFixtures(project(':lib-document-generator')) + testImplementation testFixtures(project(':lib-auditlog')) testImplementation 'org.apache.commons:commons-csv:latest.release' testImplementation 'com.tngtech.archunit:archunit-junit5:latest.release' diff --git a/backend/base/gradle.lockfile b/backend/base/gradle.lockfile index d55933112..c6b27207d 100644 --- a/backend/base/gradle.lockfile +++ b/backend/base/gradle.lockfile @@ -88,12 +88,12 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.quarkus:quarkus-junit4-mock:3.17.7=testCompileClasspath,testRuntimeClasspath +io.quarkus:quarkus-junit4-mock:3.18.0=testCompileClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.swagger:swagger-annotations:1.6.15=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -198,10 +198,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -230,24 +230,25 @@ org.keycloak:keycloak-admin-client:26.0.3=compileClasspath,productionRuntimeClas org.keycloak:keycloak-client-common-synced:26.0.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.liquibase:liquibase-core:4.29.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.mnode.ical4j:ical4j:4.0.8=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.mnode.ical4j:ical4j:4.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/base/openApi.json b/backend/base/openApi.json index ebcab2c95..af097e103 100644 --- a/backend/base/openApi.json +++ b/backend/base/openApi.json @@ -1893,14 +1893,14 @@ "content" : { "*/*" : { "schema" : { - "$ref" : "#/components/schemas/GetGdprProcedureResponse" + "$ref" : "#/components/schemas/CitizenUsersGdprProcedure" } } }, "description" : "OK" } }, - "summary" : "This endpoint allows authenticated MUK and BundID users to initiate a GDPR\nprocedure via the citizen portal initiate a GDPR procedure. A matter of\nconcern can be added to the request if desired.\n", + "summary" : "This endpoint allows authenticated MUK and BundID users to initiate a GDPR\nprocedure via the citizen portal. A matter of concern can be added to the\nrequest if desired.\n", "tags" : [ "GdprProcedure" ] } }, @@ -2080,11 +2080,10 @@ "tags" : [ "GdprProcedure" ] } }, - "/gdpr-procedures/{id}/details-page" : { - "get" : { - "operationId" : "getGdprProcedureDetailsPage", + "/gdpr-procedures/{id}/delete-downloads" : { + "post" : { + "operationId" : "deleteDownloads", "parameters" : [ { - "description" : "The Id of the GDPR procedure.", "in" : "path", "name" : "id", "required" : true, @@ -2093,26 +2092,30 @@ "format" : "uuid" } } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DeleteGdprDownloadsRequest" + } + } + }, + "required" : true + }, "responses" : { "200" : { - "content" : { - "*/*" : { - "schema" : { - "$ref" : "#/components/schemas/GetGdprProcedureDetailsPageResponse" - } - } - }, "description" : "OK" } }, - "summary" : "Get GDPR procedure details page information. Used exclusively by the frontend to display the GDPR Procedure.", + "summary" : "Delete one or multiple downloads of GDPR-related document or data of this GDPR procedure.", "tags" : [ "GdprProcedure" ] } }, - "/gdpr-procedures/{id}/downloads" : { - "delete" : { - "operationId" : "deleteDownloads", + "/gdpr-procedures/{id}/details-page" : { + "get" : { + "operationId" : "getGdprProcedureDetailsPage", "parameters" : [ { + "description" : "The Id of the GDPR procedure.", "in" : "path", "name" : "id", "required" : true, @@ -2121,24 +2124,23 @@ "format" : "uuid" } } ], - "requestBody" : { - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/DeleteGdprDownloadsRequest" - } - } - }, - "required" : true - }, "responses" : { "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/GetGdprProcedureDetailsPageResponse" + } + } + }, "description" : "OK" } }, - "summary" : "Delete one or multiple downloads of GDPR-related document or data of this GDPR procedure.", + "summary" : "Get GDPR procedure details page information. Used exclusively by the frontend to display the GDPR Procedure.", "tags" : [ "GdprProcedure" ] - }, + } + }, + "/gdpr-procedures/{id}/downloads" : { "get" : { "operationId" : "getDownloads", "parameters" : [ { @@ -4277,7 +4279,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -4286,9 +4288,9 @@ "tags" : [ "TestHelper" ] } }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "/test-helper/auditlog-notification-job" : { + "post" : { + "operationId" : "runAuditlogNotificationJob", "responses" : { "200" : { "description" : "OK" @@ -4297,9 +4299,9 @@ "tags" : [ "TestHelper" ] } }, - "/test-helper/auditlog-notification-job" : { + "/test-helper/business-modules/reset" : { "post" : { - "operationId" : "runAuditlogNotificationJob", + "operationId" : "resetActivatedBusinessModules", "responses" : { "200" : { "description" : "OK" @@ -4393,6 +4395,17 @@ "tags" : [ "TestHelper" ] } }, + "/test-helper/gdpr-cleanup-job" : { + "post" : { + "operationId" : "runGdprCleanupJob", + "responses" : { + "200" : { + "description" : "OK" + } + }, + "tags" : [ "TestHelper" ] + } + }, "/test-helper/idp-user/{nameId}" : { "get" : { "operationId" : "getIdpUser", @@ -7520,7 +7533,7 @@ "GdprProcedureStatus" : { "type" : "string", "description" : "A list of statuses a GDPR procedure can have.", - "enum" : [ "DRAFT", "OPEN", "IN_PROGRESS", "CLOSED", "ABORTED" ] + "enum" : [ "DRAFT", "IN_PROGRESS", "CLOSED", "ABORTED" ] }, "GdprProcedureType" : { "type" : "string", @@ -11023,7 +11036,7 @@ "example" : "UUID_2" }, "updatedPerson" : { - "$ref" : "#/components/schemas/PersonDetails" + "$ref" : "#/components/schemas/UpdatePersonRequest" } } }, @@ -11046,11 +11059,90 @@ } }, "UpdatePersonRequest" : { - "required" : [ "updatedPerson" ], + "required" : [ "dateOfBirth", "firstName", "lastName" ], "type" : "object", "properties" : { - "updatedPerson" : { - "$ref" : "#/components/schemas/PersonDetails" + "contactAddress" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/DomesticAddress" + }, { + "$ref" : "#/components/schemas/PostboxAddress" + } ] + }, + "countryOfBirth" : { + "$ref" : "#/components/schemas/CountryCode" + }, + "dateOfBirth" : { + "type" : "string", + "description" : "The date of birth of the Person.", + "format" : "date", + "example" : "2000-01-01" + }, + "differentBillingAddress" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/DomesticAddress" + }, { + "$ref" : "#/components/schemas/PostboxAddress" + } ] + }, + "emailAddresses" : { + "type" : "array", + "description" : "A list of email addresses of the Person.", + "example" : "['mail1@address.de','mail2@address.de','mail3@address.de']", + "items" : { + "type" : "string" + } + }, + "firstName" : { + "maxLength" : 80, + "minLength" : 1, + "type" : "string", + "description" : "The given name(s) of the Person.", + "example" : "John" + }, + "gender" : { + "$ref" : "#/components/schemas/Gender" + }, + "lastName" : { + "maxLength" : 120, + "minLength" : 1, + "type" : "string", + "description" : "The last name of the Person.", + "example" : "Doe" + }, + "nameAtBirth" : { + "maxLength" : 40, + "minLength" : 1, + "type" : "string", + "description" : "The last name at birth of the Person.", + "example" : "Smith" + }, + "phoneNumbers" : { + "type" : "array", + "description" : "A list of telephone numbers of the Person.", + "example" : "['+4912345678901','+4912345678902','+4912345678903']", + "items" : { + "maxLength" : 23, + "minLength" : 1, + "type" : "string" + } + }, + "placeOfBirth" : { + "maxLength" : 50, + "minLength" : 1, + "type" : "string", + "description" : "The place of birth (without country) of the Person.", + "example" : "Berlin" + }, + "salutation" : { + "$ref" : "#/components/schemas/Salutation" + }, + "title" : { + "maxLength" : 119, + "minLength" : 1, + "type" : "string", + "description" : "The academic title of a Person.", + "example" : "Prof. Dr." } }, "description" : "Request used for performing a consistent update of a person file state and its associated reference person" @@ -11110,7 +11202,7 @@ "type" : "object", "properties" : { "personDetails" : { - "$ref" : "#/components/schemas/PersonDetails" + "$ref" : "#/components/schemas/UpdatePersonRequest" }, "version" : { "type" : "integer", diff --git a/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java b/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java index cb6b604e5..b24c8c9a3 100644 --- a/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java +++ b/backend/base/src/main/java/de/eshg/base/centralfile/mapper/PersonMapper.java @@ -190,7 +190,7 @@ public class PersonMapper { } public static Person mapPersonToDm(UpdatePersonRequest request) { - return mapPersonDetailsToDm(request.updatedPerson()); + return mapPersonDetailsToDm(request); } public static Person mapPersonToDm(UpdatePersonInBulkRequest request) { diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java index 8bf5f1cf9..2f04fb169 100644 --- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java +++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureController.java @@ -95,7 +95,7 @@ public class GdprProcedureController implements GdprProcedureApi { @Override @Transactional - public GetGdprProcedureResponse addGdprProcedureFromCitizenPortal( + public CitizenUsersGdprProcedureDto addGdprProcedureFromCitizenPortal( AddGdprProcedureFromCitizenPortalRequest request) { baseFeatureToggle.assertNewFeatureIsEnabled(BaseFeature.GDPR_ONLINE_PORTAL); @@ -105,7 +105,7 @@ public class GdprProcedureController implements GdprProcedureApi { procedure.setIdentificationData(identificationData); GdprProcedure saved = service.addFromCitizenPortal(procedure); - return mapGdprProcedureToApi(saved); + return GdprProcedureMapper.mapProcedureToCitizenApi(saved); } @Override @@ -216,6 +216,7 @@ public class GdprProcedureController implements GdprProcedureApi { public GetGdprProcedureResponse addCentralFileIdToGdprProcedure( UUID id, AddCentralFileIdToGdprProcedureRequest request) { baseFeatureToggle.assertNewFeatureIsEnabled(BaseFeature.GDPR); + List<CentralFileIdWrapper> centralFileIds = mapToDm(request.centralFileIds().stream().distinct().toList()); return mapGdprProcedureToApi( diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureMapper.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureMapper.java index d263c95b4..d9c00252e 100644 --- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureMapper.java +++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureMapper.java @@ -144,7 +144,6 @@ public class GdprProcedureMapper { public static GdprProcedureStatusDto mapToApi(GdprProcedureStatus status) { return switch (status) { case DRAFT -> GdprProcedureStatusDto.DRAFT; - case OPEN -> GdprProcedureStatusDto.OPEN; case CLOSED -> GdprProcedureStatusDto.CLOSED; case ABORTED -> GdprProcedureStatusDto.ABORTED; case IN_PROGRESS -> GdprProcedureStatusDto.IN_PROGRESS; @@ -260,7 +259,7 @@ public class GdprProcedureMapper { return new GetCitizenSelfUsersGdprProceduresResponse(responses); } - private static CitizenUsersGdprProcedureDto mapProcedureToCitizenApi(GdprProcedure procedure) { + static CitizenUsersGdprProcedureDto mapProcedureToCitizenApi(GdprProcedure procedure) { return new CitizenUsersGdprProcedureDto( procedure.getExternalId(), GdprProcedureMapper.mapToApi(procedure.getType()), diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java index 0826b792b..d9bc9c190 100644 --- a/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java +++ b/backend/base/src/main/java/de/eshg/base/gdpr/GdprProcedureService.java @@ -278,8 +278,9 @@ public class GdprProcedureService { public GdprProcedure addCentralFileIdsToGdprProcedure( List<CentralFileIdWrapper> centralFileIds, UUID gdprProcedureId, long version) { GdprProcedure procedure = getGdprProcedureForUpdate(gdprProcedureId); - ValidationUtil.validateVersion(version, procedure); + ValidationUtil.validateVersion(version, procedure); + throwIfProcedureIsNoDraft(procedure); throwIfCentralFileIdAlreadyExists(centralFileIds, procedure); entityManager.lock(procedure, LockModeType.PESSIMISTIC_FORCE_INCREMENT); @@ -317,6 +318,13 @@ public class GdprProcedureService { return procedure; } + private static void throwIfProcedureIsNoDraft(GdprProcedure procedure) { + if (procedure.getStatus() != GdprProcedureStatus.DRAFT) { + throw new BadRequestException( + "Cannot add centralFileIds: Gdpr Procedure is not in Draft Status"); + } + } + private static void throwIfCentralFileIdAlreadyExists( List<CentralFileIdWrapper> centralFileIds, GdprProcedure procedure) { List<UUID> existingIds = diff --git a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedureStatus.java b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedureStatus.java index d55c45294..d15d2cea4 100644 --- a/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedureStatus.java +++ b/backend/base/src/main/java/de/eshg/base/gdpr/persistence/GdprProcedureStatus.java @@ -7,7 +7,6 @@ package de.eshg.base.gdpr.persistence; public enum GdprProcedureStatus { DRAFT, - OPEN, IN_PROGRESS, CLOSED, ABORTED; diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakProvisioning.java b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakProvisioning.java index aab71f230..fb04056d5 100644 --- a/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakProvisioning.java +++ b/backend/base/src/main/java/de/eshg/base/keycloak/CitizenKeycloakProvisioning.java @@ -130,7 +130,8 @@ public class CitizenKeycloakProvisioning extends KeycloakProvisioning<CitizenKey builder.setDescription("Citizen Portal Browser Flow"); builder.addAlternativeStep("auth-cookie"); builder.addAlternativeStep("identity-provider-redirector"); - builder.addAlternativeStep("access-code"); + builder.addAlternativeStep("date-of-birth-access-code"); + builder.addAlternativeStep("pin-access-code"); builder.build(keycloakClient); keycloakClient.bindBrowserFlow(PORTAL_BROWSER_FLOW_ALIAS); diff --git a/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java b/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java index 706ea02c1..a87d01ad4 100644 --- a/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java +++ b/backend/base/src/main/java/de/eshg/base/keycloak/ModuleClient.java @@ -44,7 +44,8 @@ public enum ModuleClient { MEDICAL_REGISTRY( "medical-registry", List.of(BASE_MAIL_SEND, BASE_PERSONS_DELETE, BASE_FACILITIES_DELETE)), DENTAL("dental", List.of(BASE_MAIL_SEND, BASE_PERSONS_DELETE, BASE_FACILITIES_DELETE)), - OFFICIAL_MEDICAL_SERVICE("official-medical-service", List.of(BASE_MAIL_SEND)); + OFFICIAL_MEDICAL_SERVICE( + "official-medical-service", List.of(BASE_MAIL_SEND, BASE_ACCESS_CODE_USER_ADMIN)); private final String clientIdWithoutPrefix; private final List<EmployeePermissionRole> roles; diff --git a/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java b/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java index 14ce21425..a3dfd6cc2 100644 --- a/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java +++ b/backend/base/src/main/java/de/eshg/base/spring/config/BaseInternalSecurityConfig.java @@ -6,6 +6,7 @@ package de.eshg.base.spring.config; import static de.eshg.base.gdpr.GdprProcedureApi.BY_ID; +import static de.eshg.base.gdpr.GdprProcedureApi.DELETE_DOWNLOADS; import static de.eshg.base.gdpr.GdprProcedureApi.DOWNLOADS; import static de.eshg.base.gdpr.GdprProcedureApi.FILE_STATE_IDS; import static org.springframework.http.HttpMethod.DELETE; @@ -87,7 +88,7 @@ public class BaseInternalSecurityConfig { auth.requestMatchers(POST, GdprProcedureApi.BASE_URL + DOWNLOADS) .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW.name()); - auth.requestMatchers(DELETE, GdprProcedureApi.BASE_URL + DOWNLOADS) + auth.requestMatchers(POST, GdprProcedureApi.BASE_URL + DELETE_DOWNLOADS) .hasRole(EmployeePermissionRole.BASE_GDPR_PROCEDURE_REVIEW.name()); } diff --git a/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java b/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java index c6b3682df..88ed25ffa 100644 --- a/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java +++ b/backend/base/src/main/java/de/eshg/base/statistics/StatisticsController.java @@ -26,6 +26,7 @@ import de.eshg.base.street.SearchStreetResponse; import de.eshg.base.street.StreetController; import de.eshg.base.util.Gender; import de.eshg.lib.common.CountryCode; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.DataRow; import de.eshg.lib.statistics.api.ValueType; import de.eshg.rest.service.error.BadRequestException; @@ -86,7 +87,8 @@ public class StatisticsController implements BaseStatisticsApi { commonAttribute.getType(), null, commonAttribute.getValueOptions(), - commonAttribute.isMandatory()); + commonAttribute.isMandatory(), + DataPrivacyCategory.QUASI_IDENTIFYING); } @Override @@ -158,7 +160,14 @@ public class StatisticsController implements BaseStatisticsApi { ValueType valueType = mapToValueType(subjectType); List<BaseAttribute> attributes = new ArrayList<>(); attributes.add( - new BaseAttribute(valueType.name(), valueType.name(), valueType, null, null, true)); + new BaseAttribute( + valueType.name(), + valueType.name(), + valueType, + null, + null, + true, + DataPrivacyCategory.QUASI_IDENTIFYING)); baseAttributes.forEach(baseAttribute -> attributes.add(mapToAttribute(baseAttribute))); return attributes; } 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 835b4cb27..a3c3a579c 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 @@ -6,10 +6,11 @@ package de.eshg.base.testhelper; import de.cronn.commons.lang.StreamUtil; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.base.contact.api.SearchContactsResponse; import de.eshg.base.feature.BaseFeature; import de.eshg.base.feature.BaseFeatureToggle; +import de.eshg.base.gdpr.GdprCleanupJob; import de.eshg.base.inventory.api.GetInventoryItemsResponse; import de.eshg.base.keycloak.CitizenKeycloakClient; import de.eshg.base.keycloak.MasterKeycloakProvisioning; @@ -35,7 +36,6 @@ import de.eshg.testhelper.api.RealmDto; import de.eshg.testhelper.api.TestHelperLoginAsCitizenAccessCodeUserRequest; import de.eshg.testhelper.api.TestHelperLoginRequest; import de.eshg.testhelper.environment.EnvironmentConfig; -import java.io.IOException; import java.util.List; import java.util.Map; import java.util.UUID; @@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController @ConditionalOnTestHelperEnabled public class BaseTestHelperController extends TestHelperController - implements BaseTestHelperApi, SharedAuditLogTestHelperApi { + implements BaseTestHelperApi, AuditLogClientTestHelperApi { private final BaseTestHelperService baseTestHelperService; private final BaseFeatureToggle baseFeatureToggle; @@ -57,6 +57,7 @@ public class BaseTestHelperController extends TestHelperController private final BusinessModulesConfigurationProperties businessModulesConfigurationProperties; private final CitizenKeycloakClient citizenKeycloakClient; private final AuditLogNotificationJob auditLogNotificationJob; + private final GdprCleanupJob gdprCleanupJob; public BaseTestHelperController( BaseTestHelperService baseTestHelperService, @@ -66,7 +67,8 @@ public class BaseTestHelperController extends TestHelperController EnvironmentConfig environmentConfig, BusinessModulesConfigurationProperties businessModulesConfigurationProperties, CitizenKeycloakClient citizenKeycloakClient, - AuditLogNotificationJob auditLogNotificationJob) { + AuditLogNotificationJob auditLogNotificationJob, + GdprCleanupJob gdprCleanupJob) { super(baseTestHelperService, environmentConfig); this.baseTestHelperService = baseTestHelperService; this.baseFeatureToggle = baseFeatureToggle; @@ -75,6 +77,7 @@ public class BaseTestHelperController extends TestHelperController this.businessModulesConfigurationProperties = businessModulesConfigurationProperties; this.citizenKeycloakClient = citizenKeycloakClient; this.auditLogNotificationJob = auditLogNotificationJob; + this.gdprCleanupJob = gdprCleanupJob; } @Override @@ -167,6 +170,11 @@ public class BaseTestHelperController extends TestHelperController } } + @Override + public void resetActivatedBusinessModules() { + baseTestHelperService.resetProperties(businessModulesConfigurationProperties); + } + @Override public void createSetupAdmin(CreateSetupAdminRequest request) { masterKeycloakProvisioning.initializeSetupAdmin(request.username(), request.emailAddress()); @@ -219,17 +227,17 @@ public class BaseTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditlogNotificationJob() { + auditLogNotificationJob.run(); } @Override - public void runAuditlogNotificationJob() { - auditLogNotificationJob.run(); + public void runGdprCleanupJob() { + gdprCleanupJob.performGdprCleanup(); } } diff --git a/backend/base/src/main/resources/migrations/0040_add_auditlog_entry.xml b/backend/base/src/main/resources/migrations/0040_add_auditlog_entry.xml new file mode 100644 index 000000000..ebb946b32 --- /dev/null +++ b/backend/base/src/main/resources/migrations/0040_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/base/src/main/resources/migrations/0041_remove_gdpr_status_open.xml b/backend/base/src/main/resources/migrations/0041_remove_gdpr_status_open.xml new file mode 100644 index 000000000..f27aec591 --- /dev/null +++ b/backend/base/src/main/resources/migrations/0041_remove_gdpr_status_open.xml @@ -0,0 +1,11 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1738097216405-1"> + <ext:modifyPostgresEnumType name="gdprprocedurestatus" newValues="ABORTED, CLOSED, DRAFT, IN_PROGRESS"/> + </changeSet> +</databaseChangeLog> diff --git a/backend/base/src/main/resources/migrations/changelog.xml b/backend/base/src/main/resources/migrations/changelog.xml index a617fb2ae..7a468ec09 100644 --- a/backend/base/src/main/resources/migrations/changelog.xml +++ b/backend/base/src/main/resources/migrations/changelog.xml @@ -47,5 +47,7 @@ <include file="migrations/0037_add_maps-id-to-muk-facility-link.xml"/> <include file="migrations/0038_shedlock.xml"/> <include file="migrations/0039-add-countrycodes.xml"/> + <include file="migrations/0040_add_auditlog_entry.xml"/> + <include file="migrations/0041_remove_gdpr_status_open.xml"/> </databaseChangeLog> diff --git a/backend/build.gradle b/backend/build.gradle index 8a19f90ac..d2ee26a35 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -19,7 +19,8 @@ buildscript { plugins { id 'io.spring.dependency-management' version 'latest.release' apply false id 'com.avast.gradle.docker-compose' version 'latest.release' apply false - id 'org.springframework.boot' version 'latest.release' apply false + // âš Note âš Pinned to 3.4.1 because of https://github.com/spring-projects/spring-framework/issues/34324 + id 'org.springframework.boot' version '3.4.1' apply false id 'org.cyclonedx.bom' version 'latest.release' apply false id 'com.trileuco.dependency-track-gradle' version 'latest.release' apply false id 'jacoco-report-aggregation' @@ -61,7 +62,7 @@ sonar { //CAUTION: run ./gradlew wrapper (twice) after changes to this task! //See https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper wrapper { - gradleVersion = "8.9" + gradleVersion = "8.12.1" distributionType = Wrapper.DistributionType.ALL } @@ -257,9 +258,6 @@ subprojects { dependsOn findUnusedValidationFilesTask } - def springBootVersion = dependencyManagement.managedVersions['org.springframework.boot:spring-boot'] - assert springBootVersion == "3.4.1": "Remove the dependency overwrite of 'hibernate.version' in gradle.properties since Spring Boot is updated to ${springBootVersion}" - clean { delete test.outputs } diff --git a/backend/buildSrc/src/main/groovy/eshg.service.gradle b/backend/buildSrc/src/main/groovy/eshg.service.gradle index e872a90e0..161bc7669 100644 --- a/backend/buildSrc/src/main/groovy/eshg.service.gradle +++ b/backend/buildSrc/src/main/groovy/eshg.service.gradle @@ -76,7 +76,7 @@ bootJar { ext { listenPort = 8080 additionalAptPackages = null - jreDockerImage = 'eclipse-temurin:21.0.5_11-jre-jammy@sha256:5d7781c6aabbc2f8673ec1899f2fdf6851f20c94dcdefeb8a1ca1e37e9c13e92' + jreDockerImage = 'eclipse-temurin:21.0.6_7-jre-noble@sha256:7cad9a0229472d691deae12aaa342dfc1541251a9bfc4557f65cdf0b81817ddd' } tasks.register('createDockerfile', Dockerfile) { @@ -97,7 +97,7 @@ tasks.register('createDockerfile', Dockerfile) { def userName = 'eshg' runCommand("addgroup --system ${groupName}") - runCommand("adduser --shell /usr/sbin/nologin --system --home /app --ingroup ${groupName} --uid 1000 ${userName}") + runCommand("adduser --shell /usr/sbin/nologin --system --home /app --ingroup ${groupName} ${userName}") if (additionalAptPackages != null) { runCommand("DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ${additionalAptPackages} && rm -rf /var/lib/apt/lists/*") } diff --git a/backend/business-module-commons/gradle.lockfile b/backend/business-module-commons/gradle.lockfile index a53750b21..20feb6af7 100644 --- a/backend/business-module-commons/gradle.lockfile +++ b/backend/business-module-commons/gradle.lockfile @@ -53,10 +53,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -91,13 +91,13 @@ org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testFixturesCompileClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jetbrains:annotations:26.0.1=compileClasspath +org.jetbrains:annotations:26.0.2=compileClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -112,16 +112,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/business-module-persistence-commons/gradle.lockfile b/backend/business-module-persistence-commons/gradle.lockfile index f2aa7273e..d4a16358e 100644 --- a/backend/business-module-persistence-commons/gradle.lockfile +++ b/backend/business-module-persistence-commons/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -114,10 +114,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -135,16 +135,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/business-module-persistence-commons/src/main/resources/common-persistence.properties b/backend/business-module-persistence-commons/src/main/resources/common-persistence.properties index ab1884ca9..1a6757229 100644 --- a/backend/business-module-persistence-commons/src/main/resources/common-persistence.properties +++ b/backend/business-module-persistence-commons/src/main/resources/common-persistence.properties @@ -13,3 +13,5 @@ spring.jpa.properties.hibernate.order_updates=true spring.jpa.properties.hibernate.batch_versioned_data=true spring.jpa.properties.hibernate.jdbc.fetch_size=100 + +spring.jpa.properties.hibernate.type.preferred_duration_jdbc_type=INTERVAL_SECOND diff --git a/backend/central-repository/gradle.lockfile b/backend/central-repository/gradle.lockfile index 0fb3cb82f..ac6f1e427 100644 --- a/backend/central-repository/gradle.lockfile +++ b/backend/central-repository/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -118,10 +118,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntime org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -139,16 +139,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/central-repository/src/main/java/de/eshg/centralrepository/config/CentralRepositorySecurityConfig.java b/backend/central-repository/src/main/java/de/eshg/centralrepository/config/CentralRepositorySecurityConfig.java index 06b19a27d..15fcc7a5b 100644 --- a/backend/central-repository/src/main/java/de/eshg/centralrepository/config/CentralRepositorySecurityConfig.java +++ b/backend/central-repository/src/main/java/de/eshg/centralrepository/config/CentralRepositorySecurityConfig.java @@ -5,8 +5,6 @@ package de.eshg.centralrepository.config; -import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; - import de.eshg.lib.common.EshgHttpHeaders; import de.eshg.rest.service.security.AuthorizationCustomizer; import de.eshg.rest.service.security.DefaultEshgSecurityConfig; @@ -27,6 +25,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.web.context.NullSecurityContextRepository; @Configuration public class CentralRepositorySecurityConfig { @@ -43,6 +42,8 @@ public class CentralRepositorySecurityConfig { EshgHeaderPreAuthenticatedProcessingFilter eshgHeaderPreAuthenticatedProcessingFilter = new EshgHeaderPreAuthenticatedProcessingFilter(); eshgHeaderPreAuthenticatedProcessingFilter.setAuthenticationManager(authenticationManager); + eshgHeaderPreAuthenticatedProcessingFilter.setSecurityContextRepository( + new NullSecurityContextRepository()); return http.authorizeHttpRequests( auth -> { @@ -61,7 +62,7 @@ public class CentralRepositorySecurityConfig { .formLogin(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .logout(AbstractHttpConfigurer::disable) - .sessionManagement(customizer -> customizer.sessionCreationPolicy(STATELESS)) + .sessionManagement(AbstractHttpConfigurer::disable) .addFilter(eshgHeaderPreAuthenticatedProcessingFilter) .headers(DefaultEshgSecurityConfig::securityHeaders) .build(); diff --git a/backend/chat-management/gradle.lockfile b/backend/chat-management/gradle.lockfile index e39e67905..1ed92facd 100644 --- a/backend/chat-management/gradle.lockfile +++ b/backend/chat-management/gradle.lockfile @@ -61,10 +61,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,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 @@ -116,10 +116,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -137,16 +137,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/compliance-test/gradle.lockfile b/backend/compliance-test/gradle.lockfile index cfac600db..c77e4a7a5 100644 --- a/backend/compliance-test/gradle.lockfile +++ b/backend/compliance-test/gradle.lockfile @@ -3,8 +3,10 @@ # This file is expected to be part of source control. ch.qos.logback:logback-classic:1.5.12=testCompileClasspath,testRuntimeClasspath ch.qos.logback:logback-core:1.5.12=testCompileClasspath,testRuntimeClasspath +colt:colt:1.2.0=testRuntimeClasspath com.adobe.xmp:xmpcore:6.1.11=testRuntimeClasspath com.bucket4j:bucket4j-core:8.10.1=testRuntimeClasspath +com.carrotsearch:hppc:0.6.0=testRuntimeClasspath com.drewnoakes:metadata-extractor:2.19.0=testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.18.2=testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-core:2.18.2=testCompileClasspath,testRuntimeClasspath @@ -100,6 +102,7 @@ commons-codec:commons-codec:1.17.1=testCompileClasspath,testRuntimeClasspath commons-collections:commons-collections:3.2.2=testRuntimeClasspath commons-io:commons-io:2.18.0=testCompileClasspath,testRuntimeClasspath commons-logging:commons-logging:1.3.4=testRuntimeClasspath +concurrent:concurrent:1.3.4=testRuntimeClasspath de.cronn:commons-lang:1.3=testCompileClasspath,testRuntimeClasspath de.cronn:liquibase-postgres-enum-extension:1.1=testRuntimeClasspath de.cronn:postgres-snapshot-util:1.4=testRuntimeClasspath @@ -110,7 +113,7 @@ de.rototor.pdfbox:graphics2d:3.0.1=testRuntimeClasspath de.topobyte:adt-multicollections:0.0.4=testRuntimeClasspath de.topobyte:osm4j-core:1.4.0=testRuntimeClasspath de.topobyte:osm4j-pbf:1.4.0=testRuntimeClasspath -dnsjava:dnsjava:3.6.2=testRuntimeClasspath +dnsjava:dnsjava:3.6.3=testRuntimeClasspath info.picocli:picocli:4.7.6=testRuntimeClasspath io.github.hakky54:sslcontext-kickstart-for-netty:9.0.0=testRuntimeClasspath io.github.hakky54:sslcontext-kickstart:9.0.0=testRuntimeClasspath @@ -152,10 +155,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=testRuntimeClasspa io.prometheus:prometheus-metrics-model:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=testRuntimeClasspath io.smallrye:jandex:3.2.0=testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath io.swagger:swagger-annotations:1.6.15=testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath @@ -274,10 +277,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=testRuntimeClassp org.hibernate.orm:hibernate-core:6.6.4.Final=testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=testCompileClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.java-websocket:Java-WebSocket:1.6.0=testRuntimeClasspath org.javassist:javassist:3.28.0-GA=testCompileClasspath,testRuntimeClasspath org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=testRuntimeClasspath @@ -305,16 +308,17 @@ org.keycloak:keycloak-client-common-synced:26.0.3=testRuntimeClasspath org.latencyutils:LatencyUtils:2.0.3=testRuntimeClasspath org.liquibase:liquibase-core:4.29.2=testRuntimeClasspath org.lz4:lz4-java:1.8.0=testRuntimeClasspath -org.mnode.ical4j:ical4j:4.0.8=testRuntimeClasspath +org.mnode.ical4j:ical4j:4.1.0=testRuntimeClasspath org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.mozilla:rhino:1.7.13=testRuntimeClasspath org.objenesis:objenesis:3.4=testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=testRuntimeClasspath org.reflections:reflections:0.10.2=testCompileClasspath,testRuntimeClasspath @@ -322,7 +326,7 @@ org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/dental/gradle.lockfile b/backend/dental/gradle.lockfile index 0fbd5407e..03d1be535 100644 --- a/backend/dental/gradle.lockfile +++ b/backend/dental/gradle.lockfile @@ -70,10 +70,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -148,10 +148,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -170,16 +170,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/dental/openApi.json b/backend/dental/openApi.json index 2d20461f4..4c9998473 100644 --- a/backend/dental/openApi.json +++ b/backend/dental/openApi.json @@ -3863,7 +3863,7 @@ } ] }, "Examination" : { - "required" : [ "dateAndTime", "fluoridationConsentGiven", "id", "isFluoridation", "isScreening", "prophylaxisType", "version" ], + "required" : [ "dateAndTime", "id", "isFluoridation", "isScreening", "prophylaxisType", "version" ], "type" : "object", "properties" : { "dateAndTime" : { @@ -3905,7 +3905,7 @@ } }, "ExistingUser" : { - "required" : [ "firstName", "lastName" ], + "required" : [ "firstName", "id", "lastName" ], "type" : "object", "allOf" : [ { "$ref" : "#/components/schemas/PerformingPerson" @@ -4024,7 +4024,6 @@ } }, "FluoridationExaminationResult" : { - "required" : [ "fluorideVarnishApplied" ], "type" : "object", "allOf" : [ { "$ref" : "#/components/schemas/DentalExaminationResult" @@ -5376,6 +5375,7 @@ } }, "NonExistingUser" : { + "required" : [ "id" ], "type" : "object", "allOf" : [ { "$ref" : "#/components/schemas/PerformingPerson" @@ -5450,11 +5450,15 @@ } ] }, "PerformingPerson" : { - "required" : [ "@type" ], + "required" : [ "@type", "id" ], "type" : "object", "properties" : { "@type" : { "type" : "string" + }, + "id" : { + "type" : "string", + "format" : "uuid" } }, "discriminator" : { @@ -5920,7 +5924,7 @@ "enum" : [ "NOT_SPECIFIED", "NEUTRAL", "FEMALE", "MALE" ] }, "ScreeningExaminationResult" : { - "required" : [ "fluorideVarnishApplied", "toothDiagnoses" ], + "required" : [ "toothDiagnoses" ], "type" : "object", "allOf" : [ { "$ref" : "#/components/schemas/DentalExaminationResult" @@ -6322,19 +6326,30 @@ } }, "UpdateProphylaxisSessionRequest" : { - "required" : [ "dateAndTime", "groupName", "isScreening", "type", "version" ], + "required" : [ "dateAndTime", "dentistIds", "groupName", "institutionId", "isScreening", "type", "version", "zfaIds" ], "type" : "object", "properties" : { "dateAndTime" : { "type" : "string", "format" : "date-time" }, + "dentistIds" : { + "type" : "array", + "items" : { + "type" : "string", + "format" : "uuid" + } + }, "fluoridationVarnish" : { "$ref" : "#/components/schemas/FluoridationVarnish" }, "groupName" : { "type" : "string" }, + "institutionId" : { + "type" : "string", + "format" : "uuid" + }, "isScreening" : { "type" : "boolean" }, @@ -6344,6 +6359,13 @@ "version" : { "type" : "integer", "format" : "int64" + }, + "zfaIds" : { + "type" : "array", + "items" : { + "type" : "string", + "format" : "uuid" + } } } }, diff --git a/backend/dental/src/main/java/de/eshg/dental/ChildController.java b/backend/dental/src/main/java/de/eshg/dental/ChildController.java index d99e4d59b..4f2b4ff10 100644 --- a/backend/dental/src/main/java/de/eshg/dental/ChildController.java +++ b/backend/dental/src/main/java/de/eshg/dental/ChildController.java @@ -166,7 +166,7 @@ public class ChildController { @PathVariable("examinationId") UUID examinationId, @Valid @RequestBody UpdateExaminationRequest request) { Examination examination = examinationService.findExaminationForUpdate(examinationId); - examinationService.updateExamination(examination, request); + examinationService.updateExaminationAndFlush(examination, request); return ExaminationMapper.mapToDto(examination); } diff --git a/backend/dental/src/main/java/de/eshg/dental/ChildService.java b/backend/dental/src/main/java/de/eshg/dental/ChildService.java index 079fdffea..f1a694d5f 100644 --- a/backend/dental/src/main/java/de/eshg/dental/ChildService.java +++ b/backend/dental/src/main/java/de/eshg/dental/ChildService.java @@ -60,6 +60,7 @@ import de.eshg.rest.service.error.NotFoundException; import de.eshg.validation.ValidationUtil; import java.io.IOException; import java.time.Clock; +import java.time.Instant; import java.time.Year; import java.util.Collection; import java.util.Collections; @@ -259,7 +260,7 @@ public class ChildService { .map(ChildWithAugmentedData::child) .map(Child::getFluoridationConsents) .flatMap(Collection::stream) - .sorted(Comparator.comparing(FluoridationConsent::dateOfConsent).reversed()) + .sorted(Comparator.comparing(FluoridationConsent::getModifiedAt).reversed()) .toList(); } @@ -407,7 +408,7 @@ public class ChildService { ChildImporter importer = new ChildImporter( sheet, - new ChildRowReader(sheet, actualColumns), + new ChildRowReader(sheet, clock, actualColumns), new FeedbackColumnAccessor(actualColumns, ChildColumn.CHILD_ID.getHeader()), institutionId, year, @@ -485,20 +486,28 @@ public class ChildService { FluoridationConsent requestedFluoridationConsent = ChildMapper.mapFluoridationToDomain(request.fluoridationConsent()); - List<FluoridationConsent> persistedFluoridationConsents = child.getFluoridationConsents(); + FluoridationConsent persistedFluoridationConsent = child.getCurrentFluoridationConsent(); boolean updateFluoridationConsent = requestedFluoridationConsent != null - && (persistedFluoridationConsents.isEmpty() - || !Objects.equals( - requestedFluoridationConsent, persistedFluoridationConsents.getLast())); + && (persistedFluoridationConsent == null + || !fluoridationConsentsMatch( + requestedFluoridationConsent, persistedFluoridationConsent)); if (updateFluoridationConsent) { + requestedFluoridationConsent.setModifiedAt(Instant.now(clock)); child.addFluoridationConsent(requestedFluoridationConsent); } childRepository.flush(); } + private boolean fluoridationConsentsMatch( + FluoridationConsent fluoridationConsent1, FluoridationConsent fluoridationConsent2) { + return fluoridationConsent1.isConsented() == fluoridationConsent2.isConsented() + && Objects.equals(fluoridationConsent1.hasAllergy(), fluoridationConsent2.hasAllergy()) + && fluoridationConsent1.getDateOfConsent().equals(fluoridationConsent2.getDateOfConsent()); + } + private void addSystemProgressEntry( Child child, ChildSystemProgressEntryType childSystemProgressEntryType) { progressEntryUtil.addSystemProgressEntry(child, childSystemProgressEntryType); diff --git a/backend/dental/src/main/java/de/eshg/dental/ExaminationService.java b/backend/dental/src/main/java/de/eshg/dental/ExaminationService.java index fdb952c1d..ef6ecd79a 100644 --- a/backend/dental/src/main/java/de/eshg/dental/ExaminationService.java +++ b/backend/dental/src/main/java/de/eshg/dental/ExaminationService.java @@ -5,16 +5,12 @@ package de.eshg.dental; -import static de.eshg.dental.mapper.ExaminationMapper.mapToDomain; - import de.cronn.reflection.util.ClassUtils; import de.eshg.dental.api.AbsenceExaminationResultDto; import de.eshg.dental.api.ExaminationResultDto; import de.eshg.dental.api.FluoridationExaminationResultDto; import de.eshg.dental.api.IsFluorideVarnishApplicable; import de.eshg.dental.api.ScreeningExaminationResultDto; -import de.eshg.dental.api.ToothDiagnosisDto; -import de.eshg.dental.api.ToothDto; import de.eshg.dental.api.UpdateExaminationRequest; import de.eshg.dental.domain.model.AbsenceExaminationResult; import de.eshg.dental.domain.model.Examination; @@ -22,21 +18,17 @@ import de.eshg.dental.domain.model.ExaminationResult; import de.eshg.dental.domain.model.FluoridationExaminationResult; import de.eshg.dental.domain.model.ProphylaxisSession; import de.eshg.dental.domain.model.ScreeningExaminationResult; -import de.eshg.dental.domain.model.Tooth; -import de.eshg.dental.domain.model.ToothDiagnosis; import de.eshg.dental.domain.repository.ExaminationRepository; +import de.eshg.dental.mapper.ExaminationMapper; import de.eshg.dental.util.ChildSystemProgressEntryType; import de.eshg.dental.util.ExceptionUtil; import de.eshg.dental.util.ProgressEntryUtil; import de.eshg.rest.service.error.BadRequestException; import de.eshg.rest.service.error.NotFoundException; import de.eshg.validation.ValidationUtil; -import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import org.springframework.stereotype.Component; -import org.springframework.util.Assert; @Component public class ExaminationService { @@ -62,13 +54,17 @@ public class ExaminationService { .orElseThrow(ExaminationService::examinationNotFoundException); } + void updateExaminationAndFlush(Examination examination, UpdateExaminationRequest request) { + updateExamination(examination, request); + examinationRepository.flush(); + } + void updateExamination(Examination examination, UpdateExaminationRequest request) { ValidationUtil.validateVersion(request.version(), examination); examination.setNote(request.note()); updateResult(examination, request.result()); progressEntryUtil.addSystemProgressEntry( examination.getChild(), ChildSystemProgressEntryType.EXAMINATION_MODIFIED); - examinationRepository.flush(); } private void updateResult(Examination examination, ExaminationResultDto newResult) { @@ -94,22 +90,28 @@ public class ExaminationService { private static void validateExaminationResult( Examination examination, ExaminationResultDto newResult) { - if (newResult == null || newResult instanceof AbsenceExaminationResultDto) { + if (newResult == null) { return; } ProphylaxisSession prophylaxisSession = examination.getProphylaxisSession(); if (prophylaxisSession.isScreening()) { + if (newResult instanceof AbsenceExaminationResultDto) { + return; + } if (!(newResult instanceof ScreeningExaminationResultDto screeningExaminationResult)) { throw newIllegalExaminationResultException(newResult, ScreeningExaminationResultDto.class); } - if (screeningExaminationResult.fluorideVarnishApplied() + if (screeningExaminationResult.isFluorideVarnishAppliedOrFalse() && !prophylaxisSession.hasFluoridationVarnish()) { throw newIllegalExaminationResultException( "Got fluorideVarnishApplied=true but no fluoridation varnish is configured for prophylaxis session"); } Validator.validateToothDiagnoses(screeningExaminationResult.toothDiagnoses()); } else if (prophylaxisSession.hasFluoridationVarnish()) { + if (newResult instanceof AbsenceExaminationResultDto) { + return; + } if (!(newResult instanceof FluoridationExaminationResultDto)) { throw newIllegalExaminationResultException( newResult, FluoridationExaminationResultDto.class); @@ -119,7 +121,7 @@ public class ExaminationService { } if (newResult instanceof IsFluorideVarnishApplicable fluorideVarnishApplicableResult - && fluorideVarnishApplicableResult.fluorideVarnishApplied() + && fluorideVarnishApplicableResult.isFluorideVarnishAppliedOrFalse() && !examination.getChild().isFluoridationConsentCurrentlyGiven()) { throw newIllegalExaminationResultException( "Got fluorideVarnishApplied=true but fluoridation consent is not given"); @@ -140,77 +142,54 @@ public class ExaminationService { } private void mapResult(Examination examination, ScreeningExaminationResultDto newResult) { - Map<Tooth, ToothDiagnosis> persistedToothDiagnoses = Map.of(); - if (examination.getResult() instanceof ScreeningExaminationResult screeningExaminationResult - && !newResult.toothDiagnoses().isEmpty()) { - List<ToothDto> teeth = - newResult.toothDiagnoses().stream().map(ToothDiagnosisDto::tooth).toList(); - persistedToothDiagnoses = screeningExaminationResult.getToothDiagnoses(); - - removeMatchingTeethFromPersistedTeeth(teeth, persistedToothDiagnoses); - } - - Map<Tooth, ToothDiagnosis> newToothDiagnoses = mapToDomain(newResult.toothDiagnoses()); - newToothDiagnoses.putAll(persistedToothDiagnoses); mapResult( examination, ScreeningExaminationResult.class, existingResult -> { existingResult.setFluorideVarnishApplied(newResult.fluorideVarnishApplied()); - existingResult.setOralHygieneStatus(mapToDomain(newResult.oralHygieneStatus())); - existingResult.setToothDiagnoses(newToothDiagnoses); + existingResult.setOralHygieneStatus( + ExaminationMapper.mapToDomain(newResult.oralHygieneStatus())); + existingResult.setToothDiagnoses( + ExaminationMapper.mapToDomain(newResult.toothDiagnoses())); }); } - private static void removeMatchingTeethFromPersistedTeeth( - List<ToothDto> teeth, Map<Tooth, ToothDiagnosis> persistedToothDiagnoses) { - for (ToothDto tooth : teeth) { - // milk teeth - Tooth matchingPermanentTooth = - mapToDomain(ToothDto.matchingPermanentToothForMilkTooth(tooth)); - if (matchingPermanentTooth != null) { - persistedToothDiagnoses.remove(matchingPermanentTooth); - } - - // permanentTeeth - Tooth matchingMilkTooth = mapToDomain(ToothDto.matchingMilkToothForPermanentTooth(tooth)); - if (matchingMilkTooth != null) { - persistedToothDiagnoses.remove(matchingMilkTooth); - } - - persistedToothDiagnoses.remove(mapToDomain(tooth)); - } - } - private void mapResult(Examination examination, AbsenceExaminationResultDto newResult) { mapResult( examination, AbsenceExaminationResult.class, existingResult -> - existingResult.setReasonForAbsence(mapToDomain(newResult.reasonForAbsence()))); + existingResult.setReasonForAbsence( + ExaminationMapper.mapToDomain(newResult.reasonForAbsence()))); } private <R extends ExaminationResult> void mapResult( Examination examination, Class<R> expectedResultClass, Consumer<R> mapping) { ExaminationResult existingResult = examination.getResult(); - if (existingResult != null) { + if (existingResult == null || existingResult instanceof AbsenceExaminationResult) { + acceptAndSetResult(examination, expectedResultClass, mapping); + } else { Class<? extends ExaminationResult> examinationResultClass = ClassUtils.getRealClass(existingResult); - Assert.isTrue( - examinationResultClass.equals(expectedResultClass), - () -> - "Cannot change examination result class from %s to %s" - .formatted(examinationResultClass, expectedResultClass)); + if (!examinationResultClass.equals(expectedResultClass)) { + throw new BadRequestException( + "Illegal examination result type", + "Cannot change examination result class from %s to %s" + .formatted(examinationResultClass, expectedResultClass)); + } @SuppressWarnings("unchecked") // We actually did check the type R castedResult = (R) existingResult; mapping.accept(castedResult); - } else { - R result = ClassUtils.createNewInstance(expectedResultClass); - mapping.accept(result); - examination.setResult(result); } } + private static <R extends ExaminationResult> void acceptAndSetResult( + Examination examination, Class<R> expectedResultClass, Consumer<R> mapping) { + R result = ClassUtils.createNewInstance(expectedResultClass); + mapping.accept(result); + examination.setResult(result); + } + private static NotFoundException examinationNotFoundException() { return ExceptionUtil.notFoundException(Examination.class); } diff --git a/backend/dental/src/main/java/de/eshg/dental/ProphylaxisSessionService.java b/backend/dental/src/main/java/de/eshg/dental/ProphylaxisSessionService.java index bf78d4319..53247b32d 100644 --- a/backend/dental/src/main/java/de/eshg/dental/ProphylaxisSessionService.java +++ b/backend/dental/src/main/java/de/eshg/dental/ProphylaxisSessionService.java @@ -25,12 +25,12 @@ import de.eshg.dental.business.model.ProphylaxisSessionWithAugmentedInstitution; import de.eshg.dental.client.PersonClient; import de.eshg.dental.domain.model.Child; import de.eshg.dental.domain.model.Examination; +import de.eshg.dental.domain.model.Person; import de.eshg.dental.domain.model.ProphylaxisSession; import de.eshg.dental.domain.repository.ChildRepository; import de.eshg.dental.domain.repository.ExaminationRepository; import de.eshg.dental.domain.repository.ProphylaxisSessionRepository; import de.eshg.dental.mapper.ProphylaxisSessionMapper; -import de.eshg.domain.model.BaseEntityWithExternalId; import de.eshg.lib.contact.ContactClient; import de.eshg.lib.procedure.domain.model.ProcedureStatus; import de.eshg.rest.service.error.BadRequestException; @@ -44,6 +44,7 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; @@ -92,9 +93,6 @@ public class ProphylaxisSessionService { public ProphylaxisSession createProphylaxisSession(CreateProphylaxisSessionRequest request) { ProphylaxisSession session = new ProphylaxisSession(); mapProphylaxisSessionRequest(session, request); - session.setInstitutionId(request.institutionId()); - session.setDentistIds(request.dentistIds()); - session.setZfaIds(request.zfaIds()); addExaminationsForChildren(request, session); prophylaxisSessionRepository.save(session); return session; @@ -198,8 +196,11 @@ public class ProphylaxisSessionService { .toList(); Map<UUID, List<Examination>> examinationsByFileStateId = - examinationRepository.findAllByChildFileStateIds(allFileStateIds).stream() - .collect(Collectors.groupingBy(ex -> ex.getChild().getChildIdFromCentralFile())); + examinationRepository + .findAllByPersonFileStateIds(Person.PERSON_TYPE_USED_FOR_CHILDREN, allFileStateIds) + .collect( + Collectors.groupingBy( + examination -> examination.getChild().getChildIdFromCentralFile())); Map<UUID, List<Examination>> previousExaminationsBySessionChildFileStateId = fileStateIdsOfChildrenInSession.stream() @@ -233,9 +234,18 @@ public class ProphylaxisSessionService { .map(examinationsByChildFileStateId::get) .filter(Objects::nonNull) .flatMap(List::stream)) + .filter(isBefore(prophylaxisSessionToIgnore)) .toList(); } + private static Predicate<Examination> isBefore(ProphylaxisSession prophylaxisSession) { + return examination -> + examination + .getProphylaxisSession() + .getDateAndTime() + .isBefore(prophylaxisSession.getDateAndTime()); + } + private Map<UUID, GetPersonFileStateResponse> fetchPersonFileStatesInBulk( List<Examination> examinations) { List<Child> children = examinations.stream().map(Examination::getChild).toList(); @@ -320,6 +330,9 @@ public class ProphylaxisSessionService { UUID prophylaxisSessionId, UpdateProphylaxisSessionRequest updateRequest) { ProphylaxisSession persistedProphylaxisSession = findProphylaxisSessionForUpdate(prophylaxisSessionId, updateRequest.version()); + Validator.validateUpdatableFields( + persistedProphylaxisSession, + mapProphylaxisSessionRequest(new ProphylaxisSession(), updateRequest)); validator.validateGroupAtInstitutionExists( persistedProphylaxisSession.getInstitutionId(), updateRequest.groupName()); @@ -337,30 +350,38 @@ public class ProphylaxisSessionService { .map(UpdateExaminationsInBulkRequest::id) .toList(); + List<Long> ids = examinationRepository.findAllByExternalIdsForUpdate(examinationIds); Map<UUID, Examination> persistedExaminations = - examinationRepository.findAllByExternalIdsForUpdate(examinationIds).stream() - .collect( - Collectors.toMap(BaseEntityWithExternalId::getExternalId, Function.identity())); + examinationRepository + .fetchByIds(ids) + .collect(StreamUtil.toLinkedHashMap(Examination::getExternalId)); for (UpdateExaminationsInBulkRequest examinationUpdate : updateRequest.examinationUpdates()) { Examination persistedExamination = persistedExaminations.get(examinationUpdate.id()); - + if (persistedExamination == null) { + throw new NotFoundException( + "Examination with id %s not found".formatted(examinationUpdate.id())); + } examinationService.updateExamination( persistedExamination, new UpdateExaminationRequest( examinationUpdate.version(), examinationUpdate.note(), examinationUpdate.result())); } - + examinationRepository.flush(); return getProphylaxisSessionWithDetails(prophylaxisSessionId); } - private void mapProphylaxisSessionRequest( + private ProphylaxisSession mapProphylaxisSessionRequest( ProphylaxisSession session, ProphylaxisSessionRequest request) { + session.setInstitutionId(request.institutionId()); session.setDateAndTime(request.dateAndTime()); session.setGroupName(request.groupName()); session.setType(ProphylaxisSessionMapper.mapToDomain(request.type())); session.setIsScreening(request.isScreening()); session.setFluoridationVarnish( ProphylaxisSessionMapper.mapToDomain(request.fluoridationVarnish())); + session.setDentistIds(request.dentistIds()); + session.setZfaIds(request.zfaIds()); + return session; } } diff --git a/backend/dental/src/main/java/de/eshg/dental/Validator.java b/backend/dental/src/main/java/de/eshg/dental/Validator.java index 9f65e6ef4..7e3d32479 100644 --- a/backend/dental/src/main/java/de/eshg/dental/Validator.java +++ b/backend/dental/src/main/java/de/eshg/dental/Validator.java @@ -8,6 +8,8 @@ package de.eshg.dental; import static de.eshg.lib.procedure.util.ProcedureValidator.hasNonNullValue; import de.cronn.commons.lang.StreamUtil; +import de.cronn.reflection.util.PropertyGetter; +import de.cronn.reflection.util.PropertyUtils; import de.eshg.base.contact.api.InstitutionContactCategoryDto; import de.eshg.base.user.UserApi; import de.eshg.base.user.api.UserDto; @@ -15,16 +17,20 @@ import de.eshg.dental.api.ChildFilterParameters; import de.eshg.dental.api.FluoridationConsentDto; import de.eshg.dental.api.ToothDiagnosisDto; import de.eshg.dental.api.ToothDto; +import de.eshg.dental.domain.model.Examination; +import de.eshg.dental.domain.model.ProphylaxisSession; import de.eshg.dental.domain.repository.ChildRepository; import de.eshg.lib.contact.ContactClient; import de.eshg.lib.keycloak.TechnicalGroup; import de.eshg.lib.procedure.api.ProcedureSearchParameters; import de.eshg.lib.procedure.domain.model.ProcedureStatus; import de.eshg.rest.service.error.BadRequestException; +import java.beans.PropertyDescriptor; import java.time.Clock; import java.time.Year; import java.util.EnumSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.UUID; import org.springframework.stereotype.Component; @@ -37,6 +43,13 @@ public class Validator { private final ChildRepository childRepository; private final UserApi userApi; + private static final List<PropertyGetter<ProphylaxisSession>> UPDATABLE_WITHOUT_RESULT_ONLY = + List.of( + ProphylaxisSession::getInstitutionId, + ProphylaxisSession::getGroupName, + ProphylaxisSession::isScreening, + ProphylaxisSession::getFluoridationVarnish); + public Validator( Clock clock, ContactClient contactClient, ChildRepository childRepository, UserApi userApi) { this.clock = clock; @@ -135,4 +148,27 @@ public class Validator { throw new BadRequestException("There are teeth twice in the list."); } } + + public static void validateUpdatableFields( + ProphylaxisSession current, ProphylaxisSession update) { + boolean hasExaminationResult = + current.getExaminations().stream().anyMatch(Examination::hasResult); + if (hasExaminationResult) { + UPDATABLE_WITHOUT_RESULT_ONLY.forEach( + valueGetter -> validateNotChanged(current, update, valueGetter)); + } + } + + private static void validateNotChanged( + ProphylaxisSession current, + ProphylaxisSession update, + PropertyGetter<ProphylaxisSession> valueGetter) { + if (!Objects.equals(valueGetter.get(current), valueGetter.get(update))) { + PropertyDescriptor property = PropertyUtils.getPropertyDescriptor(current, valueGetter); + throw new BadRequestException( + String.format( + "The '%s' property cannot be modified once examination results have been entered.", + property.getDisplayName())); + } + } } diff --git a/backend/dental/src/main/java/de/eshg/dental/api/CreateChildRequest.java b/backend/dental/src/main/java/de/eshg/dental/api/CreateChildRequest.java index 1546a3570..d32a492e5 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/CreateChildRequest.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/CreateChildRequest.java @@ -9,6 +9,7 @@ import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -25,7 +26,7 @@ public record CreateChildRequest( @NotNull @Size(min = 1, max = 80) String firstName, @NotNull @Size(min = 1, max = 120) String lastName, GenderDto gender, - @NotNull LocalDate dateOfBirth, + @NotNull @DateOfBirth LocalDate dateOfBirth, @Size(min = 1, max = 40) String nameAtBirth, @Size(min = 1, max = 50) String placeOfBirth, CountryCode countryOfBirth, diff --git a/backend/dental/src/main/java/de/eshg/dental/api/ExaminationDto.java b/backend/dental/src/main/java/de/eshg/dental/api/ExaminationDto.java index 9c17af8dc..4d1052174 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/ExaminationDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/ExaminationDto.java @@ -19,6 +19,6 @@ public record ExaminationDto( @NotNull ProphylaxisTypeDto prophylaxisType, @NotNull boolean isScreening, @NotNull boolean isFluoridation, - @NotNull boolean fluoridationConsentGiven, + Boolean fluoridationConsentGiven, String note, @Valid ExaminationResultDto result) {} diff --git a/backend/dental/src/main/java/de/eshg/dental/api/ExistingUserDto.java b/backend/dental/src/main/java/de/eshg/dental/api/ExistingUserDto.java index 456f52052..00b682d00 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/ExistingUserDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/ExistingUserDto.java @@ -7,9 +7,10 @@ package de.eshg.dental.api; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import java.util.UUID; @Schema(name = ExistingUserDto.SCHEMA_NAME) -public record ExistingUserDto(@NotNull String firstName, @NotNull String lastName) +public record ExistingUserDto(@NotNull UUID id, @NotNull String firstName, @NotNull String lastName) implements PerformingPersonDto { public static final String SCHEMA_NAME = "ExistingUser"; diff --git a/backend/dental/src/main/java/de/eshg/dental/api/FluoridationExaminationResultDto.java b/backend/dental/src/main/java/de/eshg/dental/api/FluoridationExaminationResultDto.java index b4a176f4c..1e741adbb 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/FluoridationExaminationResultDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/FluoridationExaminationResultDto.java @@ -6,14 +6,17 @@ package de.eshg.dental.api; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; @Schema(name = FluoridationExaminationResultDto.SCHEMA_NAME) -public record FluoridationExaminationResultDto(@NotNull boolean fluorideVarnishApplied) +public record FluoridationExaminationResultDto(Boolean fluorideVarnishApplied) implements ExaminationResultDto, IsFluorideVarnishApplicable { static final String SCHEMA_NAME = "FluoridationExaminationResult"; + public FluoridationExaminationResultDto() { + this(null); + } + @Override public String type() { return SCHEMA_NAME; diff --git a/backend/dental/src/main/java/de/eshg/dental/api/IsFluorideVarnishApplicable.java b/backend/dental/src/main/java/de/eshg/dental/api/IsFluorideVarnishApplicable.java index f2dc46b9b..21b153663 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/IsFluorideVarnishApplicable.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/IsFluorideVarnishApplicable.java @@ -5,6 +5,14 @@ package de.eshg.dental.api; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.apache.commons.lang3.BooleanUtils; + public interface IsFluorideVarnishApplicable { - boolean fluorideVarnishApplied(); + Boolean fluorideVarnishApplied(); + + @JsonIgnore + default boolean isFluorideVarnishAppliedOrFalse() { + return BooleanUtils.isTrue(fluorideVarnishApplied()); + } } diff --git a/backend/dental/src/main/java/de/eshg/dental/api/NonExistingUserDto.java b/backend/dental/src/main/java/de/eshg/dental/api/NonExistingUserDto.java index 2dd9b7fdd..67378b1f7 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/NonExistingUserDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/NonExistingUserDto.java @@ -6,9 +6,11 @@ package de.eshg.dental.api; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.util.UUID; @Schema(name = NonExistingUserDto.SCHEMA_NAME) -public record NonExistingUserDto() implements PerformingPersonDto { +public record NonExistingUserDto(@NotNull UUID id) implements PerformingPersonDto { public static final String SCHEMA_NAME = "NonExistingUser"; @Override diff --git a/backend/dental/src/main/java/de/eshg/dental/api/PerformingPersonDto.java b/backend/dental/src/main/java/de/eshg/dental/api/PerformingPersonDto.java index 506bb6899..5eece77b3 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/PerformingPersonDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/PerformingPersonDto.java @@ -5,10 +5,13 @@ package de.eshg.dental.api; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.eshg.base.HasTypeDiscriminator; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.util.UUID; @Schema(name = "PerformingPerson") @JsonTypeInfo( @@ -20,4 +23,8 @@ import io.swagger.v3.oas.annotations.media.Schema; @JsonSubTypes.Type(value = NonExistingUserDto.class, name = NonExistingUserDto.SCHEMA_NAME) }) public sealed interface PerformingPersonDto extends HasTypeDiscriminator - permits ExistingUserDto, NonExistingUserDto {} + permits ExistingUserDto, NonExistingUserDto { + @NotNull + @JsonProperty + UUID id(); +} diff --git a/backend/dental/src/main/java/de/eshg/dental/api/ProphylaxisSessionRequest.java b/backend/dental/src/main/java/de/eshg/dental/api/ProphylaxisSessionRequest.java index a30d77a7f..f49e0e606 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/ProphylaxisSessionRequest.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/ProphylaxisSessionRequest.java @@ -6,8 +6,12 @@ package de.eshg.dental.api; import java.time.Instant; +import java.util.List; +import java.util.UUID; public interface ProphylaxisSessionRequest { + UUID institutionId(); + Instant dateAndTime(); String groupName(); @@ -17,4 +21,8 @@ public interface ProphylaxisSessionRequest { boolean isScreening(); FluoridationVarnishDto fluoridationVarnish(); + + List<UUID> dentistIds(); + + List<UUID> zfaIds(); } diff --git a/backend/dental/src/main/java/de/eshg/dental/api/ScreeningExaminationResultDto.java b/backend/dental/src/main/java/de/eshg/dental/api/ScreeningExaminationResultDto.java index ea809cc0a..6686a305f 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/ScreeningExaminationResultDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/ScreeningExaminationResultDto.java @@ -12,15 +12,19 @@ import java.util.List; @Schema(name = ScreeningExaminationResultDto.SCHEMA_NAME) public record ScreeningExaminationResultDto( - @NotNull boolean fluorideVarnishApplied, + Boolean fluorideVarnishApplied, OralHygieneStatusDto oralHygieneStatus, @NotNull @Valid List<ToothDiagnosisDto> toothDiagnoses) implements ExaminationResultDto, IsFluorideVarnishApplicable { static final String SCHEMA_NAME = "ScreeningExaminationResult"; + public ScreeningExaminationResultDto() { + this(null, null, List.of()); + } + public ScreeningExaminationResultDto( - boolean fluorideVarnishApplied, OralHygieneStatusDto oralHygieneStatus) { + Boolean fluorideVarnishApplied, OralHygieneStatusDto oralHygieneStatus) { this(fluorideVarnishApplied, oralHygieneStatus, List.of()); } diff --git a/backend/dental/src/main/java/de/eshg/dental/api/ToothDto.java b/backend/dental/src/main/java/de/eshg/dental/api/ToothDto.java index 27b951947..72a8382c9 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/ToothDto.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/ToothDto.java @@ -90,10 +90,6 @@ public enum ToothDto { } public static ToothDto matchingPermanentToothForMilkTooth(ToothDto tooth) { - if (!tooth.isMilkTooth()) { - return null; - } - return switch (tooth) { case T51 -> T11; case T52 -> T12; @@ -118,39 +114,40 @@ public enum ToothDto { case T83 -> T43; case T84 -> T44; case T85 -> T45; - default -> null; - }; - } - public static ToothDto matchingMilkToothForPermanentTooth(ToothDto tooth) { - if (tooth.isMilkTooth()) { - return null; - } - return switch (tooth) { - case T11 -> T51; - case T12 -> T52; - case T13 -> T53; - case T14 -> T54; - case T15 -> T55; - - case T21 -> T61; - case T22 -> T62; - case T23 -> T63; - case T24 -> T64; - case T25 -> T65; - - case T31 -> T71; - case T32 -> T72; - case T33 -> T73; - case T34 -> T74; - case T35 -> T75; - - case T41 -> T81; - case T42 -> T82; - case T43 -> T83; - case T44 -> T84; - case T45 -> T85; - default -> null; + case T11, + T12, + T13, + T14, + T15, + T16, + T17, + T18, + T21, + T22, + T23, + T24, + T25, + T26, + T27, + T28, + T31, + T32, + T33, + T34, + T35, + T36, + T37, + T38, + T41, + T42, + T43, + T44, + T45, + T46, + T47, + T48 -> + throw new IllegalArgumentException("Unexpected tooth: " + tooth); }; } } diff --git a/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionParticipantsRequest.java b/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionParticipantsRequest.java index 89e2dfb35..8d87485f9 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionParticipantsRequest.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionParticipantsRequest.java @@ -5,10 +5,9 @@ package de.eshg.dental.api; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.UUID; public record UpdateProphylaxisSessionParticipantsRequest( - @NotNull long version, @NotEmpty List<UUID> participants) {} + @NotNull long version, @NotNull List<UUID> participants) {} diff --git a/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionRequest.java b/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionRequest.java index 545791f50..5c5a05051 100644 --- a/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionRequest.java +++ b/backend/dental/src/main/java/de/eshg/dental/api/UpdateProphylaxisSessionRequest.java @@ -6,19 +6,31 @@ package de.eshg.dental.api; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import java.time.Instant; +import java.util.List; +import java.util.UUID; public record UpdateProphylaxisSessionRequest( @NotNull long version, + @NotNull UUID institutionId, @NotNull Instant dateAndTime, @NotBlank String groupName, @NotNull ProphylaxisTypeDto type, @NotNull boolean isScreening, - FluoridationVarnishDto fluoridationVarnish) + FluoridationVarnishDto fluoridationVarnish, + @NotEmpty List<UUID> dentistIds, + @NotEmpty List<UUID> zfaIds) implements ProphylaxisSessionRequest { public UpdateProphylaxisSessionRequest( - Long version, Instant dateAndTime, String groupName, ProphylaxisTypeDto type) { - this(version, dateAndTime, groupName, type, false, null); + Long version, + UUID institutionId, + Instant dateAndTime, + String groupName, + ProphylaxisTypeDto type, + List<UUID> dentistIds, + List<UUID> zfaIds) { + this(version, institutionId, dateAndTime, groupName, type, false, null, dentistIds, zfaIds); } } diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/Child.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/Child.java index a61fd46a9..811cd2f3f 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/Child.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/Child.java @@ -12,6 +12,7 @@ import static de.eshg.lib.common.SensitivityLevel.SENSITIVE; import de.cronn.commons.lang.StreamUtil; import de.eshg.lib.common.DataSensitivity; import de.eshg.lib.procedure.domain.model.Procedure; +import jakarta.annotation.Nullable; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; @@ -109,15 +110,22 @@ public class Child extends Procedure<Child, ChildTask, Person, Facility> { return fluoridationConsents; } - public Boolean getCurrentFluoridationConsent() { + public FluoridationConsent getCurrentFluoridationConsent() { return fluoridationConsents.stream() - .max(Comparator.comparing(FluoridationConsent::dateOfConsent)) - .map(FluoridationConsent::consented) + .max(Comparator.comparing(FluoridationConsent::getModifiedAt)) .orElse(null); } + @Nullable + public Boolean isFluoridationConsentCurrentlyGivenOptionally() { + if (getCurrentFluoridationConsent() == null) { + return null; + } + return getCurrentFluoridationConsent().isConsented(); + } + public boolean isFluoridationConsentCurrentlyGiven() { - return BooleanUtils.isTrue(getCurrentFluoridationConsent()); + return BooleanUtils.isTrue(isFluoridationConsentCurrentlyGivenOptionally()); } public void addFluoridationConsent(FluoridationConsent fluoridationConsent) { diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/Examination.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/Examination.java index 5b5d0960c..9964dd804 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/Examination.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/Examination.java @@ -84,6 +84,10 @@ public class Examination extends BaseEntityWithExternalId { this.result = result; } + public boolean hasResult() { + return result != null; + } + public Instant getDateAndTime() { return getProphylaxisSession().getDateAndTime(); } diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/ExaminationResult.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/ExaminationResult.java index 6b0aff7be..8c0dfb8c8 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/ExaminationResult.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/ExaminationResult.java @@ -5,7 +5,7 @@ package de.eshg.dental.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.DiscriminatorColumn; @@ -19,7 +19,7 @@ import jakarta.persistence.OneToOne; @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) @DataSensitivity(SensitivityLevel.SENSITIVE) -public abstract class ExaminationResult extends BaseEntity { +public abstract class ExaminationResult extends SequencedBaseEntity { @OneToOne(optional = false, mappedBy = Examination_.RESULT) private Examination examination; diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationConsent.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationConsent.java index ec5cc09c9..3179757ae 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationConsent.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationConsent.java @@ -9,11 +9,54 @@ import de.eshg.lib.common.DataSensitivity; import de.eshg.lib.common.SensitivityLevel; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; +import java.time.Instant; import java.time.LocalDate; +import org.springframework.data.annotation.LastModifiedDate; @Embeddable @DataSensitivity(SensitivityLevel.SENSITIVE) -public record FluoridationConsent( - @Column(nullable = false) LocalDate dateOfConsent, - @Column(nullable = false) boolean consented, - Boolean hasAllergy) {} +public class FluoridationConsent { + @LastModifiedDate + @Column(nullable = false) + private Instant modifiedAt; + + @Column(nullable = false) + private LocalDate dateOfConsent; + + @Column(nullable = false) + private boolean consented; + + private Boolean hasAllergy; + + public LocalDate getDateOfConsent() { + return dateOfConsent; + } + + public void setDateOfConsent(LocalDate dateOfConsent) { + this.dateOfConsent = dateOfConsent; + } + + public boolean isConsented() { + return consented; + } + + public void setConsented(boolean consented) { + this.consented = consented; + } + + public Boolean hasAllergy() { + return hasAllergy; + } + + public void setHasAllergy(Boolean hasAllergy) { + this.hasAllergy = hasAllergy; + } + + public Instant getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(Instant modifiedAt) { + this.modifiedAt = modifiedAt; + } +} diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationExaminationResult.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationExaminationResult.java index b539cedc7..1a80050fc 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationExaminationResult.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/FluoridationExaminationResult.java @@ -15,13 +15,13 @@ import jakarta.persistence.Entity; @DiscriminatorValue("FLUORIDATION") public class FluoridationExaminationResult extends ExaminationResult { - private boolean fluorideVarnishApplied; + private Boolean fluorideVarnishApplied; - public boolean isFluorideVarnishApplied() { + public Boolean isFluorideVarnishApplied() { return fluorideVarnishApplied; } - public void setFluorideVarnishApplied(boolean fluorideVarnishApplied) { + public void setFluorideVarnishApplied(Boolean fluorideVarnishApplied) { this.fluorideVarnishApplied = fluorideVarnishApplied; } } diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/model/ScreeningExaminationResult.java b/backend/dental/src/main/java/de/eshg/dental/domain/model/ScreeningExaminationResult.java index e696b6723..517288a96 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/model/ScreeningExaminationResult.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/model/ScreeningExaminationResult.java @@ -23,7 +23,7 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType; @DiscriminatorValue("SCREENING") public class ScreeningExaminationResult extends ExaminationResult { - private boolean fluorideVarnishApplied; + private Boolean fluorideVarnishApplied; @JdbcType(PostgreSQLEnumJdbcType.class) private OralHygieneStatus oralHygieneStatus; @@ -51,11 +51,11 @@ public class ScreeningExaminationResult extends ExaminationResult { this.toothDiagnoses = toothDiagnoses; } - public boolean isFluorideVarnishApplied() { + public Boolean isFluorideVarnishApplied() { return fluorideVarnishApplied; } - public void setFluorideVarnishApplied(boolean fluorideVarnishApplied) { + public void setFluorideVarnishApplied(Boolean fluorideVarnishApplied) { this.fluorideVarnishApplied = fluorideVarnishApplied; } } diff --git a/backend/dental/src/main/java/de/eshg/dental/domain/repository/ExaminationRepository.java b/backend/dental/src/main/java/de/eshg/dental/domain/repository/ExaminationRepository.java index 50de86b5d..1e8d310aa 100644 --- a/backend/dental/src/main/java/de/eshg/dental/domain/repository/ExaminationRepository.java +++ b/backend/dental/src/main/java/de/eshg/dental/domain/repository/ExaminationRepository.java @@ -6,11 +6,13 @@ package de.eshg.dental.domain.repository; import de.eshg.dental.domain.model.Examination; +import de.eshg.lib.procedure.domain.model.PersonType; import de.eshg.lib.procedure.domain.model.ProcedureStatus; import jakarta.persistence.LockModeType; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Lock; @@ -25,30 +27,38 @@ public interface ExaminationRepository Optional<Examination> findOneByExternalIdForUpdate(@Param("examinationId") UUID examinationId); @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("select e from Examination e where e.externalId in :examinationIds order by e.id") - List<Examination> findAllByExternalIdsForUpdate( - @Param("examinationIds") List<UUID> examinationIds); + @Query("select e.id from Examination e where e.externalId in :examinationIds order by e.id") + List<Long> findAllByExternalIdsForUpdate(@Param("examinationIds") List<UUID> examinationIds); + + @Query( + """ + select e from Examination e + join fetch e.child + join fetch e.prophylaxisSession + left join fetch e.result + where e.id in :ids + order by e.id""") + Stream<Examination> fetchByIds(@Param("ids") List<Long> ids); @Query( """ select e from Examination e where e.child.procedureStatus = :status + and e.result is null order by e.id""") - List<Examination> findAllByChildStatus(@Param("status") ProcedureStatus status); + List<Examination> findAllByChildStatusWhereResultIsNull(@Param("status") ProcedureStatus status); Optional<Examination> findByExternalId(UUID examinationId); @Query( - nativeQuery = true, - value = - """ - select e.* - from examination e - join child c on c.id = e.child_id - join person pers on pers.procedure_id = c.id - join prophylaxis_session sess on sess.id = e.prophylaxis_session_id - where pers.central_file_state_id in :fileStateIds - order by sess.date_and_time desc, e.id; - """) - List<Examination> findAllByChildFileStateIds(List<UUID> fileStateIds); + """ + select e from Examination e + join fetch e.child + join fetch e.prophylaxisSession + left join fetch e.result + join e.child.relatedPersons rp on rp.personType = :personType + where rp.centralFileStateId in :fileStateIds + order by e.prophylaxisSession.dateAndTime desc, e.id""") + Stream<Examination> findAllByPersonFileStateIds( + @Param("personType") PersonType personType, @Param("fileStateIds") List<UUID> fileStateIds); } diff --git a/backend/dental/src/main/java/de/eshg/dental/importer/ChildRowReader.java b/backend/dental/src/main/java/de/eshg/dental/importer/ChildRowReader.java index d89eb2a9f..e7f23b187 100644 --- a/backend/dental/src/main/java/de/eshg/dental/importer/ChildRowReader.java +++ b/backend/dental/src/main/java/de/eshg/dental/importer/ChildRowReader.java @@ -17,13 +17,14 @@ import de.eshg.dental.business.model.ImportChildData; import de.eshg.lib.xlsximport.ColumnAccessor; import de.eshg.lib.xlsximport.ErrorHandler; import de.eshg.lib.xlsximport.RowReader; +import java.time.Clock; import java.util.List; import org.apache.poi.ss.usermodel.Sheet; public class ChildRowReader extends RowReader<ChildRow, ChildColumn> { - public ChildRowReader(Sheet sheet, List<ChildColumn> actualColumns) { - super(sheet, actualColumns, ChildRow::new); + public ChildRowReader(Sheet sheet, Clock clock, List<ChildColumn> actualColumns) { + super(sheet, actualColumns, ChildRow::new, clock); } @Override @@ -40,7 +41,7 @@ public class ChildRowReader extends RowReader<ChildRow, ChildColumn> { return new ImportChildData( cellAsString(col, LAST_NAME, errorHandler), cellAsString(col, FIRST_NAME, errorHandler), - cellAsDate(col, DATE_OF_BIRTH, errorHandler), + cellAsDateOfBirth(col, DATE_OF_BIRTH, errorHandler), cellAsGender(col, GENDER, errorHandler), cellAsString(col, GROUP, errorHandler)); } diff --git a/backend/dental/src/main/java/de/eshg/dental/mapper/ChildMapper.java b/backend/dental/src/main/java/de/eshg/dental/mapper/ChildMapper.java index 2abb8c8ca..042354cad 100644 --- a/backend/dental/src/main/java/de/eshg/dental/mapper/ChildMapper.java +++ b/backend/dental/src/main/java/de/eshg/dental/mapper/ChildMapper.java @@ -92,19 +92,20 @@ public final class ChildMapper { return new ArrayList<>(); } return fluoridationConsent.stream() - .map(f -> new FluoridationConsentDto(f.dateOfConsent(), f.consented(), f.hasAllergy())) + .map(f -> new FluoridationConsentDto(f.getDateOfConsent(), f.isConsented(), f.hasAllergy())) .toList(); } - public static FluoridationConsent mapFluoridationToDomain( - FluoridationConsentDto fluoridationConsent) { - if (fluoridationConsent == null) { + public static FluoridationConsent mapFluoridationToDomain(FluoridationConsentDto dto) { + if (dto == null) { return null; } - return new FluoridationConsent( - fluoridationConsent.dateOfConsent(), - fluoridationConsent.consented(), - fluoridationConsent.hasAllergy()); + + FluoridationConsent fluoridationConsent = new FluoridationConsent(); + fluoridationConsent.setDateOfConsent(dto.dateOfConsent()); + fluoridationConsent.setConsented(dto.consented()); + fluoridationConsent.setHasAllergy(dto.hasAllergy()); + return fluoridationConsent; } public static CreateChildRequest mapImportDataToCreateChildRequest( diff --git a/backend/dental/src/main/java/de/eshg/dental/mapper/ExaminationMapper.java b/backend/dental/src/main/java/de/eshg/dental/mapper/ExaminationMapper.java index f6add5233..375938c00 100644 --- a/backend/dental/src/main/java/de/eshg/dental/mapper/ExaminationMapper.java +++ b/backend/dental/src/main/java/de/eshg/dental/mapper/ExaminationMapper.java @@ -47,7 +47,7 @@ public final class ExaminationMapper { ProphylaxisSessionMapper.mapToDto(prophylaxisSession.getType()), prophylaxisSession.isScreening(), prophylaxisSession.hasFluoridationVarnish(), - examination.getChild().isFluoridationConsentCurrentlyGiven(), + examination.getChild().isFluoridationConsentCurrentlyGivenOptionally(), examination.getNote(), mapToDto(examination.getResult())); } diff --git a/backend/dental/src/main/java/de/eshg/dental/mapper/ProphylaxisSessionMapper.java b/backend/dental/src/main/java/de/eshg/dental/mapper/ProphylaxisSessionMapper.java index 9e541b112..cd940595d 100644 --- a/backend/dental/src/main/java/de/eshg/dental/mapper/ProphylaxisSessionMapper.java +++ b/backend/dental/src/main/java/de/eshg/dental/mapper/ProphylaxisSessionMapper.java @@ -127,7 +127,7 @@ public final class ProphylaxisSessionMapper { examination.getChild().getGroupName().trim(), fileStateResponse.gender(), examination.getNote(), - examination.getChild().getCurrentFluoridationConsent(), + examination.getChild().isFluoridationConsentCurrentlyGivenOptionally(), ExaminationMapper.mapToDto(examination.getResult()), previousExaminations.stream() .map(Examination::getResult) @@ -143,8 +143,8 @@ public final class ProphylaxisSessionMapper { userId -> Optional.ofNullable(userMap.get(userId)) .<PerformingPersonDto>map( - user -> new ExistingUserDto(user.firstName(), user.lastName())) - .orElse(new NonExistingUserDto())) + user -> new ExistingUserDto(userId, user.firstName(), user.lastName())) + .orElse(new NonExistingUserDto(userId))) .toList(); } diff --git a/backend/dental/src/main/java/de/eshg/dental/testhelper/ChildrenPopulator.java b/backend/dental/src/main/java/de/eshg/dental/testhelper/ChildrenPopulator.java index 1ca6b9283..416f0cc22 100644 --- a/backend/dental/src/main/java/de/eshg/dental/testhelper/ChildrenPopulator.java +++ b/backend/dental/src/main/java/de/eshg/dental/testhelper/ChildrenPopulator.java @@ -77,16 +77,16 @@ public class ChildrenPopulator extends DentalPopulator<CreateChildResponse> { Year yearInPast = Year.of(request.year() - i); CreateChildRequest requestForPast = withNewYear(request, yearInPast); UUID childId = childController.createChild(requestForPast).id(); - updateChild(childId, faker, i); + updateChild(childId, faker); } CreateChildResponse child = childController.createChild(request); - updateChild(child.id(), faker, numberOfPastYears); + updateChild(child.id(), faker); return child; } - private void updateChild(UUID childId, Faker faker, int numberOfPastYears) { + private void updateChild(UUID childId, Faker faker) { ChildDetailsDto childDetails = childController.getChild(childId); childController.updateChild( childId, diff --git a/backend/dental/src/main/java/de/eshg/dental/testhelper/ProphylaxisSessionsPopulator.java b/backend/dental/src/main/java/de/eshg/dental/testhelper/ProphylaxisSessionsPopulator.java index 0bff92c20..32c187ce4 100644 --- a/backend/dental/src/main/java/de/eshg/dental/testhelper/ProphylaxisSessionsPopulator.java +++ b/backend/dental/src/main/java/de/eshg/dental/testhelper/ProphylaxisSessionsPopulator.java @@ -166,7 +166,9 @@ public class ProphylaxisSessionsPopulator private void randomExaminations(Faker faker) { List<Examination> someExaminations = - randomElements(faker, examinationRepository.findAllByChildStatus(ProcedureStatus.OPEN)); + randomElements( + faker, + examinationRepository.findAllByChildStatusWhereResultIsNull(ProcedureStatus.OPEN)); for (Examination examination : someExaminations) { UpdateExaminationRequest request = @@ -180,21 +182,28 @@ public class ProphylaxisSessionsPopulator private static ExaminationResultDto randomResult(Faker faker, Examination examination) { ProphylaxisSession prophylaxisSession = examination.getProphylaxisSession(); + boolean isScreening = prophylaxisSession.isScreening(); boolean hasFluoridationVarnish = prophylaxisSession.hasFluoridationVarnish(); + + if (isScreening || hasFluoridationVarnish) { + if (faker.random().nextDouble() < 0.1) { + return new AbsenceExaminationResultDto(randomElement(faker, ReasonForAbsenceDto.values())); + } + } + boolean isFluoridationConsentGiven = examination.getChild().isFluoridationConsentCurrentlyGiven(); - if (prophylaxisSession.isScreening()) { + + if (isScreening) { return new ScreeningExaminationResultDto( - hasFluoridationVarnish && isFluoridationConsentGiven && faker.bool().bool(), + optional( + faker, hasFluoridationVarnish && isFluoridationConsentGiven && faker.bool().bool()), optional(faker, randomElement(faker, OralHygieneStatusDto.values())), randomToothDiagnoses(faker)); } else if (hasFluoridationVarnish) { return new FluoridationExaminationResultDto( - isFluoridationConsentGiven && faker.bool().bool()); + optional(faker, isFluoridationConsentGiven && faker.bool().bool())); } else { - if (faker.random().nextDouble() <= 0.5) { - return new AbsenceExaminationResultDto(randomElement(faker, ReasonForAbsenceDto.values())); - } return null; } } diff --git a/backend/dental/src/main/resources/migrations/0029_add_auditlog_entry.xml b/backend/dental/src/main/resources/migrations/0029_add_auditlog_entry.xml new file mode 100644 index 000000000..ebb946b32 --- /dev/null +++ b/backend/dental/src/main/resources/migrations/0029_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/dental/src/main/resources/migrations/0030_add_modified_at_fluoridation.xml b/backend/dental/src/main/resources/migrations/0030_add_modified_at_fluoridation.xml new file mode 100644 index 000000000..46319f5c8 --- /dev/null +++ b/backend/dental/src/main/resources/migrations/0030_add_modified_at_fluoridation.xml @@ -0,0 +1,19 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1738056540772-1"> + <addColumn tableName="child_fluoridation_consents"> + <column name="modified_at" type="TIMESTAMP WITH TIME ZONE" + valueComputed="now()"> + <constraints nullable="false"/> + </column> + </addColumn> + </changeSet> +</databaseChangeLog> diff --git a/backend/dental/src/main/resources/migrations/0031_fluoride_varnish_applied_optional.xml b/backend/dental/src/main/resources/migrations/0031_fluoride_varnish_applied_optional.xml new file mode 100644 index 000000000..26fb42da4 --- /dev/null +++ b/backend/dental/src/main/resources/migrations/0031_fluoride_varnish_applied_optional.xml @@ -0,0 +1,12 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1738304707114-1"> + <dropNotNullConstraint columnDataType="boolean" columnName="fluoride_varnish_applied" tableName="fluoridation_examination_result"/> + <dropNotNullConstraint columnDataType="boolean" columnName="fluoride_varnish_applied" tableName="screening_examination_result"/> + </changeSet> +</databaseChangeLog> diff --git a/backend/dental/src/main/resources/migrations/0032_migrate_examination_result_to_use_sequences.xml b/backend/dental/src/main/resources/migrations/0032_migrate_examination_result_to_use_sequences.xml new file mode 100644 index 000000000..341792d7d --- /dev/null +++ b/backend/dental/src/main/resources/migrations/0032_migrate_examination_result_to_use_sequences.xml @@ -0,0 +1,11 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="migrate_examination_result_to_use_sequence"> + <ext:migrateAutoIncrementToSequence tableName="examination_result"/> + </changeSet> +</databaseChangeLog> diff --git a/backend/dental/src/main/resources/migrations/changelog.xml b/backend/dental/src/main/resources/migrations/changelog.xml index 1a81ab2ee..f8a472d32 100644 --- a/backend/dental/src/main/resources/migrations/changelog.xml +++ b/backend/dental/src/main/resources/migrations/changelog.xml @@ -36,5 +36,9 @@ <include file="migrations/0026_add_tooth_diagnoses.xml"/> <include file="migrations/0027_add_absence_examination_result.xml"/> <include file="migrations/0028_rename_screening.xml"/> + <include file="migrations/0029_add_auditlog_entry.xml"/> + <include file="migrations/0030_add_modified_at_fluoridation.xml"/> + <include file="migrations/0031_fluoride_varnish_applied_optional.xml"/> + <include file="migrations/0032_migrate_examination_result_to_use_sequences.xml"/> </databaseChangeLog> diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml index 03507540d..699a8b131 100644 --- a/backend/docker-compose.yaml +++ b/backend/docker-compose.yaml @@ -572,6 +572,8 @@ services: - de.eshg.base.service-url=http://base:8080 - eshg.keycloak.internal.url=http://keycloak:8080 - de.eshg.auditlog.service-url=http://auditlog:8080 + - de.eshg.official-medical-service.notification.fromAddress=tba@stadt-frankfurt.de + - de.eshg.official-medical-service.notification.greeting=Ihr TBA-Team der Stadt Frankfurt depends_on: official-medical-service-db: condition: service_healthy diff --git a/backend/file-commons/gradle.lockfile b/backend/file-commons/gradle.lockfile index ddb42dbb6..8d6033aab 100644 --- a/backend/file-commons/gradle.lockfile +++ b/backend/file-commons/gradle.lockfile @@ -35,8 +35,8 @@ de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -68,10 +68,10 @@ org.glassfish.jaxb:jaxb-runtime:4.0.5=compileClasspath,productionRuntimeClasspat org.glassfish.jaxb:txw2:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath @@ -86,9 +86,10 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.mozilla:rhino:1.7.13=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/gradle.lockfile b/backend/gradle.lockfile index 1f2f8de29..a3802b679 100644 --- a/backend/gradle.lockfile +++ b/backend/gradle.lockfile @@ -1,11 +1,11 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -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.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.7=jacocoAnt empty=aggregateCodeCoverageReportResults diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 zcmV;H4{7l5(*nQL0<imge+hUS$8~-~00g*#4w9l|=&;w6Xn{CL9Tq7;wj5rzDMCj? z9f2iVUIGhpC197?T}Yx`D`_kDO4~Gv(?m*Rxo&H^t&>Kr1kzC=_KMxQY0|W5(lc#i zH*M1^P4B}|{x<+fkObwl)u#`$GxKKV&3pg*-y6R6txw)0qU|Clf9Uds3x{_-**c=7 z&*)~RHPM>Rw#Hi1R({;bX|7?J@w}DMF>dQQU2}9yj%iLjJ*KD6IEB2^n#gK7M~}6R zkH+)bc--JU^pV~7W=3{E*4|ZFpDpBa7;wh4_%;?XM-5ZgZNnVJ=vm!%a2CdQb?oTa z70>8rTb~M$5Tp!Se+4_OKWOB1LF+7gv~$$fGC95ToUM(I>vrd$>9|@h=O?eARj0MH zT4zo(M>`LWoYvE>pXvqG=d96D-4?VySz~=tPVNyD$XMshoTX(1ZLB5OU!I2OI{kb) zS8$B8Qm>wLT6diNnyJZC?yp{Kn67S{TCOt-!OonOK7$K)e-13U9GlnQXPAb&SJ0#3 z+vs~+4Qovv(%i8g$I<g6IV^8KCdd<RKNh4Fv`QgAH457E|Iph{%?hoi#vlc0c?0=q zO^^cAz+ZO+Nug!@wGQso<f_eEw{8_QTJwTshwe#AXBt)~A6D*3*ABaknmz5eS-iR2 z_AyM^x1+k-f0xr7M^JOPy=pHxxF<+=(p_u?RZy_{SU<JGA^Lg4aUE%}M}oAO+7#ME z_Xui{q6OE;b@o}g9PkDVJAMiaYS8oJ?nDyn1T|Gol#)D#10xhwsGVXE&0&NO!wHSy z_7n5(9i$HG<nv&!=DA&ZY+IAt`)RvEJJ@~845#0)f1!kBPXuXaNy{#1tz`h|mT+=m zaO~mR?4sSgJ|Squd>#FCpCG^C4DdyQw3phJ(f#y*pvNDQCRZ~MvW<}fUs~PL=4??j zmhPyg<*I4RbTz|NHFE-DC7lf2=}-sGkE5e!RM%3ohM7_I^IF=?O{m*u<t91;)H5%X zOaAs#e;-_twd}kGo9+%T2E>UPH(V?gqyc(Rp?-Qu(3bBIL4Fz(v?=_Sh?L<pq|iP} z3i3L}6S@S=V2}>bK{nqZMD>#9D_hNhaV$0ef3@9V90|0u#|PUNTO>$F=qRhg1duaE z0`v~X3G{8RVT@kOa-pU+z8{JWyP6GF*u2e8e<u_=$(U=OZxd6?Gc~wOFg0-e7@u@X z(7v}u5FfAEeAQVjsWn#NzM7ylNFPRaqC$Ut<=iA_XAP9RwG#pR;fH(T+jn*a2o78? zMI1d{unl*jb3f<{jMs0B>Kr7a2t1fuqQy)@d|Qn(%YLZ62TWtoX@$n<jG(pE+6|iH ze+3s?=vv-Sd1i<C%9rqFDP+=kg&i<AZy6Gj7hhliU-(2XL(!#zLJplpG!@1(nhTx% zm>L}9?atE#Yw`rd(>cr0gY;dT9~^oL;u)zgHUvxc2I*b&ZkGM-iq=&(?kyO(3}=P! zRp=rErEyMT5UE9GjPHZ#T<c-|d}+-hf5grP><`cnD)jyIL!8P{H@IU#`e8cAG5jMK zVyKw7--dAC;?-qEu*rMr$5@y535qZ6p(R#+fLA_)G~!wnT~~)|s`}&fA(s6xXN`9j zP#Fd3GBa#HeS{5&8p?%DKUyN^X9cYUc6vq}D_3xJ&d@=6j(6BZKPl?!k1?!`f3z&a zR4ZF60Mx7oBxLSxGuzA*Dy5n-d2K=+)6VMZh_0KetK|{e;E{8NJJ!)=_E~1uu=A=r zrn&gh)h*SFhsQJo!f+wKMIE;-EOaMSMB@aXRU(UcnJhZW^B^mgs|M9@5WF@s6B0p& zm#CTz)yiQCgURE{%hjxH<q_qse_e2x<s4b}b@0Xdt1olZ&Lz|~2czYE)TC81QE7OV zCMzXsLQ+ec*^_qRkg8#|JaNo$3|Ad<-Ek+;S!TcvCc|>cJ6G&>G9i`7MyftL!QQd5 z@RflRs?7)99?X`kHNt>W3l7YqscBpi*R2+fsgABor>KVOu(i(`03aytf2UA!&SC9v z!E}whj#^9~=XHMinFZ;6UOJjo=mmNaWkv~nC=qH9$s-8roGeyaW-E~Sz<!B7#<2W& zkbaJSUO_SU2~b4Ae8K?p+*LC(&au?IN(n1EA2G&D)zvkHt}}P}b<^c6Cnz+pSQ;=3 zdEJ)Yg$`3R&GIkL+pkK*e@{0MQFxy0w`_eNr{}RN0`SvZx_pXmf^JOxyjz}F7{asp z@t5gWSo~L!R~BhK05vE4^y|<VfqMYyE=aF%z<!2a1+gqyKyzpxu{D0?Xr+>Z3Gg>j zZ8}<320rg4=$`M0nxN!w(PtHUjeeU?MvYgWKZ6<ocm16bvwQ(`e^_xsZy}rIct2Qh ztMbC{3A&@&P4j1eU!gCtUqW-9trjRl>kkzABK;vMN0|U;X9abJleJA(xy<}5h5P(5 z{RzAFPvMnX2m0yH0Jn2Uo-p`daE|(O`YQiC#jB8;6bVIUf?SY(k$#C0`d6qT`>X<j zjw=c&pa$q~n4b|3e_oE|0R5dHfe`x#+;z#vmY=@C#ga7|q;GOO{S*B&a=~G61CvgG z{<V^PrH_t`+15F2gyH?~Pz<&yr~4rNCqv~w=)bCy^}(t@{vXJr!o_n}chh4_1(vD1 zoktH3%D`HV?GJ%fO}F$yUKMU%P<P>e0+0}Oj0=F&*D;PVe=Z<=0AGI<6$gYLwa#r` zm449x*fU;_+J>Mz!wa;T-wldoBB%&OEMJgtm#oaI60TSYCy7;+$5?q!zi5K`u66Wq zvg)Fx$s`V3Em{=OEY{3lmh_7|08ykS&U9w!kp@Ctuzqe1JFOGz6%i5}Kmm9>^=gih z?kRxqLA<3@e=}G4R_?phW{4DVr?`tPfyZSN@R=^;P;?!2bh~F1I|fB7P=V=9a6XU5 z<#0f>RS0O&rhc&nTRFOW7&QhevP0#>j0eq<1@D5yAlgMl5n&O9X|Vq}%RX}iNyRFF z7sX&u#6?E~bm~N|z&YikXC=I0E*8Z$v7PtWfjy)$e_Ez25fnR1Q=q1`;U!~U>|&YS zaOS8y!^ORmr2<dvL4k+K`{um_5n4I}aW8<6Qkr338d^x^rBQhDZK6jJ4~SkYFdVKt zZM!gz>L4ik!IYR8@Dcx8MTC=(b4P6iE5CnrbI~7j7DmM8em$!da&D!6Xu)!vKPdLG z9f#)se|6=5yOCe)N6xDhPI!m81*dNe7u985zi%IV<DfXChy&v7V6xfL=$z)L#@wwt z0%BO9H|a&_M2HFsh~r=~TRz;5SV58E+`dVm9B8cO23eW~#7Us8TyQ2-x8!Oy91xFj z@m-^Iua4-)Iim4a^W>fOfJh69+#ag4ELzGne?o`eA`42K4T)h3S+s)5IT97%O>du- z0U54L8m4}rkRQ?QBfJ%DLssy^+a7A<zfR$+k^eR&+aN7R>jw;0&`NOTY4o;0-ivm9 zBz1C%nr_hQ)X)^QM6T1?=yeLkuG9Lf5<U&Ifi{*FiHESH9__Mdq)2xkFVdFFq)bzM ze`t~(h!$yEYUov}pS<;r$PL<&N>0(eH}`tFye;01&(p?8i+6h};VV-2B~qdxeC#=X z(JLlzy&fHkyi9Ksbcs~&r^%lh^2COldLz^H@X!s~mr9Dr6z!j+4?zkD@Ls7F8(t(f z9`U?P$Lmn*Y{K}aR4N&1N=?xtQ1%jqf1~pJyQ4SgBrEtR`j4lQuh7cqP49Em5cO=I zB(He2`iPN5M=Y0}h(IU$37ANTGx&|b-u1BYA*#dE(L-lptoOpo&th~E)_)y-`6kSH z3vvyVrcBwW^_XYReJ=JYd9OBQrzv;f2AQdZH#$Y{Y+Oa33M70XFI((fs;mB4e`<<{ ze4dv2B0V_?Ytsi>>g%qs*}oDGd5d(RNZ*6?7qNbdp7wP4T72=F&r?Ud#kZr8Ze5tB z_oNb7{G+(<vXFPx)*`+CIJU>o2ajL$!69FW@jjPQ2a5C)m!MKKRirC$_VY<U8zprz z;q^p@z0qM`Y`8u?-1O5SZ^=S0f23fapPi9f%)kOIw2pS-W*d;6xoyYq&RKh{fP@eB zdLQid8onF2{4S%j7c(BTT@mT8IGSHzH*NOZzafg-Y+%nuq8qluvD0+*GWk3&U95xd zZ$R?OOJ(4qSSs7Ns~jEA-=OQM)PAU0EYc?#cQcH;i}?680mytNb%1w<f9c~z`i*J& zoCMD2FVRZA)bAdjy!H7>IuVQCpf9rIms0GRDf)8AH${I`q^~5rjot<R`UtJ8`0Mq_ zTVIc-%1(L%|0i#~dnkgF-k6CM=`XMH&kQ0|LA>@#3$2#zT2f`(N^P<YQ<rRa(_YyQ z&_3BF>7Z;6(@EK$q*Jgif00I6*^ZGV+XB5uw*1R-@23yTw&WKD{s1;HTL<p=TE&JK z^Gn7!6pc;OUtdifh@`Gjh>;dO)%5i#`dc6b7;5@^{KU%N|A-$zsYw4)7LA{3`Zp>1 z-?K9_IE&z)dayUM)wd8K^29m-l$lFhi$zj0l!u~4;VGR6Y!?MAfBC^?QD53hy6VdD z@<Fjv%_4x4rp1b)Xsqb4{s5aB%q2YRPLc46gingBMNqI;6Ml9p0P<z_sMyG8M_lp$ z6vu_QyC^m{i+fHLMeLf`S`_!n-|nJFBz%owIDdap+~4JG=ngb=D<NM@q9BL}b*DvN zQ9O8=9$%xZ3A6;Ce?HkA(7N%d;bXOUDBtkRM=7{QY4JY&%w<{|U&DiXci@^xVrY$0 z6o*6pn5QKOAn^{}N_SxL)^kH4f5JOSPPt9opf^*^;>eUZIui}~L%#SmajaRq1J|#> z4m=o$vZ*34=ZWK2!QMNEcp2Lbc5N1q!lEDq(bz0b;WI9;e>l=CG9^n#ro`w>_0F$Q zfZ={2<mE4T<&DS;g=mycixFYS_A&T2Pv>QyTkfByC&gy;x!r*NyXXbk=a%~~(#K?< zTke0HuF5{Q+~?@!KDXR|g+43$+;ab`^flS%miup_0OUTm=nIc%<i2dV9~0fO&n@@U z;u)!DDK46TA^x`mtB+BgNRmgS(HD}X>d5nLP)i308PIjl_YMF6cpQ__6&$n6it8K- z8PIjl_YMF6cpQ_!r)L8IivW`WdK8mBs6PXdjR2DYdK8nCs73=4j{uVadK8oNjwX|E wpAeHLsTu<llK_+9k~foZpAeIasTu=imH?CCk{gpwmKT%SsSpMiod5s;0F!ULw*UYD delta 3889 zcmV-156<wv)B^C+0<imge+zt5=XE~^NtS#O58D_b9*JUbkR{9J5ipp9)HWnB7LQ^G zRUjeP(zSg-y4St;3UIQ}ZX?^;ZtL2n4`>^*Y>Trk?aBtSQ(D-o$(D8Px^?ZI-PUB? z*1fv!{YdHme3Fc8%cR@*@zc5A_nq&2=R4<r9-m+R&Zn;t(I(C@e_c3x_TaWBT88w@ zNyEyvbhl)NTiRPPc7Dvv>7Hp@$-JF4Fz*;SLw5}<j_PhQGiqc`x`n*k(mkx_T%)~Z zY$Tc2$C9SVFow3@ogLZ?UT0fn|8OCf!-PAkCpQX<HDX$Z;h5G4W=|Q8i?cAltz&b? zwq({g)$(MBm`NM7e+y2=xZlhfOe<|?q;tg4vpJ*lw4;xW8BS-v<$8K97bHK^(i8eA zy)&m<Bc1z)P8b<4NOeqgIeTQpaF|x5YV1#`#T`tctbN+b*?N{~O)bV<<z=w0G|psl z1=l>K^y<lE4A<SOTe>>s-s;V!<r$8p=Q@YM*qO64CvoA<f8FzLhw!?4Ow06kGCdNz zg}%4cu-4)M-5c$3T_Zn~!}hj^n0&Fehr+a&mTDBF2BsbV550rbq|q{J2ve9A)l-0$ zhbct$@^xF7G+HQME8$LE?OL~C!v?02niniPbVo`#)3iI~u<}T`cF+^l>}b2i=5=M- zComP?ju>8Fe@=H@rlwe1l`J*6BTTo`9b$zjQ@HxrAhp0D#u?M~TxGC_!?ccCHCjt| zF*PgJf@kJB`|Ml}cmsyrAjO#Kjr^E5p29w+#>$C`Q|54BoDv$fQ9D?3n32P9LPM<W z=*)YEse?M@JlL!0rj#DX(UtfhZPIA7xNo89?lK)He_=c0VcJsCax1jfvw(DSEHyhg zcF1kE(RSJ2&9r!?jzPMIDQLR8<sxC)Nv#X%Ub>Izu?LjNqggOH=1@T{9bMn*u8(GI z!;M<D7tPp~r<+z(x5mL%(#bU3j_B?)V;C6OsAorqO)DEU&gdC0Hy+(M%{Sf=qMjL{ zT=I92fBN8xVcW^;u<7>LTtFPHal^S>VcJdiYqX0VU|Rn@A}C1xOlxCribxes0~+n2 z6qDaIA2$?e`opx3_KW!rAgbpzU)gFdjAKXh|5w``#F0R|c)Y)Du0_Ihhz^S?k^pk% zP>9|pIDx)xHH^_~+aA=^$M!<8K~Hy(71nJGf6`HnjtS=4X4=Hk^O71oNia2V{HUCC zoN3RSBS?<d9l7c>mZCLw;l4W4a+D8qc)XJS`pUJ5X-f^1ytxwr`@si$lAE?{4G|o; zO0l>`rr?;~c;{ZEFJ!!3=7=FdGJ?Q^xfNQh4A?i;IJ4}B+A?4olTK(fN++3CRBP97 ze~lG9h%oegkn)lpW-4F8o2`*WW0mZHwHez`ko@>U1_;EC_6ig|Drn@=DMV9YEUSCa zIf$kHei3(u#zm9I!Jf(4t`Vm1lltJ&lVHy(eIXE8sy9sUpmz%I_gA#8x^Zv8%w?r2 z{GdkX1SkzRIr>prRK@rqn9j2wG|rUvf6PJbbin=yy-TAXrguvzN8jL$hUrIXzr^s5 zVM?H4;eM-QeRFr06@ifV(ocvk?_)~N@1c2ien56UjWXid6<wFbYO4An`mpeRM5@Ny z3+Rl(bU`xgF8Zh#5IvL!n|{2MoS$J@0_^k*xpuDL8B(Fc^sGo&OFzjx`jEidf6S;h zENTze3V`Ua4kTpnY_(dgG&-f4Jb8UQI;x*CqC<vZMLj)_&_*6PZF-{}tyZ6H4Vz9r z>W%6ievIh)>dk|rIs##^kY67ib8Kw%#-oVFaXG7$ERyA9(NSJUvWiOA5H(!{uOpcW zg&-?iqPhds%3%tFspHDqqr;A!e@B#iPQjHd=c>N1LoOEGRehVoPOdxJ>b6>yc#o#+ zl8s8!(|NMeqjsy@0x{8^j0d00SqRZjp{Kj)&4UHYGxG+z9b-)72I*&J70?+8e?p_@ z=>-(>l6z5vYlP~<2%DU02b!mA{7mS)NS_eLe=<xzM?bHjUHTM)uwXrIe<HT;s9Ae% z=7AZ#2zGQnY>t)sm&+Pmk?asOEKlkPQ)EUvvfC=;4M&*|I!w}(@V_)eUKLA_t^%`o z0PM9LV|UKTLn<KtS!oVFL)Q?{mT|@_FU$^-=?e7A^ee*ttH|7QwB8Lh$Ak3i&={ey z4+SMmFH1;#j$T3N&fB6&fAAb~ba_bVrJ^k<<~PyLx%#jQEs@1^*Y_0sQ1Z9v^BTQM zzbz-Di>k|?M3u!|f2S0?UqZsEIH9*NJS-8lzu;A6-rr-ot=dg9SASoluZUkFH$7X; zP=?kYX!K?JL-b~<#7wU;b;eS)O;@?h%sPPk{4xEBxb{!sm0AY|f9cNvx6>$3F!*0c z75H=dy8JvTyO8}g1w{$9T$p~5en}AeSLoCF>_RT9YPMpChUjl310o*$Qocj<cT9{r z{SUb7yw8~+eM{OAdn8QXmU#Ln`e$U@gLrUCREOwaE9Fi3=+LNRpVo&2-v188V4HG5 z4by)LRQ`khtGXQSf3FJU{{cUGNIWPFFEct{U|ELOdH7(z3amvCe*k&Q@=9;erLneI zoel2CfCMiPTmYnjjxjV!Ar1h1yQ-31h=b@RZt-play?)#cs=ZxOt;5oX)|*e=7k*A zSmQ;rO4_`=Z&gX-C2$fitvq+iGK1U*^*#IW!Bo{nON%KSf4GdBHE!bNGq<IJd>bH& z<S(_vDm{acP-pGGxdurqd6mWyUX2uh=Si>bnwg#gssR#jDVN{uEi3n(PZ%PFZ|6J2 z5_rBf0-u>e4sFe0*Km49ATi7>Kn0f9!uc|rRMR1Dtt6m1LW8^>qFlo}h$@br=Rmpi z;mI&>OF64Be{dVeHI8utrh)v^wsZ0jii%x8UgZ8TC%K~@I(4E};GFW&(;WVov}3%H zH;IhRkfD^(vt^DjZz(MyHLZxv8}qzPc(%itBkBwf_fC~sDBgh<3XAv5cxxfF3<2U! z03Xe&z`is!JDHbe;mNmfkH+_LFE*I2^mdL@7(@9DfAcP6O04V-ko;Rpgp<%Cj5r8Z zd0`sXoIjV$j)--;jA6Zy^D5&5v$o^>e%>Q?9GLm{i~p^lAn!%ZtF$I~>39XVZxk0b zROh^Bk9cE0AJBLozZIEmy7xG(yHWGztvf<IsL>nr0(2ro1%>zsGMS^EMu+S$r=_;9 zWwZkgf7Q7`H9sLf2Go^Xy6&h~a&<Ho;zy;ut<fA;NAzP7(RdB{@@`v*GfeSYLv=cf zmTC<f(3^*m5~o9A&_)%lVDe@XW#mnNpPfZAT#_;^V_zXZWH^UJ6m3LR2*TSwYLLJG z;HyFY`lD!=7J&u<gT=H2Ir9WY>%s2_T@_Csf19MntF$aVFiFkvE3_hUg(B@&Xw@YJ zpL$wNYf78=0c@!QU6_a$>CPiXT7QAGDM}7Z(0z#_ZA=fmLUj{2z7@Ypo71UDy8GHr z-&TLKf6a5WCf@Adl<p1`sp~vxi3mP+969Ibi5ssa2I4Q#TbRyM)c;uurU!iOgN?oM ze<^x!;41A&r#L=Idnf3_-~s~t7pvI@=dg{%eJ|0G1?Y9wVt#Epor*W6C+T4*d!Awm zb^e`+t8`2hd<5gi(y5neN#dISS*lO?HcgP9U#UJ~XwDIf)F93nBt8WbF`vY59QLk* zjStWQJkES{3dVXbto#gSCt&^8;FoX1f1EeKS5FzrmW^76b@AL6+Fv36rN-eY%I&*K zR=V4tn54HiETzwcx&slvSnPCqYuz){n2ZE`&2>e3VglBt4>Z>;xF}}-S~B7<(%B;Y z0QR55{z-buw>8ilNM3u6I+D$S%?)(p>=eBx-HpvZj{7c*_?K=d()*7<Jf=Q)f8<;M z4*62M$T^?hSEP@fhf0ZbkuJj7&!vK5l=QJ~zb`)MPYedy2kVl9jXxdnmn`&r8ut0w z>q?93us}1dq%FAFYLsW8ZTQ_XZLh`P2*6(NgS}qGcfGXVWpwsp#Rs}IuKbk*`2}&) zI^Vsk6S&Q4@oYS?dJ`NwMVBs6f57+RxdqVub#PvMu?$=^OJy5xEl0<5SLsSRy%%a0 zi}Y#1-F3m;Ieh#Y12UgW?-R)|eX>ZuF-2cc!1>~NS|XSF-6In>zBoZg+ml!6%fk7U zw0LHcz8VQk(jOJ+Yu)|^|15ufl$KQd_1eUZZzj`aC%umU6F1&D5XVWce_wAe(qCSZ zpX-QF4e{EmEVN9~6%<vpg<2Jj(N;yb(@sTq(;h`rv|rJKbWG7>bR5<t(rH-HQL+^+ z(0de>U*UT{eMHfcUo`jw*u?4r<c@DtY>2s_$`}U{?NjvEm(u&<>B|%mq$Q3weshxk z76<``8vh<Y?d9)(#1EiUf24mYvnG!>{+nX`@9CB6IE&z)I%IFjR^LH{s1p|eppv=x za(g_jLU|xjWMAn-V7th$<OiF$t|Usi>f({|LG8zzIE0g?cyW;%Dmtv%C+0@xVxPE^ zyZzi9P%JAD6ynwHptuzP`Kox7*9h7XSMonCalv;Md0i9Vb-c*!f0ubfk?&T&T}AHh z4m8Bz{JllKcdNg?D^%a5MFQ;#1z|*}H^qHLzW)L}wp?2tY7RejtSh8<;Zw)QGJYUm z|MbTxyj*McKlStlT9I5XlSWtQGN&-LTr2XyNU+`490rg?LYLMRnz-@oKqT1hpCGqP zyRXt4=_Woj$%n5ee<Hz1AbFV|YT~!y`W4tA4~7zAsyNJf^vES-?=1teP3#7{Ht{2{ zC=voUH1TnKCe;;(nmAjsbbOLWu2)NbnL+~hvk||1!7H!FuTz9Z=mZb3spzBdZJy4k zu}~SGp(l79#zI$P{0@3vjfKki1^R><3zhLF>5>`?m9a#xQH+Jk_+|RM8Vi;2*XbK- zEL6sCpaGPzP>k8f4Kh|##_imt#zJMB;ir|JrMPGW`rityK1vHXMLy18%qmMQAm4WZ zP)i30KR&5vs15)C+8dM66&$k~i|ZT;KR&5vs15)C+8dJ(sAmGPijyIz6_bsqKLSFH zlOd=TljEpH0>h4zA*dCTK&emy#FCRCs1=i^sZ9bFmXjf<6_X39E(XY)00000#N437 diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties index dedd5d1e6..d9fbee2e1 100644 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ b/backend/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/backend/gradlew b/backend/gradlew index f5feea6d6..f3b75f3b0 100755 --- a/backend/gradlew +++ b/backend/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/backend/gradlew.bat b/backend/gradlew.bat index 9d21a21834d5195c278ba17baec3115b2aaab06e..9b42019c7915b971238526075306ffba3b666dd5 100644 GIT binary patch delta 728 zcmdlXHcgz>p(r(%cOr`kJ1-Z6Y3s_!JF&?PNSx4x3;r^LF(;ccdNT4(u48lul1CZ+ zfTS=JNF;_y1+J!*NmC7K9v6_#OjA%&b#ZkHbyrogwN+A5a7azg$XCcuOXHn<fhhp4 zO_f;#&IXDz^K$V{Ud(I@H2eXxq8O0JRh(L);NltN=jiKdt6G;*V8A<BgheqBVwXZl zMrN@B5as0;<tFCjR4SC_l@_O_K(sjq1jIWz2D`>XHRl;1#6A2$=0Ocs1zFq3Vh6N# zKZ^m_5g%BJfV?DDO>vMTK;fj|mYJ8Lkd;`LsFzxi$~$>Jt3Fuec~&t6UM^KH$1ulu z*9cct1>LgLqT<Z_JOw+yP#*;&J2gYz$+tOWCd;$2^Mecng@m4hp`Dr$?_?vkNM2Nt zAMCzBi(NVNTtRBn^Gos-(h@UsKpd+ypbNZ!4sa{V&jryQ`MIeOFM(}`_zBLmQnFR$ zogBcf26kpSyE2CD%h=s<*)D}?JJ8q3r6oY0cW}snja<rM4ORvXA6JOHf_qV7N)Axj zH;zS|U}vZ1rSMLk#|etn+nmZkkAS=a^3!Biu27(06qgc^tl^RdlGC|Vf#e=86CnA4 kO9bdVpyJ%b%sf|+ArF`(MS!N|<R>RWLIfB9>505t0M)3ywg3PC delta 593 zcmZusOG_J36lO9nb5aV$niPEWX3`N|G{zz>EE5u&n}^^7QAt-}bnfW1nVC9Cif&vM zy9`JC0qwqvuDa+CaMRNMh0<;QfGh7jC{=_D_k4Wkdz{PMx2#kNm^+l0WQc|e9e$_} ze>{&N3c_+Sm4z0b3l01wY~a&W3!T)ngt<t~&qZQ@ienQ^uhG@rhOQZgrjZIeIP!^a z+tCKWW3j0g3K75+aV`@J!{~?;Ifvgx1FyyWI6#l}Nv*ZLQC+8oerCNZ;i8nEj;(f% zoF3uN^8?rHc2CK`8}yik$5QS|B&=?2b())@GO^GBKTBo&Bb5Ze2|2)XdHyjk9fsE( z&mwQk_hyluFual%a&h`vb+6i?ZEF1E9w{?eQVI_M!qbXmu{`?%_m$U~|M8Wwj#;&+ z0UY{$pV+3;1z53p$QsY62fiErP2XksR-ML=>MXDHRlVc-S8d$qI&s^T7d{yD8U7rb z;wGc9msv=TqA^1nfoXLauBJaJyan?t9Hk9@#MDFAbUf-=VK-h8+pvpYK*QfKi6hW) lJhL<d?8NDlg?*yac5A2eYO_XlzMm-3$>h@<@=Fd)xB-!_uB-q6 diff --git a/backend/inspection/build.gradle b/backend/inspection/build.gradle index da4de001f..3101df0cd 100644 --- a/backend/inspection/build.gradle +++ b/backend/inspection/build.gradle @@ -36,6 +36,7 @@ dependencies { testImplementation testFixtures(project(':lib-document-generator')) testImplementation testFixtures(project(':lib-xlsx-import')) testImplementation testFixtures(project(':lib-statistics')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/inspection/gradle.lockfile b/backend/inspection/gradle.lockfile index 061d1161c..548afc417 100644 --- a/backend/inspection/gradle.lockfile +++ b/backend/inspection/gradle.lockfile @@ -114,10 +114,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -213,10 +213,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -236,16 +236,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/inspection/openApi.json b/backend/inspection/openApi.json index 75e26b9b8..1d42add9f 100644 --- a/backend/inspection/openApi.json +++ b/backend/inspection/openApi.json @@ -4686,18 +4686,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" 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 index 3b31911a6..cc2b45c03 100644 --- a/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java +++ b/backend/inspection/src/main/java/de/eshg/inspection/importer/InspectionProcedureRowReader.java @@ -57,7 +57,7 @@ class InspectionProcedureRowReader extends RowReader<InspectionImporterRow, Insp List<InspectionListColumn> actualColumns, ImportPersister importPersister, Clock clock) { - super(sheet, actualColumns, InspectionImporterRow::new); + super(sheet, actualColumns, InspectionImporterRow::new, clock); this.importPersister = importPersister; this.clock = clock; } @@ -120,7 +120,7 @@ class InspectionProcedureRowReader extends RowReader<InspectionImporterRow, Insp 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); + String phonenumber = cellAsString(col, CONTACT_PHONENUMBER, true, true, errorHandler); // If every contact field is empty then don't create a contact if (salutation == null diff --git a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionTestHelperController.java b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionTestHelperController.java index 0fe6f8ba0..476c2be88 100644 --- a/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionTestHelperController.java +++ b/backend/inspection/src/main/java/de/eshg/inspection/testhelper/InspectionTestHelperController.java @@ -5,7 +5,7 @@ package de.eshg.inspection.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.inspection.checklist.persistence.ChecklistRepository; import de.eshg.inspection.feature.InspectionFeature; import de.eshg.inspection.feature.InspectionFeatureToggle; @@ -13,7 +13,6 @@ import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.TestHelperController; import de.eshg.testhelper.environment.EnvironmentConfig; -import java.io.IOException; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @@ -23,7 +22,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class InspectionTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final AuditLogTestHelperService auditLogTestHelperService; private final InspectionFeatureToggle inspectionFeatureToggle; @@ -42,13 +41,8 @@ public class InspectionTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } @PostExchange("/enabled-new-features/{featureToEnable}") diff --git a/backend/inspection/src/main/resources/migrations/0064_add_auditlog_entry.xml b/backend/inspection/src/main/resources/migrations/0064_add_auditlog_entry.xml new file mode 100644 index 000000000..123233c56 --- /dev/null +++ b/backend/inspection/src/main/resources/migrations/0064_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/inspection/src/main/resources/migrations/changelog.xml b/backend/inspection/src/main/resources/migrations/changelog.xml index 6c3a85a96..0a5a6f7af 100644 --- a/backend/inspection/src/main/resources/migrations/changelog.xml +++ b/backend/inspection/src/main/resources/migrations/changelog.xml @@ -77,5 +77,6 @@ <include file="migrations/0061_add_oms_task_type.xml"/> <include file="migrations/0062_add_cemetery_delete_at.xml"/> <include file="migrations/0063_add_previous_file_state_id_to_system_progress_entry.xml"/> + <include file="migrations/0064_add_auditlog_entry.xml"/> </databaseChangeLog> diff --git a/backend/keycloak-api/gradle.lockfile b/backend/keycloak-api/gradle.lockfile index f4c5880ce..c46c7009b 100644 --- a/backend/keycloak-api/gradle.lockfile +++ b/backend/keycloak-api/gradle.lockfile @@ -11,8 +11,8 @@ com.sun.istack:istack-commons-runtime:4.1.2=compileClasspath,productionRuntimeCl com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.swagger:swagger-annotations:1.6.15=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath 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 @@ -40,10 +40,10 @@ org.glassfish.jaxb:jaxb-core:4.0.5=compileClasspath,productionRuntimeClasspath,r org.glassfish.jaxb:jaxb-runtime:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.glassfish.jaxb:txw2:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -56,9 +56,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/keycloak/gradle.lockfile b/backend/keycloak/gradle.lockfile index 8ae82fcd0..d0d7f06cc 100644 --- a/backend/keycloak/gradle.lockfile +++ b/backend/keycloak/gradle.lockfile @@ -75,8 +75,8 @@ io.opentelemetry:opentelemetry-sdk-metrics:1.43.0=compileClasspath io.opentelemetry:opentelemetry-sdk-trace:1.43.0=compileClasspath io.opentelemetry:opentelemetry-sdk:1.43.0=compileClasspath io.quarkus.arc:arc:3.15.1=compileClasspath -io.quarkus.resteasy.reactive:resteasy-reactive-common-types:3.17.7=compileClasspath -io.quarkus.resteasy.reactive:resteasy-reactive-common:3.17.7=compileClasspath +io.quarkus.resteasy.reactive:resteasy-reactive-common-types:3.18.0=compileClasspath +io.quarkus.resteasy.reactive:resteasy-reactive-common:3.18.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 @@ -114,7 +114,7 @@ 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:mutiny:2.7.0=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 @@ -165,10 +165,10 @@ 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.11.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.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging-annotations:3.0.1.Final=compileClasspath org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath org.jboss.logmanager:jboss-logmanager:3.0.6.Final=compileClasspath @@ -195,9 +195,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/date-of-birth-access-code.ftl b/backend/keycloak/resources/themes/custom-keycloak/login/date-of-birth-access-code.ftl new file mode 100644 index 000000000..622eeac6b --- /dev/null +++ b/backend/keycloak/resources/themes/custom-keycloak/login/date-of-birth-access-code.ftl @@ -0,0 +1,61 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "title"> + + ${msg("loginTitle",(realm.displayName!''))} + + <#elseif section = "header"> + + ${msg("accessCodePageTitle")} + + <div id="page-title-info-text"> + ${msg(context_info!'')} + </div> + + <#elseif section = "form"> + + <div id="kc-form"> + <div id="kc-form-wrapper"> + <form id="kc-form-login" class="form" onsubmit="return true;" action="${url.loginAction}" method="post" autocomplete="off"> + + <div class="${properties.kcFormGroupClass!}"> + <label for="access-code" class="${properties.kcLabelClass!}"> + ${msg("accessCodeLabel")} + </label> + <input tabindex="1" id="access-code" class="${properties.kcInputClass!}" name="access_code" type="text" + value="${(access_code!'')}" <#if access_code?exists>readonly</#if> + aria-invalid="<#if messagesPerField.existsError('access_code')>true</#if>" + /> + <#if messagesPerField.existsError('access_code')> + <span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite"> + ${kcSanitize(messagesPerField.getFirstError('access_code'))?no_esc} + </span> + </#if> + </div> + + <div class="${properties.kcFormGroupClass!}"> + <label for="date-of-birth" class="${properties.kcLabelClass!}"> + ${msg("dateOfBirthLabel")} + </label> + <input tabindex="2" id="date-of-birth" class="${properties.kcInputClass!}" name="date_of_birth" type="date" + aria-invalid="<#if messagesPerField.existsError('access_code')>true</#if>" + /> + <#if messagesPerField.existsError('access_code')> + <span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite"> + ${kcSanitize(messagesPerField.getFirstError('access_code'))?no_esc} + </span> + </#if> + </div> + + <div id="kc-form-buttons" class="${properties.kcFormGroupClass!}"> + <input tabindex="3" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" + name="login" id="kc-login" type="submit" value="${msg("doSubmit")}" + /> + </div> + + </form> + </div> + </div> + + </#if> +</@layout.registrationLayout> diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_de.properties b/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_de.properties index 6d60540d5..ef80a3a1d 100644 --- a/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_de.properties +++ b/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_de.properties @@ -1,14 +1,23 @@ accessCodePageTitle=Anmelden mit Anmeldecode accessCodeLabel=Anmeldecode dateOfBirthLabel=Geburtsdatum +pinLabel=PIN missingAccessCode=Bitte geben Sie einen Anmeldecode ein. missingDateOfBirth=Bitte geben Sie das Geburtsdatum ein. -invalidAccessCodeCredentials=Ungültiger Anmeldecode oder Geburtsdatum. +missingPin=Bitte geben Sie Ihre geheime PIN ein. +invalidAccessCodeOrDateOfBirth=Ungültiger Anmeldecode oder Geburtsdatum. +invalidAccessCodeOrPin=Ungültiger Anmeldecode oder PIN. accountLockedOut=Zu viele fehlgeschlagene Anmeldeversuche. Bitte versuchen Sie es in 6 Stunden erneut. +defaultDateOfBirthPageTitleInfo=Geben Sie den Anmeldecode zusammen mit dem zweiten Faktor an. +esuDateOfBirthPageTitleInfo=Geben Sie den Anmeldecode aus dem Einladungsbrief zusammen mit dem Geburtsdatum des Kindes an. +tmDateOfBirthPageTitleInfo=Geben Sie den Anmeldecode aus der Bestätigungsemail zusammen mit Ihrem Geburtsdatum an. +pinPageTitleInfo=Bitte geben Sie den Anmeldecode sowie Ihre persönliche 6-stellige, nur numerische PIN ein. -access-code-display-name=Anmeldecode und Geburtsdatum -access-code-help-text=Anmelden, indem Sie den Ihnen zugesandten Anmeldecode verwenden und Ihr Geburtsdatum eingeben. +date-of-birth-access-code-display-name=Anmeldecode und Geburtsdatum +date-of-birth-access-code-help-text=Anmelden, indem Sie den Ihnen zugesandten Anmeldecode verwenden und Ihr Geburtsdatum eingeben. +pin-access-code-display-name=Anmeldecode und PIN +pin-access-code-help-text=Anmelden, indem Sie den Ihnen gegebenen Anmeldecode verwenden und Ihren geheime PIN eingeben. loginAccountTitle=Anmelden noAccount=Noch keinen Account? diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_en.properties b/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_en.properties index 1cbfe5db1..730ddcfab 100644 --- a/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_en.properties +++ b/backend/keycloak/resources/themes/custom-keycloak/login/messages/messages_en.properties @@ -1,13 +1,22 @@ accessCodePageTitle=Login with Access Code accessCodeLabel=Access Code dateOfBirthLabel=Date of Birth +pinLabel=PIN missingAccessCode=Missing access code missingDateOfBirth=Missing date of birth +missingPin=Missing PIN invalidAccessCodeCredentials=Invalid access code or date of birth accountLockedOut=Too many failed login attempts, try again in 6 hours. +defaultAccessCodeTitleInfo=Enter the access code. +esuDateOfBirthPageTitleInfo=Enter the access code and the birthday of the child. +tmDateOfBirthPageTitleInfo=Enter the access code and your birthday. +pinPageTitleInfo=Enter the access code and the PIN. + +date-of-birth-access-code-display-name=Access Code and Birthday +date-of-birth-access-code-help-text=Sign in by entering an access code provided to you and your birthday. +pin-access-code-display-name=Access Code and PIN +pin-access-code-help-text=Sign in by entering an access code provided to you and your secret PIN. -access-code-display-name=Access Code and Birthday -access-code-help-text=Sign in by entering an access code provided to you and your birthday. logoutConfirmHeader=Important note: To ensure that no sensible data is revealed, you must close the browser entirely after successful logout. diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/access-code.ftl b/backend/keycloak/resources/themes/custom-keycloak/login/pin-access-code.ftl similarity index 67% rename from backend/keycloak/resources/themes/custom-keycloak/login/access-code.ftl rename to backend/keycloak/resources/themes/custom-keycloak/login/pin-access-code.ftl index d14b21075..74e75bd5d 100644 --- a/backend/keycloak/resources/themes/custom-keycloak/login/access-code.ftl +++ b/backend/keycloak/resources/themes/custom-keycloak/login/pin-access-code.ftl @@ -8,6 +8,10 @@ ${msg("accessCodePageTitle")} + <div id="page-title-info-text"> + ${msg(context_info!'')} + </div> + <#elseif section = "form"> <div id="kc-form"> @@ -29,19 +33,19 @@ </#if> </div> - <div class="${properties.kcFormGroupClass!}"> - <label for="date-of-birth" class="${properties.kcLabelClass!}"> - ${msg("dateOfBirthLabel")} - </label> - <input tabindex="2" id="date-of-birth" class="${properties.kcInputClass!}" name="date_of_birth" type="date" - aria-invalid="<#if messagesPerField.existsError('access_code')>true</#if>" - /> - <#if messagesPerField.existsError('access_code')> - <span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite"> - ${kcSanitize(messagesPerField.getFirstError('access_code'))?no_esc} - </span> - </#if> - </div> + <div class="${properties.kcFormGroupClass!}"> + <label for="pin" class="${properties.kcLabelClass!}"> + ${msg("pinLabel")} + </label> + <input tabindex="2" id="pin" class="${properties.kcInputClass!}" name="pin" type="password" maxlength="6" + aria-invalid="<#if messagesPerField.existsError('access_code')>true</#if>" + /> + <#if messagesPerField.existsError('access_code')> + <span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite"> + ${kcSanitize(messagesPerField.getFirstError('access_code'))?no_esc} + </span> + </#if> + </div> <div id="kc-form-buttons" class="${properties.kcFormGroupClass!}"> <input tabindex="3" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" diff --git a/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css b/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css index 4e6e15793..05aada898 100644 --- a/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css +++ b/backend/keycloak/resources/themes/custom-keycloak/login/resources/css/custom-login.css @@ -207,3 +207,10 @@ a { position: initial; color: var(--global--color-primary); } + +#page-title-info-text { + font-size: 16px; + font-weight: normal; + line-height: 24px; + margin-top: 24px; +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeAwareAuthenticationContextBean.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeAwareAuthenticationContextBean.java new file mode 100644 index 000000000..6530b94a7 --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeAwareAuthenticationContextBean.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import org.keycloak.forms.login.freemarker.model.AuthenticationContextBean; + +public class AccessCodeAwareAuthenticationContextBean extends AuthenticationContextBean { + + public AccessCodeAwareAuthenticationContextBean() { + super(null, null); + } + + @Override + public boolean showTryAnotherWayLink() { + return false; + } + + @Override + public boolean showUsername() { + return false; + } + + @Override + public boolean showResetCredentials() { + return false; + } +} 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 c566f6de6..574943c6f 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 @@ -8,13 +8,13 @@ package de.eshg.keycloak.authenticator; import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError; import de.eshg.keycloak.api.user.KeycloakAttributes; -import de.eshg.keycloak.credentialprovider.DateOfBirthCredentialModel; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import org.jboss.logging.Logger; import org.keycloak.authentication.AbstractFormAuthenticator; import org.keycloak.authentication.AuthenticationFlowContext; @@ -23,27 +23,20 @@ import org.keycloak.events.Errors; import org.keycloak.forms.login.LoginFormsProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.FormMessage; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.utils.StringUtil; -public class AccessCodeForm extends AbstractFormAuthenticator { +public abstract class AccessCodeForm extends AbstractFormAuthenticator { private static final Logger logger = Logger.getLogger(AccessCodeForm.class); - public static final String FORM_TEMPLATE = "access-code.ftl"; public static final String ACCESS_CODE_QUERY_PARAMETER = "access_code"; - + public static final String CONTEXT_INFO_QUERY_PARAMETER = "context_info"; public static final String ACCESS_CODE_FIELD = "access_code"; - public static final String DATE_OF_BIRTH_FIELD = "date_of_birth"; - public static final String MISSING_ACCESS_CODE_MESSAGE = "missingAccessCode"; - public static final String MISSING_DATE_OF_BIRTH_MESSAGE = "missingDateOfBirth"; - public static final String INVALID_ACCESS_CODE_CREDENTIALS_MESSAGE = - "invalidAccessCodeCredentials"; public static final String ACCOUNT_LOCKED_OUT_MESSAGE = "accountLockedOut"; @Override @@ -54,11 +47,11 @@ public class AccessCodeForm extends AbstractFormAuthenticator { // Only show the authenticator if prompted by query parameter or auth note set if (Objects.equals( - queryParameters.getFirst(OIDCLoginProtocol.PROMPT_PARAM), ACCESS_CODE_QUERY_PARAMETER) + queryParameters.getFirst(OIDCLoginProtocol.PROMPT_PARAM), getAuthenticatorPrompt()) || Objects.equals( - authSession.getAuthNote(OIDCLoginProtocol.PROMPT_PARAM), ACCESS_CODE_QUERY_PARAMETER)) { + authSession.getAuthNote(OIDCLoginProtocol.PROMPT_PARAM), getAuthenticatorPrompt())) { - authSession.setAuthNote(OIDCLoginProtocol.PROMPT_PARAM, ACCESS_CODE_QUERY_PARAMETER); + authSession.setAuthNote(OIDCLoginProtocol.PROMPT_PARAM, getAuthenticatorPrompt()); // If an access code was provided in the initial rendering of the page // add it to the formData @@ -66,12 +59,21 @@ public class AccessCodeForm extends AbstractFormAuthenticator { String accessCode = queryParameters.getFirst(ACCESS_CODE_QUERY_PARAMETER); formData.add(ACCESS_CODE_FIELD, accessCode); } + applyCustomFormTemplateValues(context); + addContextInfo(context, formData); challenge(context, formData, Map.of()); } else { context.attempted(); } } + protected abstract void addContextInfo( + AuthenticationFlowContext context, MultivaluedMap<String, String> formData); + + protected abstract String getAuthenticatorPrompt(); + + protected abstract String getInvalidCredentialsMessage(); + /* * This processes the form submission and adds error handling to the same form on failure * or delegates to the next authenticators and required actions on success @@ -80,14 +82,11 @@ public class AccessCodeForm extends AbstractFormAuthenticator { public void action(AuthenticationFlowContext context) { MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters(); Map<String, String> errors = new HashMap<>(); - String accessCode = formData.getFirst(ACCESS_CODE_FIELD).replaceAll("\\s+", ""); - if (StringUtil.isBlank(accessCode)) { - errors.put(ACCESS_CODE_FIELD, MISSING_ACCESS_CODE_MESSAGE); - } - if (StringUtil.isBlank(formData.getFirst(DATE_OF_BIRTH_FIELD))) { - errors.put(DATE_OF_BIRTH_FIELD, MISSING_DATE_OF_BIRTH_MESSAGE); - } + applyCustomFormTemplateValues(context); + addContextInfo(context, formData); + validateAccesCodeField(accessCode, errors); + validateCredentialField(formData, errors); if (errors.isEmpty()) { getUserByAccessCodeAttribute(context, accessCode) @@ -96,17 +95,7 @@ public class AccessCodeForm extends AbstractFormAuthenticator { // Check if the user is locked out String bruteForceError = getDisabledByBruteForceEventError(context, user); boolean isDisabled = bruteForceError != null; - // Is submitted in the format yyyy-mm-dd from the html input tag with type=date - String dateOfBirth = formData.getFirst(DATE_OF_BIRTH_FIELD); - if (!isDisabled - && (user.credentialManager() - .isValid( - new UserCredentialModel( - null, DateOfBirthCredentialModel.TYPE, dateOfBirth)) - // Todo(ISSUE-7041): Remove. For accounts in prod that still save the date - // of birth as attribute - || dateOfBirth.equals( - user.getFirstAttribute(KeycloakAttributes.DATE_OF_BIRTH_ATTRIBUTE)))) { + if (!isDisabled && validateCredentials(formData, user)) { context.setUser(user); context.success(); } else { @@ -117,14 +106,14 @@ public class AccessCodeForm extends AbstractFormAuthenticator { if (isDisabled) { errors.put(ACCESS_CODE_FIELD, ACCOUNT_LOCKED_OUT_MESSAGE); } else { - errors.put(ACCESS_CODE_FIELD, INVALID_ACCESS_CODE_CREDENTIALS_MESSAGE); + errors.put(ACCESS_CODE_FIELD, getInvalidCredentialsMessage()); } } }, () -> { // Invalid access code, but display the same error formData.remove(ACCESS_CODE_FIELD); - errors.put(ACCESS_CODE_FIELD, INVALID_ACCESS_CODE_CREDENTIALS_MESSAGE); + errors.put(ACCESS_CODE_FIELD, getInvalidCredentialsMessage()); }); } @@ -133,6 +122,31 @@ public class AccessCodeForm extends AbstractFormAuthenticator { } } + private void validateAccesCodeField(String accessCode, Map<String, String> errors) { + if (StringUtil.isBlank(accessCode)) { + errors.put(ACCESS_CODE_FIELD, MISSING_ACCESS_CODE_MESSAGE); + } + } + + private void applyCustomFormTemplateValues(AuthenticationFlowContext context) { + LoginFormsProvider form = context.form(); + form.setAttributeMapper( + new Function<Map<String, Object>, Map<String, Object>>() { + @Override + public Map<String, Object> apply(Map<String, Object> attributes) { + attributes.computeIfPresent( + "auth", (key, bean) -> new AccessCodeAwareAuthenticationContextBean()); + return attributes; + } + }); + } + + protected abstract void validateCredentialField( + MultivaluedMap<String, String> formData, Map<String, String> errors); + + protected abstract boolean validateCredentials( + MultivaluedMap<String, String> formData, UserModel user); + /* * This creates the custom code form which asks for an access code and a second authentication information * The access code is prefilled if it was previously included in the url and was a valid one where a use lookup was successful @@ -148,17 +162,15 @@ public class AccessCodeForm extends AbstractFormAuthenticator { .filter(name -> StringUtil.isNotBlank(formData.getFirst(name))) .forEach(name -> form.setAttribute(name, formData.getFirst(name))); errors.forEach((field, message) -> form.addError(new FormMessage(field, message))); - context.challenge(form.createForm(FORM_TEMPLATE)); + context.challenge(form.createForm(getFormTemplate())); } static void logFailedAttemptOnUser( AuthenticationFlowContext context, UserModel user, String bruteForceError) { context.getEvent().user(user); - if (bruteForceError != null) { - context.getEvent().error(bruteForceError); - } else { - context.getEvent().error(Errors.INVALID_USER_CREDENTIALS); - } + context + .getEvent() + .error(Objects.requireNonNullElse(bruteForceError, Errors.INVALID_USER_CREDENTIALS)); RealmModel realm = context.getRealm(); if (realm.isBruteForceProtected()) { context @@ -198,4 +210,6 @@ public class AccessCodeForm extends AbstractFormAuthenticator { @Override public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {} + + protected abstract String getFormTemplate(); } diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeFormFactory.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeFormFactory.java index c10afd316..01288a48c 100644 --- a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeFormFactory.java +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/AccessCodeFormFactory.java @@ -7,21 +7,16 @@ package de.eshg.keycloak.authenticator; import java.util.List; import org.keycloak.Config; -import org.keycloak.authentication.Authenticator; import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.models.AuthenticationExecutionModel; -import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.ProviderConfigProperty; -public class AccessCodeFormFactory implements AuthenticatorFactory { - - public static final String PROVIDER_ID = "access-code"; - static AccessCodeForm SINGLETON = new AccessCodeForm(); +public abstract class AccessCodeFormFactory implements AuthenticatorFactory { @Override public String getDisplayType() { - return "Access Code Form"; + return "%s Access Code Form".formatted(getFormattedCredentialType()); } @Override @@ -46,7 +41,8 @@ public class AccessCodeFormFactory implements AuthenticatorFactory { @Override public String getHelpText() { - return "Validates a code and a second identity factory from a form"; + return "Validates a code and a %s credential from a form" + .formatted(getFormattedCredentialType()); } @Override @@ -54,11 +50,6 @@ public class AccessCodeFormFactory implements AuthenticatorFactory { return null; } - @Override - public Authenticator create(KeycloakSession session) { - return SINGLETON; - } - @Override public void init(Config.Scope config) {} @@ -70,6 +61,10 @@ public class AccessCodeFormFactory implements AuthenticatorFactory { @Override public String getId() { - return PROVIDER_ID; + return getCredentialType() + "-access-code"; } + + public abstract String getCredentialType(); + + public abstract String getFormattedCredentialType(); } diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeForm.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeForm.java new file mode 100644 index 000000000..0968dd8d6 --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeForm.java @@ -0,0 +1,82 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import de.eshg.keycloak.api.user.KeycloakAttributes; +import de.eshg.keycloak.credentialprovider.DateOfBirthCredentialModel; +import jakarta.ws.rs.core.MultivaluedMap; +import java.util.Map; +import java.util.Objects; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.utils.StringUtil; + +public class DateOfBirthAccessCodeForm extends AccessCodeForm { + + public static final String FORM_TEMPLATE = "date-of-birth-access-code.ftl"; + public static final String DATE_OF_BIRTH_FIELD = "date_of_birth"; + public static final String MISSING_DATE_OF_BIRTH_MESSAGE = "missingDateOfBirth"; + public static final String INVALID_CREDENTIALS_MESSAGE = "invalidAccessCodeOrDateOfBirth"; + public static final String AUTHENTICATION_PROMPT = DateOfBirthCredentialModel.TYPE; + + @Override + protected void addContextInfo( + AuthenticationFlowContext context, MultivaluedMap<String, String> formData) { + MultivaluedMap<String, String> queryParameters = context.getUriInfo().getQueryParameters(); + AuthenticationSessionModel authSession = context.getAuthenticationSession(); + String contextInfoKey = + Objects.requireNonNullElse( + queryParameters.getFirst(CONTEXT_INFO_QUERY_PARAMETER), + Objects.requireNonNullElse( + authSession.getAuthNote(CONTEXT_INFO_QUERY_PARAMETER), "default")); + + String contextInfoQueryParameter = + switch (contextInfoKey) { + case "esu" -> "esuDateOfBirthPageTitleInfo"; + case "tm" -> "tmDateOfBirthPageTitleInfo"; + default -> "defaultDateOfBirthPageTitleInfo"; + }; + formData.add(CONTEXT_INFO_QUERY_PARAMETER, contextInfoQueryParameter); + + authSession.setAuthNote(CONTEXT_INFO_QUERY_PARAMETER, contextInfoKey); + } + + @Override + protected String getAuthenticatorPrompt() { + return AUTHENTICATION_PROMPT; + } + + @Override + protected String getInvalidCredentialsMessage() { + return INVALID_CREDENTIALS_MESSAGE; + } + + @Override + protected void validateCredentialField( + MultivaluedMap<String, String> formData, Map<String, String> errors) { + if (StringUtil.isBlank(formData.getFirst(DATE_OF_BIRTH_FIELD))) { + errors.put(DATE_OF_BIRTH_FIELD, MISSING_DATE_OF_BIRTH_MESSAGE); + } + } + + @Override + protected boolean validateCredentials(MultivaluedMap<String, String> formData, UserModel user) { + // Is submitted in the format yyyy-mm-dd from the html input tag with type=date + String dateOfBirth = formData.getFirst(DATE_OF_BIRTH_FIELD); + return user.credentialManager() + .isValid(new UserCredentialModel(null, DateOfBirthCredentialModel.TYPE, dateOfBirth)) + // Todo(ISSUE-7041): Remove. For accounts in prod that still save the date + // of birth as attribute + || dateOfBirth.equals(user.getFirstAttribute(KeycloakAttributes.DATE_OF_BIRTH_ATTRIBUTE)); + } + + @Override + protected String getFormTemplate() { + return FORM_TEMPLATE; + } +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeFormFactory.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeFormFactory.java new file mode 100644 index 000000000..14d074871 --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DateOfBirthAccessCodeFormFactory.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import de.eshg.keycloak.credentialprovider.DateOfBirthCredentialModel; +import org.keycloak.authentication.Authenticator; +import org.keycloak.models.KeycloakSession; + +public class DateOfBirthAccessCodeFormFactory extends AccessCodeFormFactory { + + static DateOfBirthAccessCodeForm SINGLETON = new DateOfBirthAccessCodeForm(); + + @Override + public String getCredentialType() { + return DateOfBirthCredentialModel.TYPE; + } + + @Override + public String getFormattedCredentialType() { + return "Date of Birth"; + } + + @Override + public Authenticator create(KeycloakSession session) { + return SINGLETON; + } +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DeprecatedAccessCodeFormFactory.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DeprecatedAccessCodeFormFactory.java new file mode 100644 index 000000000..8807c6663 --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/DeprecatedAccessCodeFormFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import java.util.List; +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +// ToDo: remove after all environments migrated to new dob-authenticator +@Deprecated(since = "forever") +public class DeprecatedAccessCodeFormFactory implements AuthenticatorFactory { + + private static final Logger logger = Logger.getLogger(DeprecatedAccessCodeFormFactory.class); + + @Override + public String getDisplayType() { + return "Access Code Form"; + } + + @Override + public String getReferenceCategory() { + return "Access Code"; + } + + @Override + public boolean isConfigurable() { + return false; + } + + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return false; + } + + @Override + public String getHelpText() { + return ""; + } + + @Override + public List<ProviderConfigProperty> getConfigProperties() { + return null; + } + + @Override + public Authenticator create(KeycloakSession keycloakSession) { + logger.warn("Access Code Form created from deprecated Factory!"); + return new DateOfBirthAccessCodeForm(); + } + + @Override + public void init(Config.Scope config) { + logger.warn("DeprecatedAccessCodeFormFactory initialized!"); + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + logger.warn("DeprecatedAccessCodeFormFactory post init!"); + } + + @Override + public void close() {} + + @Override + public String getId() { + return "access-code"; + } +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeForm.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeForm.java new file mode 100644 index 000000000..d8480a33e --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeForm.java @@ -0,0 +1,59 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import de.eshg.keycloak.credentialprovider.PinCredentialModel; +import jakarta.ws.rs.core.MultivaluedMap; +import java.util.Map; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.utils.StringUtil; + +public class PinAccessCodeForm extends AccessCodeForm { + + public static final String FORM_TEMPLATE = "pin-access-code.ftl"; + public static final String PIN_FIELD = "pin"; + public static final String MISSING_PIN_MESSAGE = "missingPin"; + public static final String INVALID_CREDENTIALS_MESSAGE = "invalidAccessCodeOrPin"; + public static final String AUTHENTICATION_PROMPT = PinCredentialModel.TYPE; + + @Override + protected void addContextInfo( + AuthenticationFlowContext context, MultivaluedMap<String, String> formData) { + formData.add(CONTEXT_INFO_QUERY_PARAMETER, "pinPageTitleInfo"); + } + + @Override + protected String getAuthenticatorPrompt() { + return AUTHENTICATION_PROMPT; + } + + @Override + protected String getInvalidCredentialsMessage() { + return INVALID_CREDENTIALS_MESSAGE; + } + + @Override + protected void validateCredentialField( + MultivaluedMap<String, String> formData, Map<String, String> errors) { + if (StringUtil.isBlank(formData.getFirst(PIN_FIELD))) { + errors.put(PIN_FIELD, MISSING_PIN_MESSAGE); + } + } + + @Override + protected boolean validateCredentials(MultivaluedMap<String, String> formData, UserModel user) { + String pin = formData.getFirst(PIN_FIELD); + return user.credentialManager() + .isValid(new UserCredentialModel(null, PinCredentialModel.TYPE, pin)); + } + + @Override + protected String getFormTemplate() { + return FORM_TEMPLATE; + } +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeFormFactory.java b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeFormFactory.java new file mode 100644 index 000000000..ac9a0ad90 --- /dev/null +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/authenticator/PinAccessCodeFormFactory.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.keycloak.authenticator; + +import de.eshg.keycloak.credentialprovider.PinCredentialModel; +import org.keycloak.authentication.Authenticator; +import org.keycloak.models.KeycloakSession; + +public class PinAccessCodeFormFactory extends AccessCodeFormFactory { + + static PinAccessCodeForm SINGLETON = new PinAccessCodeForm(); + + @Override + public String getCredentialType() { + return PinCredentialModel.TYPE; + } + + @Override + public String getFormattedCredentialType() { + return "PIN"; + } + + @Override + public Authenticator create(KeycloakSession session) { + return SINGLETON; + } +} diff --git a/backend/keycloak/src/main/java/de/eshg/keycloak/credentialprovider/PinCredentialProvider.java b/backend/keycloak/src/main/java/de/eshg/keycloak/credentialprovider/PinCredentialProvider.java index 66058486d..2a84a8343 100644 --- a/backend/keycloak/src/main/java/de/eshg/keycloak/credentialprovider/PinCredentialProvider.java +++ b/backend/keycloak/src/main/java/de/eshg/keycloak/credentialprovider/PinCredentialProvider.java @@ -29,6 +29,8 @@ public class PinCredentialProvider CredentialInputUpdater, CredentialInputValidator { + private static final Pattern PIN_PATTERN = Pattern.compile("^\\d{6}$"); + protected final KeycloakSession session; public PinCredentialProvider(KeycloakSession session) { @@ -85,10 +87,9 @@ public class PinCredentialProvider } private void validate(String pin) { - Pattern pattern = Pattern.compile("^\\d{6}$"); - Matcher matcher = pattern.matcher(pin); + Matcher matcher = PIN_PATTERN.matcher(pin); if (!matcher.matches()) { - throw new ModelException("PIN does not match defined pattern", pattern.pattern()); + throw new ModelException("PIN does not match defined pattern", PIN_PATTERN.pattern()); } } diff --git a/backend/keycloak/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/backend/keycloak/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 5acaae06e..4db830f41 100755 --- a/backend/keycloak/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/backend/keycloak/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -1 +1,3 @@ -de.eshg.keycloak.authenticator.AccessCodeFormFactory +de.eshg.keycloak.authenticator.DateOfBirthAccessCodeFormFactory +de.eshg.keycloak.authenticator.PinAccessCodeFormFactory +de.eshg.keycloak.authenticator.DeprecatedAccessCodeFormFactory diff --git a/backend/lib-aggregation/gradle.lockfile b/backend/lib-aggregation/gradle.lockfile index d11c2c92b..fea50cf3e 100644 --- a/backend/lib-aggregation/gradle.lockfile +++ b/backend/lib-aggregation/gradle.lockfile @@ -52,10 +52,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -90,10 +90,10 @@ org.hamcrest:hamcrest-core:2.2=testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -110,15 +110,16 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testFixturesCompil org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/lib-appointmentblock/gradle.lockfile b/backend/lib-appointmentblock/gradle.lockfile index 4fb93e1d6..c4de7f868 100644 --- a/backend/lib-appointmentblock/gradle.lockfile +++ b/backend/lib-appointmentblock/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -114,13 +114,13 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath -org.jetbrains:annotations:26.0.1=compileClasspath +org.jetbrains:annotations:26.0.2=compileClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -136,16 +136,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-appointmentblock/openApi.json b/backend/lib-appointmentblock/openApi.json index af595e4b5..22ba0626c 100644 --- a/backend/lib-appointmentblock/openApi.json +++ b/backend/lib-appointmentblock/openApi.json @@ -488,9 +488,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -819,9 +819,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockService.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockService.java index 3a950e6ab..7232301cf 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockService.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockService.java @@ -24,8 +24,8 @@ import de.eshg.rest.service.security.CurrentUserHelper; import java.time.Clock; import java.time.DayOfWeek; import java.time.Instant; -import java.time.LocalDate; import java.time.LocalTime; +import java.time.ZonedDateTime; import java.util.*; import java.util.stream.Stream; import org.springframework.data.domain.Page; @@ -267,8 +267,7 @@ public class AppointmentBlockService { AppointmentBlockGroup appointmentBlockGroup = new AppointmentBlockGroup(); appointmentBlockGroup.setType(appointmentType); appointmentBlockGroup.setParallelExaminations(request.parallelExaminations()); - appointmentBlockGroup.setSlotDurationInMinutes( - appointmentTypeConfig.getStandardDurationInMinutes()); + appointmentBlockGroup.setSlotDuration(appointmentTypeConfig.getStandardDuration()); appointmentBlockGroup.setMfas(request.mfas()); appointmentBlockGroup.setPhysicians(request.physicians()); appointmentBlockGroup.setConsultants(request.consultants()); @@ -289,35 +288,31 @@ public class AppointmentBlockService { if (block.start().isBefore(Instant.now(clock))) { throw new BadRequestException("Start of first appointment block must be in the future."); } + ZonedDateTime start = block.start().atZone(clock.getZone()); + ZonedDateTime end = block.end().atZone(clock.getZone()); + List<DayOfWeek> daysOfWeek = DayOfWeekDtoMapper.toJavaTime(block.daysOfWeek()); + return createDailyAppointmentBlocks(start, end, daysOfWeek); + } + private static List<CreateAppointmentBlockData> createDailyAppointmentBlocks( + ZonedDateTime start, ZonedDateTime end, List<DayOfWeek> daysOfWeek) { List<CreateAppointmentBlockData> result = new ArrayList<>(); - LocalDate startDate = block.start().atZone(clock.getZone()).toLocalDate(); - LocalTime startTime = block.start().atZone(clock.getZone()).toLocalTime(); - LocalTime endTime = block.end().atZone(clock.getZone()).toLocalTime(); - long totalDays = DAYS.between(block.start(), block.end()); + LocalTime endTime = end.toLocalTime(); + long totalDays = DAYS.between(start, end); for (int daysToAdd = 0; daysToAdd <= totalDays; daysToAdd++) { - LocalDate date = startDate.plusDays(daysToAdd); - List<DayOfWeek> daysOfWeek = DayOfWeekDtoMapper.toJavaTime(block.daysOfWeek()); - if (daysOfWeek.contains(date.getDayOfWeek())) { - result.add(appointmentBlockData(date, startTime, endTime)); + ZonedDateTime appointmentStart = start.plusDays(daysToAdd); + if (daysOfWeek.contains(appointmentStart.getDayOfWeek())) { + ZonedDateTime appointmentEnd = appointmentStart.with(endTime); + result.add( + new CreateAppointmentBlockData( + appointmentStart.toInstant(), appointmentEnd.toInstant())); } } return result; } - private CreateAppointmentBlockData appointmentBlockData( - LocalDate date, LocalTime startTime, LocalTime endTime) { - Instant appointmentStart = toInstant(date, startTime); - Instant appointmentEnd = toInstant(date, endTime); - return new CreateAppointmentBlockData(appointmentStart, appointmentEnd); - } - - private Instant toInstant(LocalDate date, LocalTime startTime) { - return date.atTime(startTime).atZone(clock.getZone()).toInstant(); - } - public ValidateAppointmentBlockGroupResponse validateDailyAppointmentBlocksForGroup( CreateDailyAppointmentBlockGroupRequest request) { List<UUID> usersForEvent = diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockSlotUtil.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockSlotUtil.java index feda11e73..d3dbc4dc5 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockSlotUtil.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockSlotUtil.java @@ -16,7 +16,6 @@ import de.eshg.lib.appointmentblock.persistence.entity.AppointmentBlockGroup; import de.eshg.rest.service.error.BadRequestException; import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; @@ -75,11 +74,9 @@ public class AppointmentBlockSlotUtil { Instant start = appointmentBlock.getAppointmentBlockStart(); Instant end = appointmentBlock.getAppointmentBlockEnd(); long sequentialSlotCount = - getNumberOfSequentialAppointmentSlots( - appointmentBlockGroup.getSlotDurationInMinutes(), start, end); + getNumberOfSequentialAppointmentSlots(appointmentBlockGroup.getSlotDuration(), start, end); - Duration examinationDuration = - Duration.of(appointmentBlockGroup.getSlotDurationInMinutes(), ChronoUnit.MINUTES); + Duration examinationDuration = appointmentBlockGroup.getSlotDuration(); Instant slotStart = start; for (int i = 0; i < sequentialSlotCount; i++) { Instant slotEnd = slotStart.plus(examinationDuration); @@ -194,22 +191,20 @@ public class AppointmentBlockSlotUtil { private long getNumberOfTotalAppointmentSlots(AppointmentBlock appointmentBlock) { return getNumberOfTotalAppointmentSlots( - appointmentBlock.getAppointmentBlockGroup().getSlotDurationInMinutes(), + appointmentBlock.getAppointmentBlockGroup().getSlotDuration(), appointmentBlock.getAppointmentBlockGroup().getParallelExaminations(), appointmentBlock.getAppointmentBlockStart(), appointmentBlock.getAppointmentBlockEnd()); } private long getNumberOfTotalAppointmentSlots( - int slotDurationInMinutes, int parallelExaminations, Instant start, Instant end) { - long numberOfSequentialSlots = - getNumberOfSequentialAppointmentSlots(slotDurationInMinutes, start, end); + Duration slotDuration, int parallelExaminations, Instant start, Instant end) { + long numberOfSequentialSlots = getNumberOfSequentialAppointmentSlots(slotDuration, start, end); return numberOfSequentialSlots * parallelExaminations; } private long getNumberOfSequentialAppointmentSlots( - int slotDurationInMinutes, Instant start, Instant end) { - Duration examinationDuration = Duration.of(slotDurationInMinutes, ChronoUnit.MINUTES); + Duration examinationDuration, Instant start, Instant end) { Duration appointmentBlockDuration = Duration.between(start, end); return appointmentBlockDuration.dividedBy(examinationDuration); } diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockValidator.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockValidator.java index 23709742d..45f595880 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockValidator.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentBlockValidator.java @@ -22,7 +22,6 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.time.LocalTime; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; import java.util.Set; @@ -88,9 +87,8 @@ public class AppointmentBlockValidator { throw new BadRequestException( "AppointmentBlockGroup end time of day must be after start time of day."); } - Duration examinationDuration = - Duration.of(typeConfig.getStandardDurationInMinutes(), ChronoUnit.MINUTES); - Duration appointmentBlockLength = Duration.between(startTime, endTime); + Duration examinationDuration = typeConfig.getStandardDuration(); + Duration appointmentBlockLength = Duration.between(start, end); if (!DurationUtil.isDivisible(appointmentBlockLength, examinationDuration)) { String errorMessage = "Appointment block length %s is not a multiple of examination duration %s." diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeMapper.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeMapper.java index f4ae7bd4c..4ef669b16 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeMapper.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeMapper.java @@ -27,6 +27,6 @@ public class AppointmentTypeMapper { return new AppointmentTypeConfigDto( appointmentTypeConfig.getId(), toInterfaceType(appointmentTypeConfig.getAppointmentType()), - appointmentTypeConfig.getStandardDurationInMinutes()); + appointmentTypeConfig.getStandardDuration().toMinutes()); } } diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeService.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeService.java index fca653fe3..615ebc01f 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeService.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/AppointmentTypeService.java @@ -11,6 +11,7 @@ import de.eshg.lib.appointmentblock.api.UpdateAppointmentTypeRequest; import de.eshg.lib.appointmentblock.persistence.AppointmentTypeRepository; import de.eshg.lib.appointmentblock.persistence.entity.AppointmentTypeConfig; import de.eshg.rest.service.error.NotFoundException; +import java.time.Duration; import java.util.Comparator; import java.util.List; import java.util.UUID; @@ -49,12 +50,12 @@ public class AppointmentTypeService { public AppointmentTypeConfigDto updateAppointmentType( UUID id, UpdateAppointmentTypeRequest request) { - AppointmentTypeConfig appointmentTypeConfig = appointmentTypeRepository .findById(id) .orElseThrow(() -> new NotFoundException("Appointment type not found")); - appointmentTypeConfig.setStandardDurationInMinutes(request.standardDurationInMinutes()); + appointmentTypeConfig.setStandardDuration( + Duration.ofMinutes(request.standardDurationInMinutes())); appointmentTypeRepository.save(appointmentTypeConfig); return AppointmentTypeMapper.toInterfaceType(appointmentTypeConfig); diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/AppointmentTypeConfigDto.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/AppointmentTypeConfigDto.java index b9c049e7d..265b216d1 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/AppointmentTypeConfigDto.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/AppointmentTypeConfigDto.java @@ -14,4 +14,4 @@ import java.util.UUID; public record AppointmentTypeConfigDto( @NotNull UUID id, @NotNull AppointmentTypeDto appointmentTypeDto, - @NotNull @Min(0) int standardDurationInMinutes) {} + @NotNull @Min(1) long standardDurationInMinutes) {} diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/UpdateAppointmentTypeRequest.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/UpdateAppointmentTypeRequest.java index 6856ec61e..9a150002d 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/UpdateAppointmentTypeRequest.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/api/UpdateAppointmentTypeRequest.java @@ -8,4 +8,4 @@ package de.eshg.lib.appointmentblock.api; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; -public record UpdateAppointmentTypeRequest(@NotNull @Min(0) int standardDurationInMinutes) {} +public record UpdateAppointmentTypeRequest(@NotNull @Min(1) long standardDurationInMinutes) {} diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/CreateAppointmentTypeTask.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/CreateAppointmentTypeTask.java index 5b1464af8..306c41eeb 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/CreateAppointmentTypeTask.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/CreateAppointmentTypeTask.java @@ -9,6 +9,7 @@ import de.eshg.lib.appointmentblock.persistence.entity.AppointmentTypeConfig; import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties; import de.eshg.persistence.TransactionHelper; import jakarta.annotation.PostConstruct; +import java.time.Duration; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,8 +40,7 @@ public class CreateAppointmentTypeTask { appointmentBlockProperties .getDefaultAppointmentTypeConfiguration() .forEach( - (appointmentType, defaultDuration) -> { - int standardDuration = Math.toIntExact(defaultDuration.toMinutes()); + (appointmentType, standardDuration) -> { Optional<AppointmentTypeConfig> config = appointmentTypeRepository.findByAppointmentType(appointmentType); @@ -54,23 +54,23 @@ public class CreateAppointmentTypeTask { } private void createAppointmentTypeConfig( - AppointmentType appointmentType, int standardDurationInMinutes) { + AppointmentType appointmentType, Duration standardDuration) { AppointmentTypeConfig appointmentTypeConfig = new AppointmentTypeConfig(); appointmentTypeConfig.setAppointmentType(appointmentType); - appointmentTypeConfig.setStandardDurationInMinutes(standardDurationInMinutes); + appointmentTypeConfig.setStandardDuration(standardDuration); appointmentTypeRepository.save(appointmentTypeConfig); } private static void updateAppointmentTypeConfig( - AppointmentTypeConfig config, int standardDurationInMinutes) { - int previousStandardDurationInMinutes = config.getStandardDurationInMinutes(); - if (previousStandardDurationInMinutes != standardDurationInMinutes) { + AppointmentTypeConfig config, Duration standardDuration) { + Duration previousStandardDuration = config.getStandardDuration(); + if (!previousStandardDuration.equals(standardDuration)) { log.info( - "Updated appointment type configuration for type {} from {} to {} minutes", + "Updated appointment type configuration for type {} from {} to {}", config.getAppointmentType(), - previousStandardDurationInMinutes, - standardDurationInMinutes); + previousStandardDuration, + standardDuration); } - config.setStandardDurationInMinutes(standardDurationInMinutes); + config.setStandardDuration(standardDuration); } } diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentBlockGroup.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentBlockGroup.java index 74efd1b0e..db65b8421 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentBlockGroup.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentBlockGroup.java @@ -16,6 +16,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.OneToMany; import jakarta.persistence.OrderBy; import jakarta.persistence.OrderColumn; +import java.time.Duration; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -34,7 +35,8 @@ public class AppointmentBlockGroup extends BaseEntityWithExternalId { private int parallelExaminations; - private int slotDurationInMinutes; + @Column(nullable = false) + private Duration slotDuration; @ElementCollection @Column(name = "physician_id", nullable = false) @@ -77,12 +79,12 @@ public class AppointmentBlockGroup extends BaseEntityWithExternalId { this.parallelExaminations = parallelExaminations; } - public int getSlotDurationInMinutes() { - return slotDurationInMinutes; + public Duration getSlotDuration() { + return slotDuration; } - public void setSlotDurationInMinutes(int slotDurationInMinutes) { - this.slotDurationInMinutes = slotDurationInMinutes; + public void setSlotDuration(Duration slotDuration) { + this.slotDuration = slotDuration; } public List<UUID> getPhysicians() { diff --git a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentTypeConfig.java b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentTypeConfig.java index 3136501bd..da2fd8fb3 100644 --- a/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentTypeConfig.java +++ b/backend/lib-appointmentblock/src/main/java/de/eshg/lib/appointmentblock/persistence/entity/AppointmentTypeConfig.java @@ -12,6 +12,7 @@ import de.eshg.lib.common.SensitivityLevel; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; +import java.time.Duration; import org.hibernate.annotations.JdbcType; import org.hibernate.dialect.PostgreSQLEnumJdbcType; @@ -26,7 +27,8 @@ public class AppointmentTypeConfig extends GloballyUniqueEntityBase { @Column(nullable = false, unique = true) private AppointmentType appointmentType; - private int standardDurationInMinutes; + @Column(nullable = false) + private Duration standardDuration; public AppointmentType getAppointmentType() { return appointmentType; @@ -36,11 +38,11 @@ public class AppointmentTypeConfig extends GloballyUniqueEntityBase { this.appointmentType = appointmentType; } - public int getStandardDurationInMinutes() { - return standardDurationInMinutes; + public Duration getStandardDuration() { + return standardDuration; } - public void setStandardDurationInMinutes(int standardDuration) { - this.standardDurationInMinutes = standardDuration; + public void setStandardDuration(Duration standardDuration) { + this.standardDuration = standardDuration; } } diff --git a/backend/lib-auditlog/build.gradle b/backend/lib-auditlog/build.gradle index 2198bea77..0578e7e9e 100644 --- a/backend/lib-auditlog/build.gradle +++ b/backend/lib-auditlog/build.gradle @@ -1,6 +1,7 @@ plugins { id "eshg.java-lib" id "eshg.base-test-dependency" + id "java-test-fixtures" } dependencies { @@ -9,6 +10,7 @@ dependencies { implementation project(':rest-oauth-client-commons') implementation project(':test-helper-commons') implementation project(':lib-security-config-urls') + implementation project(':business-module-persistence-commons') implementation 'org.slf4j:slf4j-api' implementation 'org.springframework.boot:spring-boot-autoconfigure' @@ -20,9 +22,17 @@ dependencies { testImplementation project(':base-api') testImplementation project(':lib-base-client') testImplementation testFixtures(project(':business-module-commons')) + testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation 'org.springframework.boot:spring-boot-starter-oauth2-client' testRuntimeOnly 'org.springframework.boot:spring-boot-starter-web' + testRuntimeOnly 'org.postgresql:postgresql' + + testFixturesApi project(':lib-auditlog') + testFixturesApi project(':test-commons') + testFixturesApi 'de.cronn:validation-file-assertions:latest.release' + testFixturesApi 'org.springframework.data:spring-data-jpa' + testFixturesImplementation project(':business-module-persistence-commons') } tasks.named("test").configure { diff --git a/backend/lib-auditlog/gradle.lockfile b/backend/lib-auditlog/gradle.lockfile index 96976a90e..67d2f3d65 100644 --- a/backend/lib-auditlog/gradle.lockfile +++ b/backend/lib-auditlog/gradle.lockfile @@ -1,178 +1,212 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -ch.qos.logback:logback-classic:1.5.12=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.5.12=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-parameter-names:2.18.2=testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson:jackson-bom:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml:classmate:1.7.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.docker-java:docker-java-api:3.4.0=testCompileClasspath,testRuntimeClasspath -com.github.docker-java:docker-java-transport-zerodep:3.4.0=testCompileClasspath,testRuntimeClasspath -com.github.docker-java:docker-java-transport:3.4.0=testCompileClasspath,testRuntimeClasspath -com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.10.0=testRuntimeClasspath -com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0=testRuntimeClasspath -com.github.stephenc.jcip:jcip-annotations:1.0-1=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 -com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,testRuntimeClasspath -com.googlecode.libphonenumber:libphonenumber:8.13.50=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,testCompileClasspath,testRuntimeClasspath -com.nimbusds:lang-tag:1.7=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.nimbusds:nimbus-jose-jwt:9.37.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-api:1.3.0=testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-engine:1.3.0=testRuntimeClasspath -com.tngtech.archunit:archunit-junit5:1.3.0=testRuntimeClasspath -com.tngtech.archunit:archunit:1.3.0=testRuntimeClasspath +ch.qos.logback:logback-classic:1.5.12=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.5.12=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-hibernate6:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.module:jackson-module-parameter-names:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.fasterxml:classmate:1.7.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.curious-odd-man:rgxgen:2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-api:3.4.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport-zerodep:3.4.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport:3.4.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.gavlyukovskiy:datasource-decorator-spring-boot-autoconfigure:1.10.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.code.findbugs:jsr305:3.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.28.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.guava:failureaccess:1.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.guava:guava:33.3.1-jre=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.google.j2objc:j2objc-annotations:3.0.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.googlecode.java-diff-utils:diffutils:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.googlecode.libphonenumber:libphonenumber:8.13.50=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.jayway.jsonpath:json-path:2.9.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.nimbusds:content-type:2.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.nimbusds:lang-tag:1.7=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.nimbusds:nimbus-jose-jwt:9.37.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.nimbusds:oauth2-oidc-sdk:9.43.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.opencsv:opencsv:5.9=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.sun.istack:istack-commons-runtime:4.1.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-api:1.3.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine:1.3.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5:1.3.0=testFixturesRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit:1.3.0=testFixturesRuntimeClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.18.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -de.cronn:postgres-snapshot-util:1.4=testRuntimeClasspath -de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath -de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-core:1.14.2=testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-jakarta9:1.14.2=testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-registry-prometheus:1.14.2=testRuntimeClasspath -io.prometheus:prometheus-metrics-config:1.3.5=testRuntimeClasspath -io.prometheus:prometheus-metrics-core:1.3.5=testRuntimeClasspath -io.prometheus:prometheus-metrics-exposition-formats:1.3.5=testRuntimeClasspath -io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=testRuntimeClasspath -io.prometheus:prometheus-metrics-model:1.3.5=testRuntimeClasspath -io.prometheus:prometheus-metrics-tracer-common:1.3.5=testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.annotation:jakarta.annotation-api:2.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath +com.zaxxer:HikariCP:5.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +commons-io:commons-io:2.18.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath +de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath +de.cronn:liquibase-postgres-enum-extension:1.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:postgres-snapshot-util:1.4=testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:test-utils:1.1.1=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.micrometer:micrometer-core:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.micrometer:micrometer-jakarta9:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.micrometer:micrometer-registry-prometheus:1.14.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-config:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-core:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.persistence:jakarta.persistence-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +javax.xml.bind:jaxb-api:2.3.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +junit:junit:4.13.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath net.bytebuddy:byte-buddy-agent:1.15.11=testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.15.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.datafaker:datafaker:2.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.java.dev.jna:jna:5.13.0=testCompileClasspath,testRuntimeClasspath -net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.ttddyy:datasource-proxy:1.10=testRuntimeClasspath -org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-lang3:3.17.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.httpcomponents.client5:httpclient5:5.4.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.apache.httpcomponents.core5:httpcore5-h2:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.apache.httpcomponents.core5:httpcore5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.apache.logging.log4j:log4j-api:2.24.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.logging.log4j:log4j-to-slf4j:2.24.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.tomcat.embed:tomcat-embed-core:10.1.34=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.tomcat.embed:tomcat-embed-el:10.1.34=testCompileClasspath,testRuntimeClasspath -org.apache.tomcat.embed:tomcat-embed-websocket:10.1.34=testCompileClasspath,testRuntimeClasspath -org.apache.tomcat:tomcat-annotations-api:10.1.34=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.15.11=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +net.datafaker:datafaker:2.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +net.java.dev.jna:jna:5.13.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +net.minidev:accessors-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +net.minidev:json-smart:2.5.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +net.ttddyy:datasource-proxy:1.10=testFixturesRuntimeClasspath,testRuntimeClasspath +org.antlr:antlr4-runtime:4.13.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.commons:commons-lang3:3.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.commons:commons-text:1.12.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.httpcomponents.client5:httpclient5:5.4.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.httpcomponents.core5:httpcore5-h2:5.3.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.httpcomponents.core5:httpcore5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.logging.log4j:log4j-api:2.24.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.logging.log4j:log4j-to-slf4j:2.24.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-core:10.1.34=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-el:10.1.34=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-websocket:10.1.34=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apache.tomcat:tomcat-annotations-api:10.1.34=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.aspectj:aspectjweaver:1.9.22.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.assertj:assertj-core:3.26.3=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath -org.bouncycastle:bcpkix-jdk18on:1.80=testRuntimeClasspath -org.bouncycastle:bcprov-jdk18on:1.80=testRuntimeClasspath -org.bouncycastle:bcutil-jdk18on:1.80=testRuntimeClasspath -org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath -org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.hdrhistogram:HdrHistogram:2.2.2=testRuntimeClasspath -org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt -org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath +org.bouncycastle:bcpkix-jdk18on:1.80=testFixturesRuntimeClasspath,testRuntimeClasspath +org.bouncycastle:bcprov-jdk18on:1.80=testFixturesRuntimeClasspath,testRuntimeClasspath +org.bouncycastle:bcutil-jdk18on:1.80=testFixturesRuntimeClasspath,testRuntimeClasspath +org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.eclipse.angus:angus-activation:2.0.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:jaxb-core:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:jaxb-runtime:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:txw2:4.0.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt +org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter:5.11.4=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.11.4=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.11.4=testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.11.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.platform:junit-platform-launcher:1.11.4=testRuntimeClasspath -org.junit:junit-bom:5.11.4=testCompileClasspath,testRuntimeClasspath -org.latencyutils:LatencyUtils:2.0.3=testRuntimeClasspath +org.junit:junit-bom:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.latencyutils:LatencyUtils:2.0.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.liquibase:liquibase-core:4.29.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.postgresql:postgresql:42.7.4=testRuntimeClasspath -org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt +org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath +org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:2.0.16=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-actuator:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-dependencies:3.3.5=testRuntimeClasspath -org.springframework.boot:spring-boot-starter-actuator:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-json:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-logging:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-oauth2-client:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-oauth2-resource-server:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-security:3.4.1=testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-dependencies:3.3.5=testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-data-jpa:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-jdbc:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-json:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-logging:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-oauth2-client:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-oauth2-resource-server:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-security:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-tomcat:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-validation:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-web:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-tomcat:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-validation:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-web:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-test-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-test:3.4.1=testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-test:3.4.1=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-testcontainers:3.4.1=testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.data:spring-data-commons:3.4.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.springframework.security:spring-security-config:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-core:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-crypto:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-client:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-core:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-jose:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-resource-server:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.data:spring-data-commons:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.data:spring-data-jpa:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-config:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-core:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-crypto:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-client:6.4.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-core:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-jose:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-resource-server:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.security:spring-security-test:6.4.2=testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-web:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-aop:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-beans:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-context:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-core:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-expression:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-jcl:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-test:6.2.1=testCompileClasspath,testRuntimeClasspath -org.springframework:spring-tx:6.2.1=testRuntimeClasspath -org.springframework:spring-web:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-webmvc:6.2.1=testCompileClasspath,testRuntimeClasspath -org.testcontainers:testcontainers:1.20.4=testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-web:6.4.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-aop:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-aspects:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-beans:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-context:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-core:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-expression:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-jcl:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-jdbc:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-orm:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-test:6.2.1=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-tx:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-web:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springframework:spring-webmvc:6.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.testcontainers:database-commons:1.20.4=testRuntimeClasspath +org.testcontainers:jdbc:1.20.4=testRuntimeClasspath +org.testcontainers:postgresql:1.20.4=testRuntimeClasspath +org.testcontainers:testcontainers:1.20.4=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.xmlunit:xmlunit-core:2.10.0=testCompileClasspath,testRuntimeClasspath -org.yaml:snakeyaml:2.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.zalando:faux-pas:0.9.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-api:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-common:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-core:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-httpclient5:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-json:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-servlet:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-spring-boot-autoconfigure:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-spring-boot-starter:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.zalando:logbook-spring:3.10.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesCompileClasspath,testFixturesRuntimeClasspath +org.yaml:snakeyaml:2.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:faux-pas:0.9.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-api:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-common:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-core:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-httpclient5:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-json:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-servlet:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-spring-boot-autoconfigure:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-spring-boot-starter:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.zalando:logbook-spring:3.10.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +empty=annotationProcessor,developmentOnly,testAndDevelopmentOnly,testAnnotationProcessor,testFixturesAnnotationProcessor diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogArchiving.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogArchiving.java index 8ac7613a3..feca211bf 100644 --- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogArchiving.java +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogArchiving.java @@ -8,22 +8,23 @@ package de.eshg.lib.auditlog; import de.eshg.auditlog.AddAuditLogFileRequest; import de.eshg.auditlog.AuditLogArchivingApi; import de.eshg.auditlog.AuditLogSource; -import de.eshg.lib.auditlog.config.AuditLogConfig; +import de.eshg.lib.auditlog.domain.AuditLogEntry; +import de.eshg.lib.auditlog.domain.AuditLogEntryRepository; import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import de.eshg.rest.service.error.ErrorCode; +import de.eshg.rest.service.error.ErrorResponse; +import jakarta.transaction.Transactional; +import java.nio.charset.StandardCharsets; import java.time.Clock; +import java.time.Instant; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; -import java.util.stream.Stream; +import java.util.Map; +import java.util.stream.Collectors; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.util.FileSystemUtils; +import org.springframework.web.client.HttpClientErrorException.BadRequest; @Component public class AuditLogArchiving { @@ -32,106 +33,101 @@ public class AuditLogArchiving { org.slf4j.LoggerFactory.getLogger(AuditLogArchiving.class); private final AuditLogArchivingApi auditLogArchivingApi; - private final AuditLogConfig auditLogConfig; private final AuditLogSource auditLogSource; - private final Clock clock; private final ModuleClientAuthenticator moduleClientAuthenticator; + private final AuditLogEntryRepository auditLogEntryRepository; + private final Clock clock; public AuditLogArchiving( AuditLogArchivingApi auditLogArchivingApi, - AuditLogConfig auditLogConfig, AuditLogSource auditLogSource, - Clock clock, - ModuleClientAuthenticator moduleClientAuthenticator) { + ModuleClientAuthenticator moduleClientAuthenticator, + AuditLogEntryRepository auditLogEntryRepository, + Clock clock) { this.auditLogArchivingApi = auditLogArchivingApi; - this.auditLogConfig = auditLogConfig; this.auditLogSource = auditLogSource; - this.clock = clock; this.moduleClientAuthenticator = moduleClientAuthenticator; + this.auditLogEntryRepository = auditLogEntryRepository; + this.clock = clock; } @Scheduled(cron = "${de.eshg.auditlog.archiving.schedule:@daily}") + @Transactional public void runArchivingJob() { moduleClientAuthenticator.doWithModuleClientAuthentication(this::archiveOldAuditlogFiles); } private void archiveOldAuditlogFiles() { - log.info("Starting archiving of old audit log files"); - - Path logOutputDir = auditLogConfig.getLogOutputDir(); - log.debug("Using log output directory {}", logOutputDir); - - List<Path> candidates = getCandidates(logOutputDir); - - if (log.isInfoEnabled()) { - List<String> oldAuditLogFilesNames = - candidates.stream().map(Path::getFileName).map(Path::toString).toList(); - log.info( - "Found {} audit log directories for archiving {}", - candidates.size(), - !oldAuditLogFilesNames.isEmpty() ? oldAuditLogFilesNames : ""); - } - - for (Path candidate : candidates) { - archive(candidate); + log.info("Starting archiving of old audit log entries"); + + Instant todayAtStartOfDay = LocalDate.now(clock).atStartOfDay(clock.getZone()).toInstant(); + List<AuditLogEntry> toArchive = + auditLogEntryRepository.findByCreatedAtBeforeOrderByCreatedAtAscIdAsc(todayAtStartOfDay); + + log.info("Found {} entries created before {}", toArchive.size(), todayAtStartOfDay); + + Map<LocalDate, List<AuditLogEntry>> auditLogEntriesByDate = + toArchive.stream() + .collect( + Collectors.groupingBy( + auditLogEntry -> + LocalDate.ofInstant(auditLogEntry.getCreatedAt(), clock.getZone()), + LinkedHashMap::new, + Collectors.toList())); + + for (LocalDate auditLogDate : auditLogEntriesByDate.keySet()) { + try { + List<AuditLogEntry> auditLogEntries = auditLogEntriesByDate.get(auditLogDate); + log.info("Found {} entries to be archived for {}", auditLogEntries.size(), auditLogDate); + + archiveAuditLog(auditLogDate, auditLogEntries); + } catch (Exception e) { + log.error("Error while trying to archive audit log for {}.", auditLogDate, e); + } } - log.info("Finished archiving of old audit log files"); + log.info("Finished archiving of old audit logs"); } - private List<Path> getCandidates(Path logOutputDir) { - try (Stream<Path> paths = Files.list(logOutputDir)) { - return paths.filter(Files::isDirectory).filter(this::isOldAuditLogDirectory).toList(); - } catch (IOException e) { - throw new AuditLoggerException("Exception occured while reading auditlog root directory.", e); - } - } + private void archiveAuditLog(LocalDate auditLogDate, List<AuditLogEntry> auditLogEntries) { + String content = + auditLogEntries.stream().map(AuditLogFormatter::format).collect(Collectors.joining("\n")); - private boolean isOldAuditLogDirectory(Path path) { - try { - LocalDate localDate = - LocalDate.parse(path.getFileName().toString(), DateTimeFormatter.ISO_LOCAL_DATE); - return localDate.isBefore(LocalDate.now(clock)); - } catch (DateTimeParseException e) { - log.error("Failed to parse date from audit log directory name %s".formatted(path), e); - return false; - } - } + AuditLogFile auditLogFile = + new AuditLogFile(auditLogDate.toString(), content.getBytes(StandardCharsets.UTF_8)); - private void archive(Path auditLogDirectory) { try { - final LocalDate auditLogDate = - LocalDate.parse( - auditLogDirectory.getFileName().toString(), DateTimeFormatter.ISO_LOCAL_DATE); auditLogArchivingApi.addAuditlogFile( - new AddAuditLogFileRequest(auditLogDate, auditLogSource), mergeFiles(auditLogDirectory)); - log.info("Archived audit log directory for {}", auditLogDate); - - FileSystemUtils.deleteRecursively(auditLogDirectory); - log.info("Deleted local audit log directory for {}", auditLogDate); - } catch (Exception e) { - log.error( - "Error while trying to archive audit log directory {}. Resuming...", - auditLogDirectory, - e); + new AddAuditLogFileRequest(auditLogDate, auditLogSource), auditLogFile); + log.info("Archived audit logs for {}", auditLogDate); + + deleteAuditLog(auditLogDate, auditLogEntries); + } catch (BadRequest exception) { + if (isAlreadyExistsError(exception)) { + log.error( + "Audit log for {} already exists. Performing cleanup...", auditLogDate, exception); + deleteAuditLog(auditLogDate, auditLogEntries); + } else { + throw exception; + } } } - private static AuditLogFile mergeFiles(Path directory) throws IOException { - try (Stream<Path> originalFiles = Files.list(directory); - ByteArrayOutputStream mergedFileStream = new ByteArrayOutputStream()) { - - for (Path originalFile : sortedByFilename(originalFiles)) { - Files.copy(originalFile, mergedFileStream); + private boolean isAlreadyExistsError(BadRequest exception) { + try { + ErrorResponse errorResponse = exception.getResponseBodyAs(ErrorResponse.class); + if (errorResponse != null) { + return ErrorCode.ALREADY_EXISTS.equals(errorResponse.errorCode()); } - return new AuditLogFile(directory.getFileName().toString(), mergedFileStream.toByteArray()); + } catch (Exception e) { + return false; } + + return false; } - private static List<Path> sortedByFilename(Stream<Path> files) { - return files - .filter(Files::isRegularFile) - .sorted(Comparator.comparing(p -> p.getFileName().toString())) - .toList(); + private void deleteAuditLog(LocalDate auditLogDate, List<AuditLogEntry> auditLogEntries) { + auditLogEntryRepository.deleteAll(auditLogEntries); + log.info("Deleted {} local audit entries for {}", auditLogEntries.size(), auditLogDate); } } diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogFormatter.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogFormatter.java new file mode 100644 index 000000000..8271a9daa --- /dev/null +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogFormatter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.lib.auditlog; + +import de.eshg.lib.auditlog.domain.AuditLogEntry; +import java.util.stream.Collectors; + +public final class AuditLogFormatter { + + private AuditLogFormatter() {} + + private static final String TEMPLATE = + """ + Zeitstempel: %s + Kategorie: %s + Funktion: %s + Zusätzliche Attribute: %s + """; + + public static String format(AuditLogEntry auditLogEntry) { + String formattedAdditionalData = + auditLogEntry.getAdditionalData().entrySet().stream() + .map(e -> "\t- %s: %s".formatted(e.getKey(), e.getValue())) + .collect(Collectors.joining(System.lineSeparator(), System.lineSeparator(), "")); + + return TEMPLATE.formatted( + auditLogEntry.getCreatedAt(), + auditLogEntry.getCategory(), + auditLogEntry.getFunction(), + !formattedAdditionalData.isBlank() ? formattedAdditionalData : "-"); + } +} diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogTestHelperService.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogTestHelperService.java index 49c66ff1d..d9fd9738c 100644 --- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogTestHelperService.java +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogTestHelperService.java @@ -5,48 +5,27 @@ package de.eshg.lib.auditlog; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; -import de.eshg.lib.auditlog.config.AuditLogConfig; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.environment.EnvironmentConfig; -import java.io.IOException; -import java.nio.file.Files; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @ConditionalOnTestHelperEnabled @Service -public class AuditLogTestHelperService implements SharedAuditLogTestHelperApi { - - private static final Logger log = LoggerFactory.getLogger(AuditLogTestHelperService.class); +public class AuditLogTestHelperService implements AuditLogClientTestHelperApi { private final AuditLogArchiving auditLogArchiving; - private final AuditLogConfig auditLogConfig; private final EnvironmentConfig environmentConfig; public AuditLogTestHelperService( - AuditLogArchiving auditLogArchiving, - AuditLogConfig auditLogConfig, - EnvironmentConfig environmentConfig) { + AuditLogArchiving auditLogArchiving, EnvironmentConfig environmentConfig) { environmentConfig.assertIsNotProduction(); this.auditLogArchiving = auditLogArchiving; - this.auditLogConfig = auditLogConfig; this.environmentConfig = environmentConfig; } @Override - public void clearAuditLogStorageDirectory() throws IOException { - environmentConfig.assertIsNotProduction(); - log.info("Clearing audit log storage directory"); - if (Files.exists(auditLogConfig.getLogOutputDir())) { - FileUtils.cleanDirectory(auditLogConfig.getLogOutputDir().toFile()); - } - } - - @Override - public void runArchivingJob() { + public void runAuditLogArchivingJob() { environmentConfig.assertIsNotProduction(); auditLogArchiving.runArchivingJob(); } diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogger.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogger.java index 99663e4e2..df852ccc9 100644 --- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogger.java +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/AuditLogger.java @@ -5,75 +5,28 @@ package de.eshg.lib.auditlog; -import de.eshg.lib.auditlog.config.AuditLogConfig; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.time.Clock; -import java.time.Instant; -import java.time.LocalDate; +import de.eshg.lib.auditlog.domain.AuditLogEntry; +import de.eshg.lib.auditlog.domain.AuditLogEntryRepository; import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; +import java.util.TreeMap; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component -public final class AuditLogger { +public class AuditLogger { - private static final String MESSAGE_TEMPLATE = - """ - Zeitstempel: %s - Kategorie: %s - Funktion: %s - Zusätzliche Attribute: %s - """; + private final AuditLogEntryRepository auditLogEntryRepository; - private final Path outputDirPath; - private final Clock clock; - private final UuidProvider uuidProvider; - - AuditLogger(AuditLogConfig auditLogConfig, Clock clock, UuidProvider uuidProvider) { - this.outputDirPath = auditLogConfig.getLogOutputDir(); - this.clock = clock; - this.uuidProvider = uuidProvider; + AuditLogger(AuditLogEntryRepository auditLogEntryRepository) { + this.auditLogEntryRepository = auditLogEntryRepository; } + @Transactional public void log(String category, String function, Map<String, String> additionalData) { - String formattedAdditionalData = - additionalData.entrySet().stream() - .sorted(Entry.comparingByKey()) - .map(e -> "\t- %s: %s".formatted(e.getKey(), e.getValue())) - .collect(Collectors.joining(System.lineSeparator(), System.lineSeparator(), "")); - - final Instant now = clock.instant(); - - String message = - MESSAGE_TEMPLATE.formatted( - now, - category, - function, - !formattedAdditionalData.isBlank() ? formattedAdditionalData : "-"); - - try { - Files.writeString( - getLogFilePath(now), - message + System.lineSeparator(), - StandardCharsets.UTF_8, - StandardOpenOption.CREATE, - StandardOpenOption.APPEND); - } catch (IOException e) { - throw new AuditLoggerException("Exception occurred while trying to write to audit log", e); - } - } - - private Path getLogFilePath(Instant now) throws IOException { - final Path auditLogDateBasedDirectory = - outputDirPath.resolve(AuditLogHelper.getAuditLogFileName(LocalDate.now(clock))); - Files.createDirectories(auditLogDateBasedDirectory); // create directory if it does not exist - return auditLogDateBasedDirectory.resolve( - String.format( - "%012d%09d-%s", now.getEpochSecond(), now.getNano(), uuidProvider.nextUuid())); + AuditLogEntry auditLogEntry = new AuditLogEntry(); + auditLogEntry.setCategory(category); + auditLogEntry.setFunction(function); + auditLogEntry.setAdditionalData(new TreeMap<>(additionalData)); + auditLogEntryRepository.save(auditLogEntry); } } diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/config/AuditLogConfig.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/config/AuditLogConfig.java index abdcf6d92..a4eb5aad4 100644 --- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/config/AuditLogConfig.java +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/config/AuditLogConfig.java @@ -6,24 +6,16 @@ package de.eshg.lib.auditlog.config; import jakarta.validation.constraints.NotNull; -import java.nio.file.Files; -import java.nio.file.Path; import org.hibernate.validator.constraints.URL; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.Assert; import org.springframework.validation.annotation.Validated; @Validated @ConfigurationProperties(prefix = "de.eshg.auditlog") public class AuditLogConfig { - @NotNull private Path logOutputDir; @NotNull @URL private String serviceUrl; - public Path getLogOutputDir() { - return logOutputDir; - } - public String getServiceUrl() { return serviceUrl; } @@ -31,14 +23,4 @@ public class AuditLogConfig { public void setServiceUrl(String serviceUrl) { this.serviceUrl = serviceUrl; } - - public void setLogOutputDir(Path logOutputDir) { - if (Files.exists(logOutputDir)) { - Assert.isTrue( - Files.isDirectory(logOutputDir), "log output dir must be a directory:" + logOutputDir); - Assert.isTrue( - Files.isWritable(logOutputDir), "log output dir must be writable:" + logOutputDir); - } - this.logOutputDir = logOutputDir; - } } diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntry.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntry.java new file mode 100644 index 000000000..ceaa0098c --- /dev/null +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntry.java @@ -0,0 +1,80 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.lib.auditlog.domain; + +import de.eshg.domain.model.SequencedBaseEntity; +import de.eshg.lib.common.DataSensitivity; +import de.eshg.lib.common.SensitivityLevel; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.NamedAttributeNode; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.OrderBy; +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.Map; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Entity +@EntityListeners(AuditingEntityListener.class) +@NamedEntityGraph( + name = "AuditLogEntry.additionalData", + attributeNodes = @NamedAttributeNode("additionalData")) +public class AuditLogEntry extends SequencedBaseEntity { + + @CreatedDate + @Column(nullable = false) + @DataSensitivity(value = SensitivityLevel.PUBLIC) + private Instant createdAt; + + @Column(nullable = false) + @DataSensitivity(value = SensitivityLevel.PUBLIC) + private String category; + + @Column(nullable = false) + @DataSensitivity(value = SensitivityLevel.PUBLIC) + private String function; + + @DataSensitivity(value = SensitivityLevel.SENSITIVE) + @ElementCollection + @OrderBy("additional_data_key ASC") + private Map<String, String> additionalData = new LinkedHashMap<>(); + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getFunction() { + return function; + } + + public void setFunction(String function) { + this.function = function; + } + + public Map<String, String> getAdditionalData() { + return additionalData; + } + + public void setAdditionalData(Map<String, String> additionalData) { + this.additionalData = additionalData; + } +} diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntryRepository.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntryRepository.java new file mode 100644 index 000000000..bc5cd5d7a --- /dev/null +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/domain/AuditLogEntryRepository.java @@ -0,0 +1,17 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.lib.auditlog.domain; + +import java.time.Instant; +import java.util.List; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AuditLogEntryRepository extends JpaRepository<AuditLogEntry, Long> { + @EntityGraph(value = "AuditLogEntry.additionalData", type = EntityGraphType.LOAD) + List<AuditLogEntry> findByCreatedAtBeforeOrderByCreatedAtAscIdAsc(Instant createdAt); +} diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java index 0d581269a..aecec2a6e 100644 --- a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogAutoConfiguration.java @@ -15,6 +15,8 @@ import de.eshg.lib.auditlog.config.AuditLogConfig; import de.eshg.rest.client.BearerAuthInterceptor; import de.eshg.rest.client.CorrelationIdForwardingInterceptor; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -26,6 +28,7 @@ import org.springframework.web.service.invoker.HttpServiceProxyFactory; @AutoConfiguration @PropertySource("classpath:/auditlog-default.properties") +@AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class) @EnableConfigurationProperties(AuditLogConfig.class) @Import( value = { diff --git a/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogLibraryDomainModelAutoConfiguration.java b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogLibraryDomainModelAutoConfiguration.java new file mode 100644 index 000000000..41f19500e --- /dev/null +++ b/backend/lib-auditlog/src/main/java/de/eshg/lib/auditlog/spring/AuditLogLibraryDomainModelAutoConfiguration.java @@ -0,0 +1,15 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.lib.auditlog.spring; + +import de.eshg.lib.auditlog.domain.AuditLogEntry; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration; + +@AutoConfiguration(before = JpaRepositoriesAutoConfiguration.class) +@AutoConfigurationPackage(basePackageClasses = {AuditLogEntry.class}) +public class AuditLogLibraryDomainModelAutoConfiguration {} diff --git a/backend/lib-auditlog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/backend/lib-auditlog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index f2df03b32..8a48bd070 100644 --- a/backend/lib-auditlog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/backend/lib-auditlog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,2 @@ de.eshg.lib.auditlog.spring.AuditLogAutoConfiguration +de.eshg.lib.auditlog.spring.AuditLogLibraryDomainModelAutoConfiguration diff --git a/backend/lib-auditlog/src/main/resources/auditlog-default.properties b/backend/lib-auditlog/src/main/resources/auditlog-default.properties index 1923ec895..f39ca32db 100644 --- a/backend/lib-auditlog/src/main/resources/auditlog-default.properties +++ b/backend/lib-auditlog/src/main/resources/auditlog-default.properties @@ -1,2 +1 @@ -de.eshg.auditlog.log-output-dir=${java.io.tmpdir}/auditlog de.eshg.auditlog.service-url=http://${DOCKER_HOSTNAME:localhost}:8094 diff --git a/backend/lib-base-client/gradle.lockfile b/backend/lib-base-client/gradle.lockfile index 641b20f4d..a883e63e7 100644 --- a/backend/lib-base-client/gradle.lockfile +++ b/backend/lib-base-client/gradle.lockfile @@ -25,10 +25,10 @@ de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeCla de.cronn:reflection-util:2.17.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -51,10 +51,10 @@ org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -67,13 +67,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-calendar-api/gradle.lockfile b/backend/lib-calendar-api/gradle.lockfile index ee716303e..6efa88853 100644 --- a/backend/lib-calendar-api/gradle.lockfile +++ b/backend/lib-calendar-api/gradle.lockfile @@ -10,8 +10,8 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -26,10 +26,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -42,9 +42,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/lib-calendar/gradle.lockfile b/backend/lib-calendar/gradle.lockfile index 2a6adce6b..a1752af8d 100644 --- a/backend/lib-calendar/gradle.lockfile +++ b/backend/lib-calendar/gradle.lockfile @@ -16,8 +16,8 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,9 +49,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-central-repository-api/gradle.lockfile b/backend/lib-central-repository-api/gradle.lockfile index d83e09eb8..2e040c44f 100644 --- a/backend/lib-central-repository-api/gradle.lockfile +++ b/backend/lib-central-repository-api/gradle.lockfile @@ -14,10 +14,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.servlet:jakarta.servlet-api:6.0.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -34,10 +34,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -50,13 +50,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-central-repository-client/gradle.lockfile b/backend/lib-central-repository-client/gradle.lockfile index f8c0bd7fa..185dc9a71 100644 --- a/backend/lib-central-repository-client/gradle.lockfile +++ b/backend/lib-central-repository-client/gradle.lockfile @@ -17,10 +17,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.servlet:jakarta.servlet-api:6.0.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -40,10 +40,10 @@ org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspat org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -56,13 +56,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-commons/gradle.lockfile b/backend/lib-commons/gradle.lockfile index 099bd7b42..fc7b2018a 100644 --- a/backend/lib-commons/gradle.lockfile +++ b/backend/lib-commons/gradle.lockfile @@ -8,7 +8,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=testCompileClasspath,testRuntimeClasspath @@ -22,10 +22,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -38,9 +38,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/lib-contact-api/gradle.lockfile b/backend/lib-contact-api/gradle.lockfile index f7af9e94f..da1f0d31c 100644 --- a/backend/lib-contact-api/gradle.lockfile +++ b/backend/lib-contact-api/gradle.lockfile @@ -9,7 +9,7 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -24,10 +24,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -40,9 +40,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/lib-contact/gradle.lockfile b/backend/lib-contact/gradle.lockfile index 5c5583ece..48e5e580c 100644 --- a/backend/lib-contact/gradle.lockfile +++ b/backend/lib-contact/gradle.lockfile @@ -52,10 +52,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -90,10 +90,10 @@ org.hamcrest:hamcrest-core:2.2=testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -109,15 +109,16 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/lib-document-generator/gradle.lockfile b/backend/lib-document-generator/gradle.lockfile index 85d59527f..d11aa8df1 100644 --- a/backend/lib-document-generator/gradle.lockfile +++ b/backend/lib-document-generator/gradle.lockfile @@ -28,10 +28,10 @@ io.github.openhtmltopdf:openhtmltopdf-slf4j:1.1.24=compileClasspath,productionRu io.github.openhtmltopdf:openhtmltopdf-svg-support:1.1.24=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -77,10 +77,10 @@ org.glassfish.jaxb:jaxb-core:4.0.5=testFixturesCompileClasspath,testFixturesRunt org.glassfish.jaxb:jaxb-runtime:4.0.5=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.glassfish.jaxb:txw2:4.0.5=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -94,13 +94,14 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.mozilla:rhino:1.7.13=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-editor-api/gradle.lockfile b/backend/lib-editor-api/gradle.lockfile index bb22010b5..d925e834b 100644 --- a/backend/lib-editor-api/gradle.lockfile +++ b/backend/lib-editor-api/gradle.lockfile @@ -13,9 +13,9 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -31,10 +31,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -47,13 +47,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-editor/gradle.lockfile b/backend/lib-editor/gradle.lockfile index 25daf372f..78c078921 100644 --- a/backend/lib-editor/gradle.lockfile +++ b/backend/lib-editor/gradle.lockfile @@ -52,10 +52,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath 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.persistence:jakarta.persistence-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -93,10 +93,10 @@ org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -113,16 +113,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-four-eyes-principle-api/gradle.lockfile b/backend/lib-four-eyes-principle-api/gradle.lockfile index 76b87c3ef..77ac307ea 100644 --- a/backend/lib-four-eyes-principle-api/gradle.lockfile +++ b/backend/lib-four-eyes-principle-api/gradle.lockfile @@ -14,10 +14,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,13 +49,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-four-eyes-principle/build.gradle b/backend/lib-four-eyes-principle/build.gradle index 486c2808f..adfd918b7 100644 --- a/backend/lib-four-eyes-principle/build.gradle +++ b/backend/lib-four-eyes-principle/build.gradle @@ -20,6 +20,7 @@ dependencies { testImplementation project(':test-commons') testImplementation testFixtures(project(':business-module-persistence-commons')) + testImplementation testFixtures(project(':lib-auditlog')) testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' testRuntimeOnly 'org.postgresql:postgresql' testRuntimeOnly 'com.h2database:h2' diff --git a/backend/lib-four-eyes-principle/gradle.lockfile b/backend/lib-four-eyes-principle/gradle.lockfile index 38a9d38fa..6b0be86d0 100644 --- a/backend/lib-four-eyes-principle/gradle.lockfile +++ b/backend/lib-four-eyes-principle/gradle.lockfile @@ -64,10 +64,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,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 @@ -118,10 +118,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -139,16 +139,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-keycloak/gradle.lockfile b/backend/lib-keycloak/gradle.lockfile index 7d9f7ecc7..41f00f17e 100644 --- a/backend/lib-keycloak/gradle.lockfile +++ b/backend/lib-keycloak/gradle.lockfile @@ -28,10 +28,10 @@ org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -44,9 +44,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java b/backend/lib-keycloak/src/main/java/de/eshg/lib/keycloak/EmployeePermissionRole.java index 22a2ad98d..60b325c9e 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 @@ -339,6 +339,7 @@ public enum EmployeePermissionRole implements PermissionRole { BASE_PERSONS_WRITE, BASE_FACILITIES_READ, BASE_FACILITIES_WRITE, + BASE_ACCESS_CODE_USER_ADMIN, BASE_CALENDAR_BUSINESS_EVENTS_WRITE); private final String keycloakNameWithoutPrefix; 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 c62976212..017ff1d0f 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 @@ -231,7 +231,21 @@ public enum EmployeeTestUser implements KeycloakUser { "password", "Thomas", "Müller", - List.of(ModuleMemberGroup.DENTAL, ModuleLeaderGroup.DENTAL)); + List.of(ModuleMemberGroup.DENTAL, ModuleLeaderGroup.DENTAL)), + STI_PROTECTION_DUMMY( + "sti_protection_dummy_user", + "+49 555 123 470", + "password", + "Klaus", + "Klausen", + List.of(ModuleMemberGroup.STI_PROTECTION)), + STI_PROTECTION_MODULE_LEADER( + "sti_protection_module_leader", + "+49 555 123 471", + "password", + "Max", + "Power", + List.of(ModuleMemberGroup.STI_PROTECTION, ModuleLeaderGroup.STI_PROTECTION)); private final String username; private final String email; diff --git a/backend/lib-lsd-api/gradle.lockfile b/backend/lib-lsd-api/gradle.lockfile index 36580e3b6..93e405e86 100644 --- a/backend/lib-lsd-api/gradle.lockfile +++ b/backend/lib-lsd-api/gradle.lockfile @@ -54,9 +54,9 @@ de.cronn:test-utils:1.1.1=testFixturesCompileClasspath,testFixturesRuntimeClassp de.cronn:validation-file-assertions:0.8.0=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.quarkus:quarkus-junit4-mock:3.17.7=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.quarkus:quarkus-junit4-mock:3.18.0=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.mail:jakarta.mail-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -98,10 +98,10 @@ org.glassfish.jaxb:txw2:4.0.5=compileClasspath,productionRuntimeClasspath,runtim org.glassfish.jaxb:xsom:4.0.5=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -131,9 +131,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/lib-mutex/gradle.lockfile b/backend/lib-mutex/gradle.lockfile index 50cfd1c5c..2846446dd 100644 --- a/backend/lib-mutex/gradle.lockfile +++ b/backend/lib-mutex/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -113,10 +113,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntime org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -134,16 +134,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-notification-api/gradle.lockfile b/backend/lib-notification-api/gradle.lockfile index ba60e82d9..8ffb1bdb3 100644 --- a/backend/lib-notification-api/gradle.lockfile +++ b/backend/lib-notification-api/gradle.lockfile @@ -14,10 +14,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,13 +49,14 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-notification/gradle.lockfile b/backend/lib-notification/gradle.lockfile index 9b313d57a..0bfd41050 100644 --- a/backend/lib-notification/gradle.lockfile +++ b/backend/lib-notification/gradle.lockfile @@ -61,10 +61,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,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 @@ -115,10 +115,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -136,16 +136,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-procedures-api/gradle.lockfile b/backend/lib-procedures-api/gradle.lockfile index fe9793307..a72a94f12 100644 --- a/backend/lib-procedures-api/gradle.lockfile +++ b/backend/lib-procedures-api/gradle.lockfile @@ -15,10 +15,10 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -34,10 +34,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -51,13 +51,14 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.3=testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-logging:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-test:3.4.1=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-procedures/build.gradle b/backend/lib-procedures/build.gradle index b0de9ac9f..a1262af5c 100644 --- a/backend/lib-procedures/build.gradle +++ b/backend/lib-procedures/build.gradle @@ -52,6 +52,7 @@ dependencies { testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation testFixtures(project(':base-api')) + testImplementation testFixtures(project(':lib-auditlog')) testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation 'org.instancio:instancio-core:latest.release' testImplementation 'org.apache.commons:commons-text:latest.release' diff --git a/backend/lib-procedures/gradle.lockfile b/backend/lib-procedures/gradle.lockfile index 9fd43ce78..7fc4bc1be 100644 --- a/backend/lib-procedures/gradle.lockfile +++ b/backend/lib-procedures/gradle.lockfile @@ -74,10 +74,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath,xjc jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -145,11 +145,11 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.instancio:instancio-core:5.2.1=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.instancio:instancio-core:5.3.0=testCompileClasspath,testRuntimeClasspath +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -168,16 +168,17 @@ org.mozilla:rhino:1.7.13=compileClasspath,productionRuntimeClasspath,runtimeClas org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath 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 index 7af9f3a56..b5b425695 100644 --- 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 @@ -41,6 +41,7 @@ import de.eshg.lib.procedure.procedures.ProcedureDeletionService; import de.eshg.rest.service.error.BadRequestException; import io.swagger.v3.oas.annotations.tags.Tag; import java.nio.charset.StandardCharsets; +import java.time.Period; import java.util.List; import java.util.Map; import java.util.Optional; @@ -240,7 +241,7 @@ public class GdprValidationTaskController< } @Override - @Transactional(readOnly = true) + @Transactional public GetGdprValidationTaskDetailsResponse getGdprValidationTaskDetails(UUID gdprId) { assertNewFeatureEnabled(BaseFeature.GDPR, baseFeatureTogglesApi.getFeatureToggles()); List<UUID> fileStateIds = service.getAndValidateFileStateIds(gdprId); @@ -304,7 +305,7 @@ public class GdprValidationTaskController< service.writeAuditLog( "Löschung Fachmodul Vorgang", mapAuditlog(validationTask, businessProcedure.get())); - procedureDeletionService.deleteAndWriteToCemetery(businessProcedure.get()); + procedureDeletionService.deleteAndWriteToCemetery(businessProcedure.get(), Period.ZERO); } @Override diff --git a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureQuery.java b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureQuery.java index d1d12752d..340cd53e5 100644 --- a/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureQuery.java +++ b/backend/lib-procedures/src/main/java/de/eshg/lib/procedure/procedures/ProcedureQuery.java @@ -5,9 +5,12 @@ package de.eshg.lib.procedure.procedures; +import de.eshg.lib.procedure.domain.model.FacilityType; import de.eshg.lib.procedure.domain.model.PersonType; import de.eshg.lib.procedure.domain.model.Procedure; import de.eshg.lib.procedure.domain.model.Procedure_; +import de.eshg.lib.procedure.domain.model.RelatedFacility; +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 jakarta.persistence.EntityManager; @@ -52,4 +55,29 @@ public class ProcedureQuery { return entityManager.createQuery(query).getResultList(); } + + public < + ProcedureT extends Procedure<ProcedureT, ?, ?, RelatedFacilityT>, + RelatedFacilityT extends RelatedFacility<ProcedureT>> + List<UUID> findAllRelatedFacilityFileStateIds( + Specification<ProcedureT> procedureSpecification, + Class<ProcedureT> procedureClass, + FacilityType facilityType) { + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery<UUID> query = criteriaBuilder.createQuery(UUID.class); + Root<ProcedureT> root = query.from(procedureClass); + + Join<ProcedureT, ? extends RelatedFacility<?>> relatedFacilitiesJoin = + root.join(Procedure_.relatedFacilities); + Join<ProcedureT, ? extends RelatedFacility<?>> childJoin = + relatedFacilitiesJoin.on( + criteriaBuilder.equal( + relatedFacilitiesJoin.get(RelatedFacility_.facilityType), facilityType)); + + query.select(childJoin.get(RelatedFacility_.centralFileStateId)); + + query.where(procedureSpecification.toPredicate(root, query, criteriaBuilder)); + + return entityManager.createQuery(query).getResultList(); + } } diff --git a/backend/lib-relay/gradle.lockfile b/backend/lib-relay/gradle.lockfile index 34b364c10..aa68aa2db 100644 --- a/backend/lib-relay/gradle.lockfile +++ b/backend/lib-relay/gradle.lockfile @@ -33,8 +33,8 @@ de.cronn:test-utils:1.1.1=testCompileClasspath,testFixturesCompileClasspath,test de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -59,10 +59,10 @@ org.bouncycastle:bcutil-jdk18on:1.80=testFixturesRuntimeClasspath,testRuntimeCla org.checkerframework:checker-qual:3.43.0=testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath @@ -76,9 +76,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-scheduling/gradle.lockfile b/backend/lib-scheduling/gradle.lockfile index 5e35ee6ad..ed7627ab5 100644 --- a/backend/lib-scheduling/gradle.lockfile +++ b/backend/lib-scheduling/gradle.lockfile @@ -8,7 +8,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.persistence:jakarta.persistence-api:3.1.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -26,10 +26,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -42,9 +42,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-security-config-urls/gradle.lockfile b/backend/lib-security-config-urls/gradle.lockfile index e1de784a1..205a9bf9b 100644 --- a/backend/lib-security-config-urls/gradle.lockfile +++ b/backend/lib-security-config-urls/gradle.lockfile @@ -20,10 +20,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -36,9 +36,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 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 47b639ac9..a0e009a9c 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 @@ -67,6 +67,7 @@ public final class BaseUrls { public static final class Gdpr { public static final String DOWNLOADS = "/{id}/downloads"; + public static final String DELETE_DOWNLOADS = "/{id}/delete-downloads"; public static final String FILE_STATE_IDS = "/{id}/fileStateIds"; public static final String BY_ID = "/{id}"; public static final String DETAILS_PAGE = "/{id}/details-page"; @@ -162,6 +163,7 @@ public final class BaseUrls { public static final class StiProtection { public static final String PROCEDURE_CONTROLLER = "/sti-procedures"; + public static final String CITIZEN_PUBLIC_CONTROLLER = "/citizen/public"; private StiProtection() {} } diff --git a/backend/lib-security-config/gradle.lockfile b/backend/lib-security-config/gradle.lockfile index 2022b4696..4dd6164ab 100644 --- a/backend/lib-security-config/gradle.lockfile +++ b/backend/lib-security-config/gradle.lockfile @@ -33,8 +33,8 @@ de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=testCompileClasspath,testRuntimeClasspath @@ -59,12 +59,12 @@ org.bouncycastle:bcutil-jdk18on:1.80=testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testRuntimeClasspath -org.jetbrains:annotations:26.0.1=compileClasspath +org.jetbrains:annotations:26.0.2=compileClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -77,9 +77,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java index 313037920..6fdf4e181 100644 --- a/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java +++ b/backend/lib-security-config/src/main/java/de/eshg/rest/service/security/config/StiProtectionPublicSecurityConfig.java @@ -20,6 +20,8 @@ public final class StiProtectionPublicSecurityConfig extends AbstractPublicSecur grantAccessToLibProceduresUrls( EmployeePermissionRole.STI_PROTECTION_USER, ModuleLeaderRole.STI_PROTECTION_LEADER); + requestMatchers(BaseUrls.StiProtection.CITIZEN_PUBLIC_CONTROLLER + "/**").permitAll(); + requestMatchers(GET, BaseUrls.StiProtection.PROCEDURE_CONTROLLER + "/{id}/**") .hasAnyRole( EmployeePermissionRole.STI_PROTECTION_USER, EmployeePermissionRole.PROCEDURE_ARCHIVE); diff --git a/backend/lib-service-directory-admin-api/gradle.lockfile b/backend/lib-service-directory-admin-api/gradle.lockfile index 13e7961a6..4aaabed76 100644 --- a/backend/lib-service-directory-admin-api/gradle.lockfile +++ b/backend/lib-service-directory-admin-api/gradle.lockfile @@ -33,8 +33,8 @@ de.cronn:test-utils:1.1.1=testFixturesCompileClasspath,testFixturesRuntimeClassp de.cronn:validation-file-assertions:0.8.0=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -59,10 +59,10 @@ org.bouncycastle:bcutil-jdk18on:1.80=testFixturesRuntimeClasspath,testRuntimeCla org.checkerframework:checker-qual:3.43.0=testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath @@ -76,9 +76,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-service-directory-api/gradle.lockfile b/backend/lib-service-directory-api/gradle.lockfile index 2f34a9bbd..bca9a5c42 100644 --- a/backend/lib-service-directory-api/gradle.lockfile +++ b/backend/lib-service-directory-api/gradle.lockfile @@ -16,8 +16,8 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -33,10 +33,10 @@ org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -49,9 +49,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/lib-statistics-api/gradle.lockfile b/backend/lib-statistics-api/gradle.lockfile index f7af9e94f..da1f0d31c 100644 --- a/backend/lib-statistics-api/gradle.lockfile +++ b/backend/lib-statistics-api/gradle.lockfile @@ -9,7 +9,7 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -24,10 +24,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -40,9 +40,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/Attribute.java b/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/Attribute.java index bd24d08ba..7793d69ea 100644 --- a/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/Attribute.java +++ b/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/Attribute.java @@ -18,4 +18,5 @@ public record Attribute( String unit, @Size(min = 1) @Valid List<ValueOptionInternal> valueOptions, @NotBlank String category, - @NotNull boolean mandatory) {} + @NotNull boolean mandatory, + DataPrivacyCategory dataPrivacyCategory) {} diff --git a/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/DataPrivacyCategory.java b/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/DataPrivacyCategory.java new file mode 100644 index 000000000..51705ff95 --- /dev/null +++ b/backend/lib-statistics-api/src/main/java/de/eshg/lib/statistics/api/DataPrivacyCategory.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.lib.statistics.api; + +public enum DataPrivacyCategory { + QUASI_IDENTIFYING, + SENSITIVE, + INSENSITIVE +} diff --git a/backend/lib-statistics/README.md b/backend/lib-statistics/README.md index c9e0b808d..162965c47 100644 --- a/backend/lib-statistics/README.md +++ b/backend/lib-statistics/README.md @@ -48,7 +48,7 @@ Based on the available data sources the user will select the wanted statistics d the corresponding `AttributeInfo`. `null` is also a valid value. * BOOLEAN: java.lang.Boolean (or boolean) -* DATE: java.lang.String in the format yyyy-MMM-dd as returned by java.time.LocalDate.toString() +* DATE: java.lang.String in the format yyyy-MM-dd as returned by java.time.LocalDate.toString() * DECIMAL: java.lang.Double (or double) - precision = 10, scale = 4 can be handled * INTEGER: java.lang.Integer (or int) * TEXT: java.lang.String, blank strings will be stored as null diff --git a/backend/lib-statistics/gradle.lockfile b/backend/lib-statistics/gradle.lockfile index 35b809823..b0116d899 100644 --- a/backend/lib-statistics/gradle.lockfile +++ b/backend/lib-statistics/gradle.lockfile @@ -69,10 +69,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -141,10 +141,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntime org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -163,16 +163,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testFixture org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/StatisticsService.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/StatisticsService.java index fcddd54ab..e342c7702 100644 --- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/StatisticsService.java +++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/StatisticsService.java @@ -71,7 +71,8 @@ public class StatisticsService { attribute.getUnit(), attribute.getValueOptions(), attribute.getCategory(), - attribute.isMandatory()); + attribute.isMandatory(), + attribute.getDataPrivacyCategory()); } private static ValueType mapToValueType(AttributeData attribute) { diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/AttributeData.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/AttributeData.java index d57b12ac1..06e4543be 100644 --- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/AttributeData.java +++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/AttributeData.java @@ -5,6 +5,7 @@ package de.eshg.lib.statistics.attributes; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.ValueOptionInternal; import java.util.List; @@ -25,6 +26,11 @@ public abstract sealed class AttributeData private final List<ValueOptionInternal> valueOptions; private final String category; private final boolean mandatory; + private final DataPrivacyCategory dataPrivacyCategory; + + protected AttributeData(String name, String code, String category, boolean mandatory) { + this(name, code, null, null, category, mandatory); + } protected AttributeData( String name, @@ -33,7 +39,7 @@ public abstract sealed class AttributeData ValueOptionInternal valueOption, String category, boolean mandatory) { - this(name, code, unit, toList(valueOption), category, mandatory); + this(name, code, unit, toList(valueOption), category, mandatory, null); } protected AttributeData( @@ -41,12 +47,18 @@ public abstract sealed class AttributeData String code, ValueOptionInternal valueOption, String category, - boolean mandatory) { - this(name, code, null, toList(valueOption), category, mandatory); + boolean mandatory, + DataPrivacyCategory dataPrivacyCategory) { + this(name, code, null, toList(valueOption), category, mandatory, dataPrivacyCategory); } - protected AttributeData(String name, String code, String category, boolean mandatory) { - this(name, code, null, (ValueOptionInternal) null, category, mandatory); + protected AttributeData( + String name, + String code, + ValueOptionInternal valueOption, + String category, + boolean mandatory) { + this(name, code, null, toList(valueOption), category, mandatory, null); } protected AttributeData( @@ -55,13 +67,15 @@ public abstract sealed class AttributeData String unit, List<ValueOptionInternal> valueOptions, String category, - boolean mandatory) { + boolean mandatory, + DataPrivacyCategory dataPrivacyCategory) { this.name = name; this.code = code; this.unit = unit; this.valueOptions = valueOptions; this.category = category; this.mandatory = mandatory; + this.dataPrivacyCategory = dataPrivacyCategory; } private static List<ValueOptionInternal> toList(ValueOptionInternal valueOption) { @@ -94,4 +108,8 @@ public abstract sealed class AttributeData public boolean isMandatory() { return mandatory; } + + public DataPrivacyCategory getDataPrivacyCategory() { + return dataPrivacyCategory; + } } diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ProcedureAttribute.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ProcedureAttribute.java index 3636f53ce..20d183a49 100644 --- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ProcedureAttribute.java +++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ProcedureAttribute.java @@ -5,8 +5,10 @@ package de.eshg.lib.statistics.attributes; +import de.eshg.lib.statistics.api.DataPrivacyCategory; + public final class ProcedureAttribute extends AttributeData { public ProcedureAttribute(String name, String category, boolean mandatory) { - super(name, "PROCEDURE_REFERENCE", category, mandatory); + super(name, "PROCEDURE_REFERENCE", null, category, mandatory, DataPrivacyCategory.INSENSITIVE); } } diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/TextAttribute.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/TextAttribute.java index eb6f391d5..115e59084 100644 --- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/TextAttribute.java +++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/TextAttribute.java @@ -5,11 +5,21 @@ package de.eshg.lib.statistics.attributes; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.ValueOptionInternal; public final class TextAttribute extends AttributeData { public TextAttribute(String name, String code, String category, boolean mandatory) { - this(name, code, null, category, mandatory); + this(name, code, null, category, mandatory, null); + } + + public TextAttribute( + String name, + String code, + String category, + boolean mandatory, + DataPrivacyCategory dataPrivacyCategory) { + this(name, code, null, category, mandatory, dataPrivacyCategory); } public TextAttribute( @@ -17,7 +27,8 @@ public final class TextAttribute extends AttributeData { String code, ValueOptionInternal valueOption, String category, - boolean mandatory) { - super(name, code, valueOption, category, mandatory); + boolean mandatory, + DataPrivacyCategory dataPrivacyCategory) { + super(name, code, valueOption, category, mandatory, dataPrivacyCategory); } } diff --git a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ValueWithOptionsAttribute.java b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ValueWithOptionsAttribute.java index a6f384095..7107402d1 100644 --- a/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ValueWithOptionsAttribute.java +++ b/backend/lib-statistics/src/main/java/de/eshg/lib/statistics/attributes/ValueWithOptionsAttribute.java @@ -15,6 +15,6 @@ public final class ValueWithOptionsAttribute extends AttributeData { List<ValueOptionInternal> valueOptions, String category, boolean mandatory) { - super(name, code, null, valueOptions, category, mandatory); + super(name, code, null, valueOptions, category, mandatory, null); } } diff --git a/backend/lib-xdomea/gradle.lockfile b/backend/lib-xdomea/gradle.lockfile index 181c64b34..8926b4961 100644 --- a/backend/lib-xdomea/gradle.lockfile +++ b/backend/lib-xdomea/gradle.lockfile @@ -32,10 +32,10 @@ org.glassfish.jaxb:jaxb-xjc:4.0.5=xjc org.glassfish.jaxb:txw2:4.0.5=xjc org.glassfish.jaxb:xsom:4.0.5=xjc org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -48,9 +48,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/lib-xlsx-import/gradle.lockfile b/backend/lib-xlsx-import/gradle.lockfile index 3d945effc..5898d672d 100644 --- a/backend/lib-xlsx-import/gradle.lockfile +++ b/backend/lib-xlsx-import/gradle.lockfile @@ -56,10 +56,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.mail:jakarta.mail-api:2.1.3=testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -108,10 +108,10 @@ org.hamcrest:hamcrest-core:2.2=testFixturesRuntimeClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testFixturesRuntimeClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath @@ -128,15 +128,16 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testFixturesRuntimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testFixturesRuntimeClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testFixturesRuntimeClasspath,testRuntimeClasspath 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 57fd51c31..b3c707098 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 @@ -10,36 +10,48 @@ import de.eshg.base.SalutationDto; import de.eshg.lib.common.CountryCode; import de.eshg.lib.xlsximport.model.AddressData; import de.eshg.lib.xlsximport.util.XlsxUtil; +import java.time.Clock; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.UUID; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; import org.apache.poi.ss.usermodel.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; public abstract class RowReader<R extends RowData<R>, C extends XlsxColumn> { + private static final Logger log = LoggerFactory.getLogger(RowReader.class); + private static final DataFormatter DATA_FORMATTER = new DataFormatter(); private final List<C> actualColumns; - private final CellStyle errorCellStyle; + private final Map<CellStyle, CellStyle> errorCellStyles = new LinkedHashMap<>(); private final Drawing<?> drawing; private final CreationHelper factory; private final Supplier<R> resultRowSupplier; + private final Clock clock; + private final Function<CellStyle, CellStyle> createErrorCellStyle; - protected RowReader(Sheet sheet, List<C> actualColumns, Supplier<R> resultRowSupplier) { + protected RowReader( + Sheet sheet, List<C> actualColumns, Supplier<R> resultRowSupplier, Clock clock) { Workbook workbook = sheet.getWorkbook(); this.actualColumns = actualColumns; - this.errorCellStyle = createErrorStyle(workbook); this.drawing = sheet.createDrawingPatriarch(); this.factory = workbook.getCreationHelper(); this.resultRowSupplier = resultRowSupplier; + this.clock = clock; + this.createErrorCellStyle = cellStyle -> createErrorCellStyle(cellStyle, workbook); } public R readRow(Row xlsxRow) { @@ -95,17 +107,19 @@ public abstract class RowReader<R extends RowData<R>, C extends XlsxColumn> { } protected void addCellError(Cell cell, String errorMessage) { + CellStyle errorCellStyle = + errorCellStyles.computeIfAbsent(cell.getCellStyle(), createErrorCellStyle); cell.setCellStyle(errorCellStyle); if (cell.getCellComment() == null) { cell.setCellComment(createComment(cell, errorMessage)); } } - private CellStyle createErrorStyle(Workbook workbook) { + private CellStyle createErrorCellStyle(CellStyle cellStyle, Workbook workbook) { CellStyle errorStyle = workbook.createCellStyle(); + errorStyle.cloneStyleFrom(cellStyle); errorStyle.setFillForegroundColor(XlsxUtil.newColor(255, 215, 215)); errorStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - return errorStyle; } @@ -267,13 +281,40 @@ public abstract class RowReader<R extends RowData<R>, C extends XlsxColumn> { return true; } + protected LocalDate cellAsDateOfBirth( + ColumnAccessor<C> col, C column, ErrorHandler errorHandler) { + Cell cell = col.get(column); + LocalDate dateOfBirth = cellAsDate(cell, errorHandler); + if (dateOfBirth != null) { + validateDateOfBirth(cell, dateOfBirth, errorHandler); + } + return dateOfBirth; + } + + private void validateDateOfBirth(Cell cell, LocalDate dateOfBirth, ErrorHandler errorHandler) { + LocalDate today = LocalDate.now(clock); + LocalDate minDateOfBirth = today.minusYears(150); + LocalDate maxDateOfBirth = today.plusYears(1); + if (dateOfBirth.isBefore(minDateOfBirth) || dateOfBirth.isAfter(maxDateOfBirth)) { + log.debug( + "Date of birth {} is outside the valid range: {} - {}", + dateOfBirth, + minDateOfBirth, + maxDateOfBirth); + errorHandler.handleError(cell, "Ungültiges Geburtsdatum"); + } + } + protected LocalDate cellAsDate(ColumnAccessor<C> col, C column, ErrorHandler errorHandler) { return cellAsDate(col.get(column), errorHandler); } public static LocalDate cellAsDate(Cell cell, ErrorHandler errorHandler) { LocalDateTime localDateTime = cellAsDateTime(cell, errorHandler); - return localDateTime != null ? localDateTime.toLocalDate() : null; + if (localDateTime == null) { + return null; + } + return localDateTime.toLocalDate(); } protected LocalDateTime cellAsDateTime( diff --git a/backend/local-service-directory/gradle.lockfile b/backend/local-service-directory/gradle.lockfile index b5129525d..6dd02d202 100644 --- a/backend/local-service-directory/gradle.lockfile +++ b/backend/local-service-directory/gradle.lockfile @@ -64,10 +64,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=productionRuntimeClass io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath 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 @@ -120,10 +120,10 @@ org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:commons-logging-jboss-logging:1.0.0.Final=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jboss.resteasy:resteasy-client-api:6.2.9.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -151,17 +151,18 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/logging-commons/gradle.lockfile b/backend/logging-commons/gradle.lockfile index e1de784a1..205a9bf9b 100644 --- a/backend/logging-commons/gradle.lockfile +++ b/backend/logging-commons/gradle.lockfile @@ -20,10 +20,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -36,9 +36,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/measles-protection/build.gradle b/backend/measles-protection/build.gradle index ffee8193b..8d31bb872 100644 --- a/backend/measles-protection/build.gradle +++ b/backend/measles-protection/build.gradle @@ -23,6 +23,7 @@ dependencies { testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation testFixtures(project(':lib-document-generator')) testImplementation testFixtures(project(':lib-procedures')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/measles-protection/gradle.lockfile b/backend/measles-protection/gradle.lockfile index 38ff6ee8c..89edbacc4 100644 --- a/backend/measles-protection/gradle.lockfile +++ b/backend/measles-protection/gradle.lockfile @@ -75,10 +75,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -169,10 +169,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -191,16 +191,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/measles-protection/openApi.json b/backend/measles-protection/openApi.json index 9ea20e99d..fe5c93144 100644 --- a/backend/measles-protection/openApi.json +++ b/backend/measles-protection/openApi.json @@ -3348,18 +3348,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -4166,9 +4155,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -7445,9 +7434,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, diff --git a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedureTestHelperController.java b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedureTestHelperController.java index fe07287a3..bcde5f4e1 100644 --- a/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedureTestHelperController.java +++ b/backend/measles-protection/src/main/java/de/eshg/measlesprotection/testhelper/ProtectionProcedureTestHelperController.java @@ -5,7 +5,7 @@ package de.eshg.measlesprotection.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.measlesprotection.api.MeaslesProtectionProcedurePopulationResult; import de.eshg.measlesprotection.api.draft.OpenProcedureResponse; @@ -17,7 +17,6 @@ import de.eshg.testhelper.api.PopulationRequest; import de.eshg.testhelper.environment.EnvironmentConfig; import de.eshg.testhelper.population.ListWithTotalNumber; import jakarta.validation.Valid; -import java.io.IOException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -26,7 +25,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class ProtectionProcedureTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final ProtectionProcedurePopulator populator; private final AuditLogTestHelperService auditLogTestHelperService; @@ -60,12 +59,7 @@ public class ProtectionProcedureTestHelperController extends TestHelperControlle } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/measles-protection/src/main/resources/migrations/0046_add_auditlog_entry.xml b/backend/measles-protection/src/main/resources/migrations/0046_add_auditlog_entry.xml new file mode 100644 index 000000000..28652f29b --- /dev/null +++ b/backend/measles-protection/src/main/resources/migrations/0046_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/measles-protection/src/main/resources/migrations/0047_convert_duration_columns_to_interval.xml b/backend/measles-protection/src/main/resources/migrations/0047_convert_duration_columns_to_interval.xml new file mode 100644 index 000000000..0f9b823d4 --- /dev/null +++ b/backend/measles-protection/src/main/resources/migrations/0047_convert_duration_columns_to_interval.xml @@ -0,0 +1,30 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="use_interval_for_durations"> + <sql> + ALTER TABLE appointment_type_config + ALTER COLUMN standard_duration_in_minutes TYPE interval second(6) + USING (standard_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_type_config" + oldColumnName="standard_duration_in_minutes" + newColumnName="standard_duration"/> + + <sql> + ALTER TABLE appointment_block_group + ALTER COLUMN slot_duration_in_minutes TYPE interval second(6) + USING (slot_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_block_group" + oldColumnName="slot_duration_in_minutes" + newColumnName="slot_duration"/> + </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 e99690d0b..df3deeecf 100644 --- a/backend/measles-protection/src/main/resources/migrations/changelog.xml +++ b/backend/measles-protection/src/main/resources/migrations/changelog.xml @@ -53,5 +53,7 @@ <include file="migrations/0043_make_appointment_block_users_non_nullable.xml"/> <include file="migrations/0044_add_cemetery_delete_at.xml"/> <include file="migrations/0045_add_previous_file_state_id_to_system_progress_entry.xml"/> + <include file="migrations/0046_add_auditlog_entry.xml"/> + <include file="migrations/0047_convert_duration_columns_to_interval.xml"/> </databaseChangeLog> diff --git a/backend/medical-registry/build.gradle b/backend/medical-registry/build.gradle index 56c79dc08..550e09cd2 100644 --- a/backend/medical-registry/build.gradle +++ b/backend/medical-registry/build.gradle @@ -22,6 +22,7 @@ dependencies { testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation testFixtures(project(':lib-procedures')) testImplementation testFixtures(project(':lib-xlsx-import')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/medical-registry/gradle.lockfile b/backend/medical-registry/gradle.lockfile index 1f90bae6a..ddf6f487a 100644 --- a/backend/medical-registry/gradle.lockfile +++ b/backend/medical-registry/gradle.lockfile @@ -71,10 +71,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -146,10 +146,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -168,16 +168,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/medical-registry/openApi.json b/backend/medical-registry/openApi.json index 3a88f01e4..0ea7433fb 100644 --- a/backend/medical-registry/openApi.json +++ b/backend/medical-registry/openApi.json @@ -2523,18 +2523,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" 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 0dc823d3a..f13638eaf 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 @@ -347,7 +347,7 @@ public class MedicalRegistryController { } @GetMapping("/{procedureId}") - @Transactional(readOnly = true) + @Transactional @Operation(summary = "Get medical registry procedure by id.") public GetProcedureResponse getProcedure(@PathVariable("procedureId") UUID procedureId) { MedicalRegistryProcedure medicalRegistryProcedure = diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryImportController.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryImportController.java index db3fb971c..234bdaf1c 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryImportController.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/MedicalRegistryImportController.java @@ -82,7 +82,7 @@ public class MedicalRegistryImportController { (sheet, actualColumns) -> { MedicalRegistryImporter importer = new MedicalRegistryImporter( - sheet, actualColumns, medicalRegistryService, IMPORTER_BATCH_SIZE); + sheet, actualColumns, medicalRegistryService, clock, IMPORTER_BATCH_SIZE); return importer.process(); }); log.info( diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/PersonService.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/PersonService.java index bc078aeac..73b297e77 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/PersonService.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/PersonService.java @@ -15,7 +15,6 @@ 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.centralfile.api.person.PersonDetails; -import de.eshg.base.centralfile.api.person.PersonDetailsDto; import de.eshg.base.centralfile.api.person.UpdatePersonRequest; import de.eshg.base.centralfile.api.person.UpdateReferencePersonRequest; import de.eshg.lib.procedure.MapperHelper; @@ -130,7 +129,7 @@ public class PersonService { return personApi .updatePersonFileStateAndReference( - personFileState.id(), new UpdatePersonRequest(new PersonDetailsDto(personFileState))) + personFileState.id(), new UpdatePersonRequest(personFileState)) .id(); } @@ -153,8 +152,9 @@ public class PersonService { return updatedFileState.id(); } - private PersonDetailsDto enrich(PersonDetails newPersonDetails, PersonDetails oldPersonDetails) { - return new PersonDetailsDto( + private UpdatePersonRequest enrich( + PersonDetails newPersonDetails, PersonDetails oldPersonDetails) { + return new UpdatePersonRequest( EnrichmentHelper.enrich(PersonDetails::title, newPersonDetails, oldPersonDetails), EnrichmentHelper.enrich(PersonDetails::salutation, newPersonDetails, oldPersonDetails), EnrichmentHelper.enrich(PersonDetails::gender, newPersonDetails, oldPersonDetails), diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ApplicantDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ApplicantDto.java index c4e817663..b1d06e297 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ApplicantDto.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ApplicantDto.java @@ -5,9 +5,9 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateApplicantDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateApplicantDto.java index de27df8f1..c1e3faaa1 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateApplicantDto.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreateApplicantDto.java @@ -5,9 +5,9 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.EmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.EmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreatePracticeDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreatePracticeDto.java index da2e4f678..d90c0fa3b 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreatePracticeDto.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/CreatePracticeDto.java @@ -5,7 +5,7 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; 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 e69597d4f..e61865296 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,7 +5,7 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeReferenceFacilityDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeReferenceFacilityDto.java index 68a71180b..d340e3e49 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeReferenceFacilityDto.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/PracticeReferenceFacilityDto.java @@ -5,10 +5,10 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto; import de.eshg.base.centralfile.api.facility.FacilityDetails; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalReferencePersonDto.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalReferencePersonDto.java index 569df5e16..6f7ad22dd 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalReferencePersonDto.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/api/ProfessionalReferencePersonDto.java @@ -5,12 +5,12 @@ package de.eshg.medicalregistry.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.person.PersonDetails; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryImporter.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryImporter.java index e1b56915f..104078f4e 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryImporter.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryImporter.java @@ -10,6 +10,7 @@ import de.eshg.lib.xlsximport.ImportStatus; import de.eshg.lib.xlsximport.Importer; import de.eshg.lib.xlsximport.RowData; import de.eshg.medicalregistry.MedicalRegistryService; +import java.time.Clock; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -28,8 +29,12 @@ public class MedicalRegistryImporter extends Importer<MedicalRegistryRow, Medica XSSFSheet sheet, List<MedicalRegistryColumn> actualColumns, MedicalRegistryService medicalRegistryService, + Clock clock, int batchSize) { - super(sheet, new MedicalRegistryRowReader(sheet), new FeedbackColumnAccessor(actualColumns)); + super( + sheet, + new MedicalRegistryRowReader(sheet, clock), + new FeedbackColumnAccessor(actualColumns)); this.medicalRegistryService = medicalRegistryService; if (batchSize < 1 || batchSize > 10_000) { throw new IllegalArgumentException("batchSize must be between 1 and 10_000"); diff --git a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryRowReader.java b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryRowReader.java index f43776cf7..860eee79b 100644 --- a/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryRowReader.java +++ b/backend/medical-registry/src/main/java/de/eshg/medicalregistry/importer/MedicalRegistryRowReader.java @@ -24,6 +24,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import jakarta.validation.metadata.ConstraintDescriptor; +import java.time.Clock; import java.util.Arrays; import java.util.Set; import org.apache.poi.ss.usermodel.Cell; @@ -34,8 +35,8 @@ class MedicalRegistryRowReader extends RowReader<MedicalRegistryRow, MedicalRegi private static final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); - MedicalRegistryRowReader(Sheet sheet) { - super(sheet, Arrays.asList(MedicalRegistryColumn.values()), MedicalRegistryRow::new); + MedicalRegistryRowReader(Sheet sheet, Clock clock) { + super(sheet, Arrays.asList(MedicalRegistryColumn.values()), MedicalRegistryRow::new, clock); } @Override 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 3890dc463..c132a9d3f 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 @@ -5,7 +5,7 @@ package de.eshg.medicalregistry.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.medicalregistry.featuretoggle.MedicalRegistryFeature; import de.eshg.medicalregistry.featuretoggle.MedicalRegistryFeatureToggle; @@ -13,7 +13,6 @@ import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.TestHelperController; import de.eshg.testhelper.TestHelperWithDatabaseService; import de.eshg.testhelper.environment.EnvironmentConfig; -import java.io.IOException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.service.annotation.DeleteExchange; @@ -22,7 +21,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class MedicalRegistryTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final AuditLogTestHelperService auditLogTestHelperService; private final MedicalRegistryFeatureToggle medicalRegistryFeatureToggle; @@ -50,12 +49,7 @@ public class MedicalRegistryTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/medical-registry/src/main/resources/migrations/0008_add_auditlog_entry.xml b/backend/medical-registry/src/main/resources/migrations/0008_add_auditlog_entry.xml new file mode 100644 index 000000000..ebb946b32 --- /dev/null +++ b/backend/medical-registry/src/main/resources/migrations/0008_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 cronn GmbH + SPDX-License-Identifier: Apache-2.0 +--> + +<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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/medical-registry/src/main/resources/migrations/changelog.xml b/backend/medical-registry/src/main/resources/migrations/changelog.xml index 1fbc0d576..15661d472 100644 --- a/backend/medical-registry/src/main/resources/migrations/changelog.xml +++ b/backend/medical-registry/src/main/resources/migrations/changelog.xml @@ -15,5 +15,6 @@ <include file="migrations/0005_add_cemetery_delete_at.xml"/> <include file="migrations/0006_add_countrycodes.xml"/> <include file="migrations/0007_add_previous_file_state_id_to_system_progress_entry.xml"/> + <include file="migrations/0008_add_auditlog_entry.xml"/> </databaseChangeLog> diff --git a/backend/official-medical-service/build.gradle b/backend/official-medical-service/build.gradle index e5dace08d..a9f36344d 100644 --- a/backend/official-medical-service/build.gradle +++ b/backend/official-medical-service/build.gradle @@ -19,6 +19,7 @@ dependencies { testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation testFixtures(project(':lib-procedures')) + testImplementation testFixtures(project(':base-api')) } dockerCompose { diff --git a/backend/official-medical-service/gradle.lockfile b/backend/official-medical-service/gradle.lockfile index e30023385..974f15435 100644 --- a/backend/official-medical-service/gradle.lockfile +++ b/backend/official-medical-service/gradle.lockfile @@ -67,10 +67,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -136,10 +136,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -158,16 +158,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/official-medical-service/openApi.json b/backend/official-medical-service/openApi.json index 1e9662041..fee8858e5 100644 --- a/backend/official-medical-service/openApi.json +++ b/backend/official-medical-service/openApi.json @@ -679,6 +679,187 @@ "tags" : [ "Concern" ] } }, + "/employee/document/{id}" : { + "delete" : { + "operationId" : "deleteDocumentEmployee", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Deletes a document along with all files associated with it", + "tags" : [ "OmsDocument" ] + }, + "patch" : { + "operationId" : "patchDocumentInformation", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PatchDocumentInformationRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Updates information of one oms document", + "tags" : [ "OmsDocument" ] + } + }, + "/employee/document/{id}/complete-file-upload" : { + "patch" : { + "operationId" : "patchCompleteDocumentFileUpload", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "multipart/form-data" : { + "schema" : { + "type" : "object", + "properties" : { + "files" : { + "type" : "array", + "items" : { + "type" : "string", + "format" : "binary" + } + } + }, + "required" : [ "files" ] + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Completes file upload of one oms document", + "tags" : [ "OmsDocument" ] + } + }, + "/employee/document/{id}/note" : { + "patch" : { + "operationId" : "patchDocumentNote", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PatchDocumentNoteRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Updates note of one oms document", + "tags" : [ "OmsDocument" ] + } + }, + "/employee/document/{id}/review" : { + "patch" : { + "operationId" : "patchDocumentReview", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PatchDocumentReviewRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Accepts or rejects a submitted document", + "tags" : [ "OmsDocument" ] + } + }, + "/employee/file/{fileId}" : { + "get" : { + "operationId" : "getDownloadFile", + "parameters" : [ { + "in" : "path", + "name" : "fileId", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/octet-stream" : { + "schema" : { + "type" : "string", + "format" : "binary" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Download file from oms document.", + "tags" : [ "OmsFile" ] + } + }, "/employee/procedures" : { "get" : { "operationId" : "getAllEmployeeProcedures", @@ -848,6 +1029,13 @@ } ], "responses" : { "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AcceptDraftProcedureResponse" + } + } + }, "description" : "OK" } }, @@ -974,6 +1162,117 @@ "tags" : [ "EmployeeOmsProcedure" ] } }, + "/employee/procedures/{id}/document" : { + "get" : { + "operationId" : "getAllDocuments", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetDocumentsResponse" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Get all documents for one oms procedure", + "tags" : [ "EmployeeOmsProcedure" ] + }, + "post" : { + "operationId" : "postDocument", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "multipart/form-data" : { + "schema" : { + "type" : "object", + "properties" : { + "files" : { + "type" : "array", + "items" : { + "type" : "string", + "format" : "binary" + } + }, + "note" : { + "type" : "string" + }, + "postDocumentRequest" : { + "$ref" : "#/components/schemas/PostDocumentRequest" + } + }, + "required" : [ "postDocumentRequest" ] + } + } + } + }, + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "string", + "format" : "uuid" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Add a document to an oms procedure", + "tags" : [ "EmployeeOmsProcedure" ] + } + }, + "/employee/procedures/{id}/email-notifications" : { + "patch" : { + "operationId" : "patchEmailNotifications", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PatchEmployeeOmsProcedureEmailNotificationsRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Updates the whether or not email notifications will be sent", + "tags" : [ "EmployeeOmsProcedure" ] + } + }, "/employee/procedures/{id}/facility" : { "patch" : { "operationId" : "patchFacility", @@ -1070,6 +1369,37 @@ "tags" : [ "EmployeeOmsProcedure" ] } }, + "/employee/procedures/{id}/medical-opinion-status" : { + "patch" : { + "operationId" : "patchMedicalOpinionStatus", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/MedicalOpinionStatus" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Updates the medical opinion status of a oms procedure", + "tags" : [ "EmployeeOmsProcedure" ] + } + }, "/employee/procedures/{id}/physician" : { "patch" : { "operationId" : "patchPhysician", @@ -1140,6 +1470,37 @@ "tags" : [ "EmployeeOmsProcedure" ] } }, + "/employee/procedures/{id}/waiting-room" : { + "patch" : { + "operationId" : "patchWaitingRoom", + "parameters" : [ { + "in" : "path", + "name" : "id", + "required" : true, + "schema" : { + "type" : "string", + "format" : "uuid" + } + } ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/WaitingRoom" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "OK" + } + }, + "summary" : "Update waiting room details for a procedure", + "tags" : [ "EmployeeOmsProcedure" ] + } + }, "/employee/procedures/{procedureId}/affected-person" : { "patch" : { "operationId" : "updateAffectedPerson", @@ -1198,8 +1559,60 @@ "description" : "OK" } }, - "summary" : "Synchronize affected person data", - "tags" : [ "EmployeeOmsProcedure" ] + "summary" : "Synchronize affected person data", + "tags" : [ "EmployeeOmsProcedure" ] + } + }, + "/employee/waiting-room" : { + "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" : { + "minimum" : 0, + "type" : "integer", + "format" : "int32" + } + }, { + "in" : "query", + "name" : "pageSize", + "required" : false, + "schema" : { + "minimum" : 1, + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GetWaitingRoomProceduresResponse" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Get all procedures in waiting room.", + "tags" : [ "WaitingRoom" ] } }, "/files/{fileId}" : { @@ -2920,18 +3333,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -3238,6 +3640,15 @@ "propertyName" : "@type" } }, + "AcceptDraftProcedureResponse" : { + "required" : [ "accessCode" ], + "type" : "object", + "properties" : { + "accessCode" : { + "type" : "string" + } + } + }, "AddBarrierTestHelperResponse" : { "required" : [ "barrierId" ], "type" : "object", @@ -3431,9 +3842,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -4005,6 +4416,84 @@ } } }, + "Document" : { + "required" : [ "documentStatus", "documentTypeDe", "files", "id", "mandatoryDocument", "uploadInCitizenPortal" ], + "type" : "object", + "properties" : { + "documentStatus" : { + "$ref" : "#/components/schemas/DocumentStatus" + }, + "documentTypeDe" : { + "type" : "string" + }, + "documentTypeEn" : { + "type" : "string" + }, + "files" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/OmsFile" + } + }, + "helpTextDe" : { + "type" : "string" + }, + "helpTextEn" : { + "type" : "string" + }, + "id" : { + "type" : "string", + "format" : "uuid" + }, + "lastDocumentUpload" : { + "type" : "string", + "format" : "date-time" + }, + "mandatoryDocument" : { + "type" : "boolean" + }, + "note" : { + "type" : "string" + }, + "reasonForRejection" : { + "type" : "string" + }, + "uploadInCitizenPortal" : { + "type" : "boolean" + } + } + }, + "DocumentPopulation" : { + "required" : [ "key", "request" ], + "type" : "object", + "properties" : { + "files" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/FileTestDataConfig" + } + }, + "key" : { + "type" : "string" + }, + "note" : { + "type" : "string" + }, + "reasonForRejection" : { + "type" : "string" + }, + "request" : { + "$ref" : "#/components/schemas/PostDocumentRequest" + }, + "targetState" : { + "$ref" : "#/components/schemas/DocumentStatus" + } + } + }, + "DocumentStatus" : { + "type" : "string", + "enum" : [ "MISSING", "SUBMITTED", "REJECTED", "ACCEPTED" ] + }, "DomesticAddress" : { "required" : [ "city", "country", "postalCode", "street" ], "type" : "object", @@ -4063,7 +4552,7 @@ } ] }, "EmployeeOmsProcedureDetails" : { - "required" : [ "affectedPerson", "appointments", "id", "status" ], + "required" : [ "affectedPerson", "appointments", "id", "medicalOpinionStatus", "sendEmailNotifications", "status", "waitingRoom" ], "type" : "object", "properties" : { "affectedPerson" : { @@ -4075,6 +4564,10 @@ "$ref" : "#/components/schemas/OmsAppointment" } }, + "citizenUserId" : { + "type" : "string", + "format" : "uuid" + }, "concern" : { "$ref" : "#/components/schemas/Concern" }, @@ -4085,11 +4578,20 @@ "type" : "string", "format" : "uuid" }, + "medicalOpinionStatus" : { + "$ref" : "#/components/schemas/MedicalOpinionStatus" + }, "physician" : { "$ref" : "#/components/schemas/User" }, + "sendEmailNotifications" : { + "type" : "boolean" + }, "status" : { "$ref" : "#/components/schemas/ProcedureStatus" + }, + "waitingRoom" : { + "$ref" : "#/components/schemas/WaitingRoom" } } }, @@ -4117,7 +4619,7 @@ } }, "EmployeeOmsProcedureOverview" : { - "required" : [ "id", "status" ], + "required" : [ "id", "medicalOpinionStatus", "status" ], "type" : "object", "properties" : { "concern" : { @@ -4140,6 +4642,9 @@ "lastName" : { "type" : "string" }, + "medicalOpinionStatus" : { + "$ref" : "#/components/schemas/MedicalOpinionStatus" + }, "nextAppointment" : { "type" : "string", "format" : "date-time" @@ -4298,6 +4803,10 @@ } } }, + "FileTestDataConfig" : { + "type" : "string", + "enum" : [ "PDF", "JPG", "PNG" ] + }, "FileType" : { "type" : "string", "enum" : [ "JPEG", "PNG", "PDF", "EML" ] @@ -4753,6 +5262,18 @@ } } }, + "GetDocumentsResponse" : { + "required" : [ "documents" ], + "type" : "object", + "properties" : { + "documents" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Document" + } + } + } + }, "GetEmployeeOmsProcedureOverviewResponse" : { "required" : [ "elements", "totalNumberOfElements" ], "type" : "object", @@ -5368,6 +5889,22 @@ "default" : "ASC", "enum" : [ "ASC", "DESC" ] }, + "GetWaitingRoomProceduresResponse" : { + "required" : [ "elements", "totalNumberOfElements" ], + "type" : "object", + "properties" : { + "elements" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/WaitingRoomProcedure" + } + }, + "totalNumberOfElements" : { + "type" : "integer", + "format" : "int64" + } + } + }, "HttpMethod" : { "type" : "string", "enum" : [ "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE" ] @@ -5690,6 +6227,10 @@ "type" : "string", "enum" : [ "LETTER", "PHONE_CALL", "NOTE", "EMAIL", "IMAGE", "DOCUMENT" ] }, + "MedicalOpinionStatus" : { + "type" : "string", + "enum" : [ "IN_PROGRESS", "ACCOMPLISHED" ] + }, "MetaData" : { "required" : [ "@type" ], "type" : "object", @@ -5751,6 +6292,30 @@ } } }, + "OmsFile" : { + "required" : [ "creationDate", "fileType", "id", "name", "size" ], + "type" : "object", + "properties" : { + "creationDate" : { + "type" : "string", + "format" : "date-time" + }, + "fileType" : { + "$ref" : "#/components/schemas/FileType" + }, + "id" : { + "type" : "string", + "format" : "uuid" + }, + "name" : { + "type" : "string" + }, + "size" : { + "type" : "integer", + "format" : "int32" + } + } + }, "Operation" : { "type" : "string", "enum" : [ "DELETE" ] @@ -5773,6 +6338,59 @@ } } }, + "PatchDocumentInformationRequest" : { + "required" : [ "documentTypeDe", "mandatoryDocument", "uploadInCitizenPortal" ], + "type" : "object", + "properties" : { + "documentTypeDe" : { + "type" : "string" + }, + "documentTypeEn" : { + "type" : "string" + }, + "helpTextDe" : { + "type" : "string" + }, + "helpTextEn" : { + "type" : "string" + }, + "mandatoryDocument" : { + "type" : "boolean" + }, + "uploadInCitizenPortal" : { + "type" : "boolean" + } + } + }, + "PatchDocumentNoteRequest" : { + "type" : "object", + "properties" : { + "note" : { + "type" : "string" + } + } + }, + "PatchDocumentReviewRequest" : { + "required" : [ "result" ], + "type" : "object", + "properties" : { + "reasonForRejection" : { + "type" : "string" + }, + "result" : { + "$ref" : "#/components/schemas/ReviewResult" + } + } + }, + "PatchEmployeeOmsProcedureEmailNotificationsRequest" : { + "required" : [ "sendEmailNotifications" ], + "type" : "object", + "properties" : { + "sendEmailNotifications" : { + "type" : "boolean" + } + } + }, "PatchEmployeeOmsProcedureFacilityRequest" : { "required" : [ "updatedFacility" ], "type" : "object", @@ -5873,6 +6491,30 @@ } } }, + "PostDocumentRequest" : { + "required" : [ "documentTypeDe", "mandatoryDocument", "uploadInCitizenPortal" ], + "type" : "object", + "properties" : { + "documentTypeDe" : { + "type" : "string" + }, + "documentTypeEn" : { + "type" : "string" + }, + "helpTextDe" : { + "type" : "string" + }, + "helpTextEn" : { + "type" : "string" + }, + "mandatoryDocument" : { + "type" : "boolean" + }, + "uploadInCitizenPortal" : { + "type" : "boolean" + } + } + }, "PostEmployeeOmsProcedureFacilityRequest" : { "required" : [ "facility" ], "type" : "object", @@ -5939,6 +6581,9 @@ "type" : "string" } }, + "citizenUserId" : { + "type" : "string" + }, "closedAppointments" : { "type" : "array", "items" : { @@ -5948,9 +6593,18 @@ "concern" : { "$ref" : "#/components/schemas/ConcernTestDataConfig" }, + "documents" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/DocumentPopulation" + } + }, "facility" : { "$ref" : "#/components/schemas/PostEmployeeOmsProcedureFacilityRequest" }, + "medicalOpinionStatus" : { + "$ref" : "#/components/schemas/MedicalOpinionStatus" + }, "physician" : { "type" : "string", "format" : "uuid" @@ -5958,8 +6612,14 @@ "procedureData" : { "$ref" : "#/components/schemas/PostEmployeeOmsProcedureRequest" }, + "sendEmailNotifications" : { + "type" : "boolean" + }, "targetState" : { "$ref" : "#/components/schemas/ProcedureStatus" + }, + "waitingRoom" : { + "$ref" : "#/components/schemas/WaitingRoom" } } }, @@ -5974,6 +6634,13 @@ "format" : "uuid" } }, + "documentMap" : { + "type" : "object", + "additionalProperties" : { + "type" : "string", + "format" : "uuid" + } + }, "facilityId" : { "type" : "string", "format" : "uuid" @@ -6229,6 +6896,10 @@ "default" : "ASC", "enum" : [ "ASC", "DESC" ] }, + "ReviewResult" : { + "type" : "string", + "enum" : [ "ACCEPTED", "REJECTED" ] + }, "Salutation" : { "type" : "string", "enum" : [ "NOT_SPECIFIED", "NEUTRAL", "FEMALE", "MALE" ] @@ -6518,9 +7189,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -6590,6 +7261,58 @@ } } } + }, + "WaitingRoom" : { + "type" : "object", + "properties" : { + "info" : { + "type" : "string" + }, + "status" : { + "$ref" : "#/components/schemas/WaitingStatus" + } + } + }, + "WaitingRoomProcedure" : { + "required" : [ "dateOfBirth", "facilityName", "firstName", "id", "lastName", "modifiedAt", "waitingRoom" ], + "type" : "object", + "properties" : { + "dateOfBirth" : { + "type" : "string", + "format" : "date" + }, + "facilityName" : { + "type" : "string" + }, + "firstName" : { + "type" : "string" + }, + "id" : { + "type" : "string", + "format" : "uuid" + }, + "lastName" : { + "type" : "string" + }, + "modifiedAt" : { + "type" : "string", + "format" : "date-time" + }, + "physicianName" : { + "type" : "string" + }, + "waitingRoom" : { + "$ref" : "#/components/schemas/WaitingRoom" + } + } + }, + "WaitingRoomSortKey" : { + "type" : "string", + "enum" : [ "ID", "FIRSTNAME", "LASTNAME", "DATE_OF_BIRTH", "FACILITY", "PHYSICIAN", "STATUS", "INFO", "MODIFIED_AT" ] + }, + "WaitingStatus" : { + "type" : "string", + "enum" : [ "WAITING_FOR_CONSULTATION", "IN_CONSULTATION", "DONE" ] } } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/OfficialMedicalServiceApplication.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/OfficialMedicalServiceApplication.java index 141d856b4..a947dc88e 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/OfficialMedicalServiceApplication.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/OfficialMedicalServiceApplication.java @@ -8,6 +8,7 @@ package de.eshg.officialmedicalservice; import de.eshg.lib.common.BusinessModule; import de.eshg.officialmedicalservice.citizenpublic.DepartmentInfoProperties; import de.eshg.officialmedicalservice.citizenpublic.OpeningHoursProperties; +import de.eshg.officialmedicalservice.notification.NotificationProperties; import de.eshg.rest.service.security.config.OfficialMedicalServicePublicSecurityConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -17,7 +18,11 @@ import org.springframework.context.annotation.Import; @SpringBootApplication @Import(OfficialMedicalServicePublicSecurityConfig.class) -@EnableConfigurationProperties({OpeningHoursProperties.class, DepartmentInfoProperties.class}) +@EnableConfigurationProperties({ + OpeningHoursProperties.class, + DepartmentInfoProperties.class, + NotificationProperties.class +}) public class OfficialMedicalServiceApplication { @Bean diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/OmsAppointmentService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/OmsAppointmentService.java index 38bc715d8..99ae7a29f 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/OmsAppointmentService.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/OmsAppointmentService.java @@ -15,6 +15,7 @@ import de.eshg.officialmedicalservice.appointment.persistence.OmsAppointmentRepo import de.eshg.officialmedicalservice.appointment.persistence.entity.AppointmentState; import de.eshg.officialmedicalservice.appointment.persistence.entity.BookingState; import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment; +import de.eshg.officialmedicalservice.procedure.ProgressEntryService; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedureRepository; import de.eshg.rest.service.error.BadRequestException; @@ -35,16 +36,19 @@ public class OmsAppointmentService { private static final List<AppointmentTypeDto> supportedAppointmentTypes = List.of(AppointmentTypeDto.OFFICIAL_MEDICAL_SERVICE); + private final ProgressEntryService progressEntryService; public OmsAppointmentService( OmsProcedureRepository omsProcedureRepository, OmsAppointmentRepository omsAppointmentRepository, OmsAppointmentMapper omsAppointmentMapper, - AppointmentBlockSlotUtil appointmentBlockSlotUtil) { + AppointmentBlockSlotUtil appointmentBlockSlotUtil, + ProgressEntryService progressEntryService) { this.omsProcedureRepository = omsProcedureRepository; this.omsAppointmentRepository = omsAppointmentRepository; this.omsAppointmentMapper = omsAppointmentMapper; this.appointmentBlockSlotUtil = appointmentBlockSlotUtil; + this.progressEntryService = progressEntryService; } @Transactional @@ -74,6 +78,12 @@ public class OmsAppointmentService { omsAppointmentRepository.save(appointment); + if (bookingInfo != null) { + progressEntryService.createProgressEntryForAddingAppointmentWithBooking(procedure, request); + } else { + progressEntryService.createProgressEntryForAddingSelfBookingAppointment(procedure); + } + return appointment.getExternalId(); } @@ -90,6 +100,14 @@ public class OmsAppointmentService { throw new BadRequestException("Appointment is withdrawn"); } + if (BookingState.BOOKED == appointment.getBookingState()) { + progressEntryService.createProgressEntryForRebookedAppointment( + appointment.getProcedure(), appointment, request); + } else { + progressEntryService.createProgressEntryForBookingAppointment( + appointment.getProcedure(), request); + } + processBooking(request, appointment); } @@ -111,6 +129,8 @@ public class OmsAppointmentService { appointment.setBookingType(null); appointment.setDuration(null); appointment.setAppointment(null); // to unlock appointment block + progressEntryService.createProgressEntryForCancelingAppointment( + appointment.getProcedure(), appointment); } @Transactional @@ -126,6 +146,12 @@ public class OmsAppointmentService { if (BookingState.BOOKABLE == appointment.getBookingState()) { appointment.setBookingState(BookingState.WITHDRAWN); + + progressEntryService.createProgressEntryForWithdrawingAppointmentOption( + appointment.getProcedure()); + } else { + progressEntryService.createProgressEntryForClosingAppointment( + appointment.getProcedure(), appointment); } appointment.setAppointmentState(AppointmentState.CLOSED); diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/persistence/entity/AppointmentState.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/persistence/entity/AppointmentState.java index bc405ef86..7c23e4838 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/persistence/entity/AppointmentState.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/appointment/persistence/entity/AppointmentState.java @@ -6,6 +6,17 @@ package de.eshg.officialmedicalservice.appointment.persistence.entity; public enum AppointmentState { - OPEN, - CLOSED + OPEN("OFFEN"), + CLOSED("GESCHLOSSEN"), + ; + + private final String germanName; + + AppointmentState(String germanName) { + this.germanName = germanName; + } + + public String getGermanName() { + return germanName; + } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentController.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentController.java new file mode 100644 index 000000000..b3224e9ea --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentController.java @@ -0,0 +1,82 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document; + +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + +import de.eshg.officialmedicalservice.document.api.PatchDocumentInformationRequest; +import de.eshg.officialmedicalservice.document.api.PatchDocumentNoteRequest; +import de.eshg.officialmedicalservice.document.api.PatchDocumentReviewRequest; +import de.eshg.rest.service.security.config.BaseUrls; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import java.util.List; +import java.util.UUID; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +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(path = OmsDocumentController.BASE_URL, produces = MediaType.APPLICATION_JSON_VALUE) +@Tag(name = "OmsDocument") +public class OmsDocumentController { + public static final String BASE_URL = BaseUrls.OfficialMedicalService.EMPLOYEE_API; + public static final String DOCUMENT_URL = "/document"; + public static final String COMPLETE_FILE_UPLOAD_URL = "/complete-file-upload"; + public static final String NOTE_URL = "/note"; + public static final String REVIEW_URL = "/review"; + + private final OmsDocumentService omsDocumentService; + + public OmsDocumentController(OmsDocumentService omsDocumentService) { + this.omsDocumentService = omsDocumentService; + } + + @PatchMapping(path = DOCUMENT_URL + "/{id}") + @Operation(summary = "Updates information of one oms document") + public void patchDocumentInformation( + @PathVariable("id") UUID documentId, + @Valid @RequestBody PatchDocumentInformationRequest request) { + omsDocumentService.updateDocumentInformationEmployee(documentId, request); + } + + @PatchMapping( + path = DOCUMENT_URL + "/{id}" + COMPLETE_FILE_UPLOAD_URL, + consumes = MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "Completes file upload of one oms document") + public void patchCompleteDocumentFileUpload( + @PathVariable("id") UUID documentId, + @RequestPart(value = "files") List<MultipartFile> files) { + omsDocumentService.completeDocumentFileUploadEmployee(documentId, files); + } + + @DeleteMapping(path = DOCUMENT_URL + "/{id}") + @Operation(summary = "Deletes a document along with all files associated with it") + public void deleteDocumentEmployee(@PathVariable("id") UUID documentId) { + omsDocumentService.deleteDocumentEmployee(documentId); + } + + @PatchMapping(path = DOCUMENT_URL + "/{id}" + NOTE_URL) + @Operation(summary = "Updates note of one oms document") + public void patchDocumentNote( + @PathVariable("id") UUID documentId, @Valid @RequestBody PatchDocumentNoteRequest request) { + omsDocumentService.updateDocumentNoteEmployee(documentId, request); + } + + @PatchMapping(path = DOCUMENT_URL + "/{id}" + REVIEW_URL) + @Operation(summary = "Accepts or rejects a submitted document") + public void patchDocumentReview( + @PathVariable("id") UUID documentId, @Valid @RequestBody PatchDocumentReviewRequest request) { + omsDocumentService.reviewDocumentEmployee(documentId, request); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentMapper.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentMapper.java new file mode 100644 index 000000000..4e87f1c6a --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentMapper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document; + +import de.eshg.officialmedicalservice.document.api.DocumentDto; +import de.eshg.officialmedicalservice.document.api.DocumentStatusDto; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocumentStatus; +import de.eshg.officialmedicalservice.file.OmsFileMapper; +import java.util.Collections; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class OmsDocumentMapper { + private final OmsFileMapper omsFileMapper; + + public OmsDocumentMapper(OmsFileMapper omsFileMapper) { + this.omsFileMapper = omsFileMapper; + } + + public List<DocumentDto> toInterfaceType(List<OmsDocument> documentList) { + if (documentList == null) { + return Collections.emptyList(); + } + return documentList.stream() + .sorted( + (doc1, doc2) -> doc1.getDocumentTypeDe().compareToIgnoreCase(doc2.getDocumentTypeDe())) + .map(this::toInterfaceType) + .toList(); + } + + public DocumentDto toInterfaceType(OmsDocument document) { + if (document == null) { + return null; + } + return new DocumentDto( + document.getId(), + document.getDocumentTypeDe(), + document.getDocumentTypeEn(), + document.getHelpTextDe(), + document.getHelpTextEn(), + toInterfaceType(document.getDocumentStatus()), + document.getLastDocumentUpload(), + omsFileMapper.toInterfaceType(document.getFiles()), + document.getNote(), + document.isMandatoryDocument(), + document.isUploadInCitizenPortal(), + document.getReasonForRejection()); + } + + public DocumentStatusDto toInterfaceType(OmsDocumentStatus documentStatus) { + if (documentStatus == null) { + return null; + } + return DocumentStatusDto.valueOf(documentStatus.name()); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentService.java new file mode 100644 index 000000000..10987c63b --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/OmsDocumentService.java @@ -0,0 +1,295 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.springframework.http.MediaType.APPLICATION_PDF_VALUE; +import static org.springframework.http.MediaType.IMAGE_JPEG_VALUE; +import static org.springframework.http.MediaType.IMAGE_PNG_VALUE; + +import de.eshg.lib.procedure.model.FileTypeDto; +import de.eshg.officialmedicalservice.document.api.PatchDocumentInformationRequest; +import de.eshg.officialmedicalservice.document.api.PatchDocumentNoteRequest; +import de.eshg.officialmedicalservice.document.api.PatchDocumentReviewRequest; +import de.eshg.officialmedicalservice.document.api.PostDocumentRequest; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocumentRepository; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocumentStatus; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFile; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFileRepository; +import de.eshg.officialmedicalservice.procedure.OmsProgressEntryType; +import de.eshg.officialmedicalservice.procedure.ProgressEntryService; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedureRepository; +import de.eshg.rest.service.error.BadRequestException; +import de.eshg.rest.service.error.NotFoundException; +import java.io.IOException; +import java.time.Clock; +import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +public class OmsDocumentService { + private final OmsProcedureRepository omsProcedureRepository; + private final OmsDocumentRepository omsDocumentRepository; + private final OmsFileRepository omsFileRepository; + private final ProgressEntryService progressEntryService; + private final Clock clock; + + private static final Logger logger = LoggerFactory.getLogger(OmsDocumentService.class); + + public OmsDocumentService( + OmsProcedureRepository omsProcedureRepository, + OmsDocumentRepository omsDocumentRepository, + OmsFileRepository omsFileRepository, + ProgressEntryService progressEntryService, + Clock clock) { + this.omsProcedureRepository = omsProcedureRepository; + this.omsDocumentRepository = omsDocumentRepository; + this.omsFileRepository = omsFileRepository; + this.progressEntryService = progressEntryService; + this.clock = clock; + } + + @Transactional + public UUID addDocumentEmployee( + UUID externalId, PostDocumentRequest request, List<MultipartFile> files, String note) { + OmsProcedure omsProcedure = loadOmsProcedure(externalId); + + if (omsProcedure.isFinalized()) { + throw new BadRequestException("Document cannot be added when the procedure is finalized."); + } + validateFileTypes(files); + + OmsDocument document = new OmsDocument(); + document.setDocumentTypeDe(request.documentTypeDe()); + document.setDocumentTypeEn(request.documentTypeEn()); + document.setHelpTextDe(request.helpTextDe()); + document.setHelpTextEn(request.helpTextEn()); + + if (!files.isEmpty()) { + document.setReasonForRejection(null); + document.setDocumentStatus(OmsDocumentStatus.ACCEPTED); + document.setLastDocumentUpload(Instant.now(clock)); + document.setNote(note); + } else { + document.setDocumentStatus(OmsDocumentStatus.MISSING); + } + + document.setMandatoryDocument(request.mandatoryDocument()); + document.setUploadInCitizenPortal(request.uploadInCitizenPortal()); + + document.setOmsProcedure(omsProcedure); + omsDocumentRepository.save(document); + + saveFiles(document, files); + + if (document.isUploadInCitizenPortal() && document.getFiles().isEmpty()) { + progressEntryService.createProgressEntryAddDocumentEmployee( + omsProcedure, OmsProgressEntryType.DOCUMENT_MISSING_BY_CITIZEN, document); + } else if (!document.isUploadInCitizenPortal() && document.getFiles().isEmpty()) { + progressEntryService.createProgressEntryAddDocumentEmployee( + omsProcedure, OmsProgressEntryType.DOCUMENT_MISSING_BY_EMPLOYEE, document); + } else if (!document.isUploadInCitizenPortal() && !document.getFiles().isEmpty()) { + progressEntryService.createProgressEntryAddDocumentEmployee( + omsProcedure, OmsProgressEntryType.DOCUMENT_ACCEPTED, document); + } + + return document.getExternalId(); + } + + @Transactional + public void updateDocumentInformationEmployee( + UUID documentId, PatchDocumentInformationRequest request) { + OmsDocument omsDocument = loadOmsDocument(documentId); + + if (omsDocument.getOmsProcedure().isFinalized()) { + throw new BadRequestException( + "Document information cannot be updated when the procedure is finalized."); + } + if (omsDocument.getDocumentStatus() != OmsDocumentStatus.MISSING) { + throw new BadRequestException("Document information can only be updated in MISSING status"); + } + + String oldDocumentTypeDe = omsDocument.getDocumentTypeDe(); + String oldHelpTextDe = omsDocument.getHelpTextDe(); + omsDocument.setDocumentTypeDe(request.documentTypeDe()); + omsDocument.setDocumentTypeEn(request.documentTypeEn()); + omsDocument.setHelpTextDe(request.helpTextDe()); + omsDocument.setHelpTextEn(request.helpTextEn()); + omsDocument.setMandatoryDocument(request.mandatoryDocument()); + omsDocument.setUploadInCitizenPortal(request.uploadInCitizenPortal()); + + if (!Objects.equals(oldDocumentTypeDe, request.documentTypeDe()) + || !Objects.equals(oldHelpTextDe, request.helpTextDe())) { + OmsProcedure omsProcedure = omsDocument.getOmsProcedure(); + progressEntryService.createProgressEntryUpdateDocumentInformation( + omsProcedure, omsDocument, oldDocumentTypeDe, oldHelpTextDe); + } + } + + @Transactional + public void completeDocumentFileUploadEmployee(UUID documentId, List<MultipartFile> files) { + OmsDocument omsDocument = loadOmsDocument(documentId); + + if (omsDocument.getOmsProcedure().isFinalized()) { + throw new BadRequestException( + "File upload cannot be completed when the procedure is finalized."); + } + if (omsDocument.getDocumentStatus() == OmsDocumentStatus.ACCEPTED) { + throw new BadRequestException("Files can not be uploaded twice"); + } + validateFileTypes(files); + + saveFiles(omsDocument, files); + omsDocument.setReasonForRejection(null); + omsDocument.setDocumentStatus(OmsDocumentStatus.ACCEPTED); + omsDocument.setLastDocumentUpload(Instant.now(clock)); + + OmsProcedure omsProcedure = omsDocument.getOmsProcedure(); + progressEntryService.createProgressEntryCompleteDocumentFileUploadEmployee( + omsProcedure, omsDocument); + } + + @Transactional + public void deleteDocumentEmployee(UUID documentId) { + OmsDocument omsDocument = loadOmsDocument(documentId); + + OmsProcedure omsProcedure = omsDocument.getOmsProcedure(); + if (omsProcedure.isFinalized()) { + throw new BadRequestException("Documents cannot be deleted when the procedure is finalized."); + } + + deleteDocument(omsDocument); + progressEntryService.createProgressEntryForDocumentDeletion(omsProcedure, omsDocument); + } + + @Transactional + public void updateDocumentNoteEmployee(UUID documentId, PatchDocumentNoteRequest request) { + OmsDocument omsDocument = loadOmsDocument(documentId); + + if (omsDocument.getOmsProcedure().isFinalized()) { + throw new BadRequestException( + "Document note can not be updated when the procedure is finalized."); + } + if (omsDocument.getFiles().isEmpty()) { + throw new BadRequestException("Document note can not be updated when no files are uploaded"); + } + + omsDocument.setNote(request.note()); + } + + @Transactional + public void reviewDocumentEmployee(UUID documentId, PatchDocumentReviewRequest request) { + OmsDocument document = loadOmsDocument(documentId); + + if (document.getOmsProcedure().isFinalized()) { + throw new BadRequestException("Document cannot be reviewed when the procedure is finalized."); + } + if (document.getDocumentStatus() != OmsDocumentStatus.SUBMITTED) { + throw new BadRequestException("Only submitted documents can be reviewed"); + } + + switch (request.result()) { + case ACCEPTED: + if (!isBlank(request.reasonForRejection())) { + logger.warn( + "Ignoring reasonForRejection for accepted document: {}", + request.reasonForRejection()); + } + document.setReasonForRejection(null); + document.setDocumentStatus(OmsDocumentStatus.ACCEPTED); + break; + case REJECTED: + if (isBlank(request.reasonForRejection())) { + throw new BadRequestException("reasonForRejection must not be blank"); + } + deleteAllFiles(document); + document.setReasonForRejection(request.reasonForRejection()); + document.setDocumentStatus(OmsDocumentStatus.REJECTED); + } + } + + private void deleteAllFiles(OmsDocument document) { + omsFileRepository.deleteAll(document.getFiles()); + document.getFiles().clear(); + } + + private OmsProcedure loadOmsProcedure(UUID externalId) { + return omsProcedureRepository + .findByExternalId(externalId) + .orElseThrow(() -> new NotFoundException("Procedure not found")); + } + + private OmsDocument loadOmsDocument(UUID externalId) { + return omsDocumentRepository + .findById(externalId) + .orElseThrow(() -> new NotFoundException("Document not found")); + } + + private void validateFileTypes(List<MultipartFile> files) { + List<String> allowedFileTypes = + List.of(APPLICATION_PDF_VALUE, IMAGE_JPEG_VALUE, IMAGE_PNG_VALUE); + + for (MultipartFile file : files) { + String contentType = file.getContentType(); + if (!allowedFileTypes.contains(contentType)) { + throw new BadRequestException( + "Invalid file type: " + contentType + ". Only PDF, JPG, and PNG are allowed."); + } + } + } + + private byte[] getBytes(MultipartFile file) { + try { + return file.getBytes(); + } catch (IOException e) { + throw new BadRequestException("Corrupt file content"); + } + } + + private void saveFiles(OmsDocument omsDocument, List<MultipartFile> files) { + for (MultipartFile file : files) { + OmsFile omsFile = new OmsFile(); + omsFile.setFileName(file.getOriginalFilename()); + omsFile.setContent(getBytes(file)); + omsFile.setFileType(getFileType(file.getContentType())); + omsFile.setCreatedDate(Instant.now(clock)); + omsFile.setDocument(omsDocument); + + omsFileRepository.save(omsFile); + omsDocument.getFiles().add(omsFile); + } + } + + private FileTypeDto getFileType(String contentType) { + if (contentType == null || contentType.isBlank()) { + throw new BadRequestException("Content type is missing or empty."); + } + + return switch (contentType) { + case APPLICATION_PDF_VALUE -> FileTypeDto.PDF; + case IMAGE_JPEG_VALUE -> FileTypeDto.JPEG; + case IMAGE_PNG_VALUE -> FileTypeDto.PNG; + default -> + throw new BadRequestException( + "Invalid file type: " + contentType + ". Only PDF, JPG, and PNG are allowed."); + }; + } + + private void deleteDocument(OmsDocument omsDocument) { + OmsProcedure omsProcedure = omsDocument.getOmsProcedure(); + omsDocument.setOmsProcedure(null); + omsProcedure.getDocuments().remove(omsDocument); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentDto.java new file mode 100644 index 000000000..daa937687 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentDto.java @@ -0,0 +1,29 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import de.eshg.officialmedicalservice.file.api.OmsFileDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +@Schema(name = "Document") +public record DocumentDto( + @NotNull UUID id, + @NotNull String documentTypeDe, + String documentTypeEn, + String helpTextDe, + String helpTextEn, + @NotNull DocumentStatusDto documentStatus, + Instant lastDocumentUpload, + @NotNull @Valid List<OmsFileDto> files, + String note, + @NotNull boolean mandatoryDocument, + @NotNull boolean uploadInCitizenPortal, + String reasonForRejection) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentStatusDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentStatusDto.java new file mode 100644 index 000000000..fd428be5b --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/DocumentStatusDto.java @@ -0,0 +1,16 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "DocumentStatus") +public enum DocumentStatusDto { + MISSING, + SUBMITTED, + REJECTED, + ACCEPTED, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/GetDocumentsResponse.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/GetDocumentsResponse.java new file mode 100644 index 000000000..201d5c5dd --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/GetDocumentsResponse.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +public record GetDocumentsResponse(@NotNull @Valid List<DocumentDto> documents) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentInformationRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentInformationRequest.java new file mode 100644 index 000000000..84d590de2 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentInformationRequest.java @@ -0,0 +1,16 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import jakarta.validation.constraints.NotNull; + +public record PatchDocumentInformationRequest( + @NotNull String documentTypeDe, + String documentTypeEn, + String helpTextDe, + String helpTextEn, + @NotNull boolean mandatoryDocument, + @NotNull boolean uploadInCitizenPortal) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentNoteRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentNoteRequest.java new file mode 100644 index 000000000..845c1516a --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentNoteRequest.java @@ -0,0 +1,8 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +public record PatchDocumentNoteRequest(String note) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentReviewRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentReviewRequest.java new file mode 100644 index 000000000..9cf34016f --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PatchDocumentReviewRequest.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import jakarta.validation.constraints.NotNull; + +public record PatchDocumentReviewRequest( + @NotNull ReviewResultDto result, String reasonForRejection) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PostDocumentRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PostDocumentRequest.java new file mode 100644 index 000000000..5266ec8cb --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/PostDocumentRequest.java @@ -0,0 +1,16 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import jakarta.validation.constraints.NotNull; + +public record PostDocumentRequest( + @NotNull String documentTypeDe, + String documentTypeEn, + String helpTextDe, + String helpTextEn, + @NotNull boolean mandatoryDocument, + @NotNull boolean uploadInCitizenPortal) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/ReviewResultDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/ReviewResultDto.java new file mode 100644 index 000000000..72602a075 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/api/ReviewResultDto.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "ReviewResult") +public enum ReviewResultDto { + ACCEPTED, + REJECTED +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocument.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocument.java new file mode 100644 index 000000000..7cf239e4d --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocument.java @@ -0,0 +1,184 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.persistence.entity; + +import de.eshg.domain.model.GloballyUniqueEntityBase; +import de.eshg.lib.common.DataSensitivity; +import de.eshg.lib.common.SensitivityLevel; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFile; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFile_; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import org.hibernate.annotations.JdbcType; +import org.hibernate.dialect.PostgreSQLEnumJdbcType; + +@Entity +@Table(indexes = @Index(columnList = "oms_procedure_id")) +public class OmsDocument extends GloballyUniqueEntityBase { + + @ManyToOne(optional = false) + @JoinColumn(name = "oms_procedure_id") + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private OmsProcedure omsProcedure; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String documentTypeDe; + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String documentTypeEn; + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String helpTextDe; + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String helpTextEn; + + @Column(nullable = false) + @NotNull + @JdbcType(PostgreSQLEnumJdbcType.class) + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private OmsDocumentStatus documentStatus; + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private Instant lastDocumentUpload; + + @Column + @OneToMany( + mappedBy = OmsFile_.DOCUMENT, + cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, + orphanRemoval = true) + @OrderBy("createdDate") + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private final List<OmsFile> files = new ArrayList<>(); + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String note; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private boolean mandatoryDocument; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private boolean uploadInCitizenPortal; + + @Column + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String reasonForRejection; + + public OmsProcedure getOmsProcedure() { + return omsProcedure; + } + + public void setOmsProcedure(OmsProcedure omsProcedure) { + this.omsProcedure = omsProcedure; + } + + public String getDocumentTypeDe() { + return documentTypeDe; + } + + public void setDocumentTypeDe(String documentTypeDe) { + this.documentTypeDe = documentTypeDe; + } + + public String getDocumentTypeEn() { + return documentTypeEn; + } + + public void setDocumentTypeEn(String documentTypeEn) { + this.documentTypeEn = documentTypeEn; + } + + public String getHelpTextDe() { + return helpTextDe; + } + + public void setHelpTextDe(String helpTextDe) { + this.helpTextDe = helpTextDe; + } + + public String getHelpTextEn() { + return helpTextEn; + } + + public void setHelpTextEn(String helpTextEn) { + this.helpTextEn = helpTextEn; + } + + public OmsDocumentStatus getDocumentStatus() { + return documentStatus; + } + + public void setDocumentStatus(OmsDocumentStatus documentStatus) { + this.documentStatus = documentStatus; + } + + public Instant getLastDocumentUpload() { + return lastDocumentUpload; + } + + public void setLastDocumentUpload(Instant lastDocumentUpload) { + this.lastDocumentUpload = lastDocumentUpload; + } + + public List<OmsFile> getFiles() { + return files; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public boolean isMandatoryDocument() { + return mandatoryDocument; + } + + public void setMandatoryDocument(boolean mandatoryDocument) { + this.mandatoryDocument = mandatoryDocument; + } + + public boolean isUploadInCitizenPortal() { + return uploadInCitizenPortal; + } + + public void setUploadInCitizenPortal(boolean uploadInCitizenPortal) { + this.uploadInCitizenPortal = uploadInCitizenPortal; + } + + public String getReasonForRejection() { + return reasonForRejection; + } + + public void setReasonForRejection(String reasonForRejection) { + this.reasonForRejection = reasonForRejection; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentRepository.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentRepository.java new file mode 100644 index 000000000..96c4bc5f2 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentRepository.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.persistence.entity; + +import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OmsDocumentRepository extends JpaRepository<OmsDocument, UUID> {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentStatus.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentStatus.java new file mode 100644 index 000000000..30fc9e6fa --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/document/persistence/entity/OmsDocumentStatus.java @@ -0,0 +1,13 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.document.persistence.entity; + +public enum OmsDocumentStatus { + MISSING, + SUBMITTED, + REJECTED, + ACCEPTED, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileController.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileController.java new file mode 100644 index 000000000..033fe94c5 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileController.java @@ -0,0 +1,48 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file; + +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.util.UUID; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path = OmsFileController.BASE_URL, produces = MediaType.APPLICATION_JSON_VALUE) +@Tag(name = "OmsFile") +public class OmsFileController { + public static final String BASE_URL = BaseUrls.OfficialMedicalService.EMPLOYEE_API; + public static final String FILE_URL = "/file"; + + private final OmsFileService omsFileService; + + public OmsFileController(OmsFileService omsFileService) { + this.omsFileService = omsFileService; + } + + @GetMapping(path = FILE_URL + "/{fileId}") + @Operation(summary = "Download file from oms document.") + @Transactional(readOnly = true) + @ApiResponse( + responseCode = "200", + content = + @Content( + mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE, + schema = @Schema(format = "binary"))) + public ResponseEntity<byte[]> getDownloadFile(@PathVariable("fileId") UUID fileId) { + return omsFileService.downloadDocumentFileEmployee(fileId); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileMapper.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileMapper.java new file mode 100644 index 000000000..332757b67 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file; + +import de.eshg.officialmedicalservice.file.api.OmsFileDto; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFile; +import java.util.Collections; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class OmsFileMapper { + + public List<OmsFileDto> toInterfaceType(List<OmsFile> files) { + if (files == null) { + return Collections.emptyList(); + } + return files.stream() + .sorted((file1, file2) -> file1.getFileName().compareToIgnoreCase(file2.getFileName())) + .map(this::toInterfaceType) + .toList(); + } + + public OmsFileDto toInterfaceType(OmsFile file) { + if (file == null) { + return null; + } + return new OmsFileDto( + file.getExternalId(), + file.getFileName(), + file.getFileType(), + file.getContent().length, + file.getCreatedDate()); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileService.java new file mode 100644 index 000000000..1dc6010d0 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/OmsFileService.java @@ -0,0 +1,60 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file; + +import de.eshg.lib.procedure.model.FileTypeDto; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFile; +import de.eshg.officialmedicalservice.file.persistence.entity.OmsFileRepository; +import de.eshg.rest.service.error.BadRequestException; +import de.eshg.rest.service.error.NotFoundException; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class OmsFileService { + + private final OmsFileRepository omsFileRepository; + + public OmsFileService(OmsFileRepository omsFileRepository) { + this.omsFileRepository = omsFileRepository; + } + + @Transactional + public ResponseEntity<byte[]> downloadDocumentFileEmployee(UUID fileId) { + OmsFile omsFile = loadOmsFile(fileId); + + ContentDisposition contentDisposition = + ContentDisposition.attachment() + .filename(omsFile.getFileName(), StandardCharsets.UTF_8) + .build(); + + return ResponseEntity.ok() + .contentType(getMediaType(omsFile.getFileType())) + .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()) + .body(omsFile.getContent()); + } + + private OmsFile loadOmsFile(UUID fileId) { + return omsFileRepository + .findById(fileId) + .orElseThrow(() -> new NotFoundException("File not found")); + } + + private MediaType getMediaType(FileTypeDto fileType) { + return switch (fileType) { + case JPEG -> MediaType.IMAGE_JPEG; + case PNG -> MediaType.IMAGE_PNG; + case PDF -> MediaType.APPLICATION_PDF; + default -> throw new BadRequestException("Invalid file type"); + }; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/api/OmsFileDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/api/OmsFileDto.java new file mode 100644 index 000000000..f09ae8fe2 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/api/OmsFileDto.java @@ -0,0 +1,20 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file.api; + +import de.eshg.lib.procedure.model.FileTypeDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import java.util.UUID; + +@Schema(name = "OmsFile") +public record OmsFileDto( + @NotNull UUID id, + @NotNull String name, + @NotNull FileTypeDto fileType, + @NotNull int size, + @NotNull Instant creationDate) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFile.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFile.java new file mode 100644 index 000000000..427d1d1d0 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFile.java @@ -0,0 +1,93 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file.persistence.entity; + +import de.eshg.domain.model.GloballyUniqueEntityBase; +import de.eshg.lib.common.DataSensitivity; +import de.eshg.lib.common.SensitivityLevel; +import de.eshg.lib.procedure.model.FileTypeDto; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import org.hibernate.annotations.JdbcType; +import org.hibernate.dialect.PostgreSQLEnumJdbcType; + +@Entity +@Table(indexes = @Index(columnList = "document_id")) +public class OmsFile extends GloballyUniqueEntityBase { + + @ManyToOne(optional = false) + @JoinColumn(name = "document_id") + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private OmsDocument document; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private String fileName; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private byte[] content; + + @Column(nullable = false) + @NotNull + @JdbcType(PostgreSQLEnumJdbcType.class) + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private FileTypeDto fileType; + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private Instant createdDate; + + public OmsDocument getDocument() { + return document; + } + + public void setDocument(OmsDocument document) { + this.document = document; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public byte[] getContent() { + return content; + } + + public void setContent(byte[] content) { + this.content = content; + } + + public FileTypeDto getFileType() { + return fileType; + } + + public void setFileType(FileTypeDto fileType) { + this.fileType = fileType; + } + + public Instant getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Instant createdDate) { + this.createdDate = createdDate; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFileRepository.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFileRepository.java new file mode 100644 index 000000000..74c06b5e9 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/file/persistence/entity/OmsFileRepository.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.file.persistence.entity; + +import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OmsFileRepository extends JpaRepository<OmsFile, UUID> {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/MailClient.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/MailClient.java new file mode 100644 index 000000000..77ff8f8f2 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/MailClient.java @@ -0,0 +1,32 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.notification; + +import de.eshg.base.mail.MailApi; +import de.eshg.base.mail.SendEmailRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class MailClient { + private static final Logger log = LoggerFactory.getLogger(MailClient.class); + + private final MailApi mailApi; + + public MailClient(MailApi mailApi) { + this.mailApi = mailApi; + } + + void sendMail(String to, String from, String subject, String text) { + log.info("Sending E-Mail notification"); + + SendEmailRequest sendEmailRequest = new SendEmailRequest(to, from, subject, text); + mailApi.sendEmail(sendEmailRequest); + + log.info("E-Mail notification sent"); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationProperties.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationProperties.java new file mode 100644 index 000000000..54c844465 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationProperties.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.notification; + +import jakarta.validation.constraints.NotNull; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@Validated +@ConfigurationProperties(prefix = "de.eshg.official-medical-service.notification") +public record NotificationProperties(@NotNull String fromAddress, @NotNull String greeting) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationService.java new file mode 100644 index 000000000..65a59066f --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationService.java @@ -0,0 +1,138 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.notification; + +import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator; +import de.eshg.officialmedicalservice.procedure.api.AffectedPersonDto; +import java.util.List; +import java.util.function.IntSupplier; +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; + +@Service +public class NotificationService { + + private static final Logger log = LoggerFactory.getLogger(NotificationService.class); + + private static final String NOTIFICATION_IS_DISABLED = + "Für diesen Vorgang ist der Mailversand deaktiviert."; + private static final String MISSING_ADDRESSES = + "Für diese Person sind keine Mailadressen erfasst."; + + public record NotificationSummary(String subject, int numSentMails, String notSentBecause) { + @Override + public String toString() { + return "Benachrichtigung mit Betreff '" + + subject + + "': " + + (notSentBecause == null + ? "gesendet an " + numSentMails + " Mailadresse(n)" + : "nicht versendet: " + notSentBecause); + } + } + + private static final String CITIZEN_PORTAL_OMS_LOGIN_PATH = + "amtsärztlichegutachten/allesnurgeraten/meine-termine"; + + private final ModuleClientAuthenticator moduleClientAuthenticator; + private final MailClient mailClient; + private final NotificationProperties notificationProperties; + private final NotificationText notificationText; + private final String citizenPortalUrl; + private final SecurityContextHolderStrategy securityContextHolderStrategy = + SecurityContextHolder.getContextHolderStrategy(); + + @FunctionalInterface + public interface MailEnabledProvider { + boolean isSendEmailNotifications(); + } + + public NotificationService( + ModuleClientAuthenticator moduleClientAuthenticator, + MailClient mailClient, + NotificationProperties notificationProperties, + NotificationText notificationText, + @Value("${eshg.citizen-portal.reverse-proxy.url}") String citizenPortalUrl) { + this.moduleClientAuthenticator = moduleClientAuthenticator; + this.mailClient = mailClient; + this.notificationProperties = notificationProperties; + this.notificationText = notificationText; + this.citizenPortalUrl = citizenPortalUrl; + } + + public NotificationSummary notifyNewCitizenUser( + MailEnabledProvider mailEnabledProvider, AffectedPersonDto person, String accessCode) { + + String newCitizenUserSubject = notificationText.getNewCitizenUserSubject(); + String newCitizenUserBody = + notificationText.assembleNewCitizenUserBody( + person.firstName(), + person.lastName(), + buildLoginUrl(accessCode), + accessCode, + notificationProperties.greeting()); + return doNotification( + mailEnabledProvider, + person, + newCitizenUserSubject, + () -> + sendMailWithModuleClientAuthentication( + newCitizenUserSubject, newCitizenUserBody, person)); + } + + private final NotificationSummary doNotification( + MailEnabledProvider procedure, + AffectedPersonDto person, + String subject, + IntSupplier sendMail) { + + if (!procedure.isSendEmailNotifications()) { + return new NotificationSummary(subject, 0, NOTIFICATION_IS_DISABLED); + } + List<String> mailAddresses = person.emailAddresses(); + if (mailAddresses.isEmpty()) { + return new NotificationSummary(subject, 0, MISSING_ADDRESSES); + } + + int numSentMails = sendMail.getAsInt(); + return new NotificationSummary(subject, numSentMails, null); + } + + private final int sendMailWithModuleClientAuthentication( + String subject, String body, AffectedPersonDto personDto) { + SecurityContext previousContext = securityContextHolderStrategy.getContext(); + try { + securityContextHolderStrategy.clearContext(); + return moduleClientAuthenticator.doWithModuleClientAuthentication( + () -> doSendMail(subject, body, personDto)); + } finally { + securityContextHolderStrategy.setContext(previousContext); + } + } + + private final int doSendMail(String subject, String body, AffectedPersonDto personDto) { + log.info("send mail(s): " + subject); + + for (String emailAddress : personDto.emailAddresses()) { + mailClient.sendMail(emailAddress, notificationProperties.fromAddress(), subject, body); + } + return personDto.emailAddresses().size(); + } + + private String buildLoginUrl(String accessCode) { + return UriComponentsBuilder.fromUriString(citizenPortalUrl) + .pathSegment(CITIZEN_PORTAL_OMS_LOGIN_PATH) + .queryParam("access_code", accessCode) + .build() + .toUriString(); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationText.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationText.java new file mode 100644 index 000000000..d4cfeee63 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/notification/NotificationText.java @@ -0,0 +1,52 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.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.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 final DateTimeFormatter appointmentStartFormat = + DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm", Locale.GERMAN); + + @Value("${de.eshg.official-medical-service.notification.template.new_citizen_user.subject}") + private String newCitizenUserSubject; + + @Value("${de.eshg.official-medical-service.notification.template.new_citizen_user.body}") + private Resource newCitizenUserBodyTemplate; + + public String getNewCitizenUserSubject() { + return newCitizenUserSubject; + } + + public String assembleNewCitizenUserBody( + String firstName, String lastName, String loginUrl, String accessCode, String greeting) { + + String templateBody = readTemplateBody(newCitizenUserBodyTemplate); + + return String.format(templateBody, firstName, lastName, loginUrl, accessCode, 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/official-medical-service/src/main/java/de/eshg/officialmedicalservice/person/PersonClient.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/person/PersonClient.java index 138e2d696..c0f7b352f 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/person/PersonClient.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/person/PersonClient.java @@ -8,17 +8,20 @@ package de.eshg.officialmedicalservice.person; import de.eshg.base.centralfile.PersonApi; 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.SyncFileStateRequest; import de.eshg.base.centralfile.api.person.UpdatePersonRequest; +import de.eshg.officialmedicalservice.procedure.api.AffectedPersonDto; import de.eshg.rest.service.error.BadRequestException; import de.eshg.rest.service.error.ErrorCode; import de.eshg.rest.service.error.ErrorResponse; import java.util.UUID; import java.util.function.Supplier; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; @@ -58,6 +61,27 @@ public class PersonClient { return doAndForwardErrorCodes(() -> personApi.getPersonDiff(fileStateId)); } + public UUID createPersonFromExternalSource(AffectedPersonDto person) { + ExternalAddPersonFileStateRequest request = + new ExternalAddPersonFileStateRequest( + StringUtils.trimToNull(person.title()), + person.salutation(), + person.gender(), + person.firstName().trim(), + person.lastName().trim(), + person.dateOfBirth(), + StringUtils.trimToNull(person.nameAtBirth()), + StringUtils.trimToNull(person.placeOfBirth()), + person.countryOfBirth(), + person.emailAddresses(), + person.phoneNumbers(), + person.contactAddress(), + null); + AddPersonFileStateResponse personFromExternalSource = + personApi.addPersonFromExternalSource(request); + return personFromExternalSource.id(); + } + private <T> T doAndForwardErrorCodes(Supplier<T> action) { try { return action.get(); diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureController.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureController.java index 5ce09297d..13d991b2a 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureController.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureController.java @@ -5,28 +5,41 @@ package de.eshg.officialmedicalservice.procedure; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + import de.eshg.api.commons.InlineParameterObject; import de.eshg.lib.procedure.api.ProcedureSearchParameters; import de.eshg.officialmedicalservice.appointment.OmsAppointmentService; import de.eshg.officialmedicalservice.appointment.api.PostOmsAppointmentRequest; +import de.eshg.officialmedicalservice.document.OmsDocumentService; +import de.eshg.officialmedicalservice.document.api.GetDocumentsResponse; +import de.eshg.officialmedicalservice.document.api.PostDocumentRequest; +import de.eshg.officialmedicalservice.procedure.api.AcceptDraftProcedureResponse; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureDetailsDto; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureHeaderDto; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedurePaginationAndSortParameters; import de.eshg.officialmedicalservice.procedure.api.EmployeePagedOmsProcedures; import de.eshg.officialmedicalservice.procedure.api.GetEmployeeOmsProcedureOverviewResponse; import de.eshg.officialmedicalservice.procedure.api.GetOmsProceduresFilterOptionsDto; +import de.eshg.officialmedicalservice.procedure.api.MedicalOpinionStatusDto; import de.eshg.officialmedicalservice.procedure.api.PatchAffectedPersonRequest; import de.eshg.officialmedicalservice.procedure.api.PatchConcernRequest; +import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedureEmailNotificationsRequest; import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedureFacilityRequest; import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedurePhysicianRequest; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureFacilityRequest; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureRequest; import de.eshg.officialmedicalservice.procedure.api.SyncAffectedPersonRequest; import de.eshg.officialmedicalservice.procedure.api.SyncFacilityRequest; +import de.eshg.officialmedicalservice.waitingroom.WaitingRoomService; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomDto; import de.eshg.rest.service.security.config.BaseUrls; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import java.util.UUID; import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.MediaType; @@ -38,7 +51,9 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping( @@ -56,15 +71,25 @@ public class EmployeeOmsProcedureController { public static final String CLOSE_PROCEDURE_URL = "/close"; public static final String PHYSICIAN_URL = "/physician"; public static final String APPOINTMENT_URL = "/appointment"; + public static final String DOCUMENT_URL = "/document"; + public static final String MEDICAL_OPINION_STATUS_URL = "/medical-opinion-status"; + public static final String EMAIL_NOTIFICATIONS_URL = "/email-notifications"; + public static final String WAITING_ROOM_URL = "/waiting-room"; private final EmployeeOmsProcedureService employeeOmsProcedureService; private final OmsAppointmentService omsAppointmentService; + private final OmsDocumentService omsDocumentService; + private final WaitingRoomService waitingRoomService; public EmployeeOmsProcedureController( EmployeeOmsProcedureService employeeOmsProcedureService, - OmsAppointmentService omsAppointmentService) { + OmsAppointmentService omsAppointmentService, + OmsDocumentService omsDocumentService, + WaitingRoomService waitingRoomService) { this.employeeOmsProcedureService = employeeOmsProcedureService; this.omsAppointmentService = omsAppointmentService; + this.omsDocumentService = omsDocumentService; + this.waitingRoomService = waitingRoomService; } @PostMapping(path = PROCEDURES_URL) @@ -155,8 +180,8 @@ public class EmployeeOmsProcedureController { @PatchMapping(path = PROCEDURES_URL + "/{id}" + ACCEPT_DRAFT_URL) @Operation(summary = "Accept draft oms procedure") - public void acceptDraftProcedure(@PathVariable("id") UUID procedureId) { - employeeOmsProcedureService.acceptDraftProcedure(procedureId); + public AcceptDraftProcedureResponse acceptDraftProcedure(@PathVariable("id") UUID procedureId) { + return employeeOmsProcedureService.acceptDraftProcedure(procedureId); } @PatchMapping(path = PROCEDURES_URL + "/{id}" + CLOSE_PROCEDURE_URL) @@ -179,4 +204,43 @@ public class EmployeeOmsProcedureController { @PathVariable("id") UUID procedureId, @Valid @RequestBody PostOmsAppointmentRequest request) { return omsAppointmentService.addAppointmentEmployee(procedureId, request); } + + @PostMapping(path = PROCEDURES_URL + "/{id}" + DOCUMENT_URL, consumes = MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "Add a document to an oms procedure") + public UUID postDocument( + @PathVariable("id") UUID id, + @RequestPart("postDocumentRequest") @Valid PostDocumentRequest request, + @RequestPart(value = "files", required = false) List<MultipartFile> files, + @RequestPart(value = "note", required = false) String note) { + return omsDocumentService.addDocumentEmployee( + id, request, Optional.ofNullable(files).orElse(Collections.emptyList()), note); + } + + @GetMapping(path = PROCEDURES_URL + "/{id}" + DOCUMENT_URL) + @Operation(summary = "Get all documents for one oms procedure") + public GetDocumentsResponse getAllDocuments(@PathVariable("id") UUID id) { + return employeeOmsProcedureService.getAllDocuments(id); + } + + @PatchMapping(path = PROCEDURES_URL + "/{id}" + MEDICAL_OPINION_STATUS_URL) + @Operation(summary = "Updates the medical opinion status of a oms procedure") + public void patchMedicalOpinionStatus( + @PathVariable("id") UUID externalId, @Valid @RequestBody MedicalOpinionStatusDto request) { + employeeOmsProcedureService.updateMedicalOpinionStatus(externalId, request); + } + + @PatchMapping(path = PROCEDURES_URL + "/{id}" + EMAIL_NOTIFICATIONS_URL) + @Operation(summary = "Updates the whether or not email notifications will be sent") + public void patchEmailNotifications( + @PathVariable("id") UUID externalId, + @Valid @RequestBody PatchEmployeeOmsProcedureEmailNotificationsRequest request) { + employeeOmsProcedureService.patchEmailNotifications(externalId, request); + } + + @PatchMapping(path = PROCEDURES_URL + "/{id}" + WAITING_ROOM_URL) + @Operation(summary = "Update waiting room details for a procedure") + public void patchWaitingRoom( + @PathVariable("id") UUID id, @Valid @RequestBody WaitingRoomDto request) { + waitingRoomService.updateWaitingRoom(id, request); + } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureService.java index a0e4df614..a30405312 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureService.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/EmployeeOmsProcedureService.java @@ -22,6 +22,7 @@ import de.eshg.base.centralfile.api.person.AddPersonFileStateResponse; 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.citizenuser.api.CitizenAccessCodeUserDto; import de.eshg.base.user.api.UserDto; import de.eshg.lib.auditlog.AuditLogger; import de.eshg.lib.procedure.api.ProcedureSearchParameters; @@ -34,15 +35,20 @@ import de.eshg.lib.procedure.mapping.ProcedureMapper; import de.eshg.lib.procedure.model.ProcedureStatusDto; import de.eshg.lib.procedure.procedures.ProcedureSearchService; import de.eshg.lib.procedure.util.ProcedureValidator; +import de.eshg.lib.rest.oauth.client.commons.ModuleClientAuthenticator; import de.eshg.officialmedicalservice.appointment.OmsAppointmentMapper; import de.eshg.officialmedicalservice.appointment.persistence.entity.AppointmentState; import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment; import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment_; import de.eshg.officialmedicalservice.concern.ConcernMapper; +import de.eshg.officialmedicalservice.document.OmsDocumentMapper; +import de.eshg.officialmedicalservice.document.api.GetDocumentsResponse; import de.eshg.officialmedicalservice.facility.FacilityClient; import de.eshg.officialmedicalservice.facility.FacilityMapper; +import de.eshg.officialmedicalservice.notification.NotificationService; import de.eshg.officialmedicalservice.person.PersonClient; import de.eshg.officialmedicalservice.person.PersonMapper; +import de.eshg.officialmedicalservice.procedure.api.AcceptDraftProcedureResponse; import de.eshg.officialmedicalservice.procedure.api.AffectedPersonDto; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureDetailsDto; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureHeaderDto; @@ -52,8 +58,10 @@ import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureSortKey; import de.eshg.officialmedicalservice.procedure.api.EmployeePagedOmsProcedures; import de.eshg.officialmedicalservice.procedure.api.FacilityDto; import de.eshg.officialmedicalservice.procedure.api.GetOmsProceduresFilterOptionsDto; +import de.eshg.officialmedicalservice.procedure.api.MedicalOpinionStatusDto; import de.eshg.officialmedicalservice.procedure.api.PatchAffectedPersonRequest; import de.eshg.officialmedicalservice.procedure.api.PatchConcernRequest; +import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedureEmailNotificationsRequest; import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedureFacilityRequest; import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedurePhysicianRequest; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureFacilityRequest; @@ -63,12 +71,15 @@ import de.eshg.officialmedicalservice.procedure.api.SyncFacilityRequest; import de.eshg.officialmedicalservice.procedure.persistence.entity.Concern; import de.eshg.officialmedicalservice.procedure.persistence.entity.Concern_; import de.eshg.officialmedicalservice.procedure.persistence.entity.Facility; +import de.eshg.officialmedicalservice.procedure.persistence.entity.MedicalOpinionStatus; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedureRepository; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedureView; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure_; import de.eshg.officialmedicalservice.procedure.persistence.entity.Person; +import de.eshg.officialmedicalservice.user.CitizenAccessCodeUserClient; import de.eshg.officialmedicalservice.user.UserClient; +import de.eshg.officialmedicalservice.waitingroom.WaitingRoomMapper; import de.eshg.rest.service.error.BadRequestException; import de.eshg.rest.service.error.NotFoundException; import de.eshg.rest.service.security.CurrentUserHelper; @@ -98,8 +109,12 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +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.transaction.annotation.Transactional; @@ -116,6 +131,12 @@ public class EmployeeOmsProcedureService { private final ProgressEntryService progressEntryService; private final EntityManager entityManager; private final ProcedureSearchService<OmsProcedure> procedureSearchService; + private final OmsDocumentMapper omsDocumentMapper; + private final NotificationService notificationService; + private final SecurityContextHolderStrategy securityContextHolderStrategy = + SecurityContextHolder.getContextHolderStrategy(); + private final ModuleClientAuthenticator moduleClientAuthenticator; + private final CitizenAccessCodeUserClient citizenAccessCodeUserClient; public EmployeeOmsProcedureService( OmsProcedureRepository omsProcedureRepository, @@ -128,7 +149,11 @@ public class EmployeeOmsProcedureService { UserClient userClient, ProgressEntryService progressEntryService, EntityManager entityManager, - ProcedureSearchService<OmsProcedure> procedureSearchService) { + ProcedureSearchService<OmsProcedure> procedureSearchService, + OmsDocumentMapper omsDocumentMapper, + NotificationService notificationService, + ModuleClientAuthenticator moduleClientAuthenticator, + CitizenAccessCodeUserClient citizenAccessCodeUserClient) { this.omsProcedureRepository = omsProcedureRepository; this.omsProcedureOverviewMapper = omsProcedureOverviewMapper; this.omsAppointmentMapper = omsAppointmentMapper; @@ -140,6 +165,10 @@ public class EmployeeOmsProcedureService { this.progressEntryService = progressEntryService; this.entityManager = entityManager; this.procedureSearchService = procedureSearchService; + this.omsDocumentMapper = omsDocumentMapper; + this.notificationService = notificationService; + this.moduleClientAuthenticator = moduleClientAuthenticator; + this.citizenAccessCodeUserClient = citizenAccessCodeUserClient; } @Transactional @@ -193,12 +222,17 @@ public class EmployeeOmsProcedureService { omsProcedureAndAffectedPerson.omsProcedure.getExternalId(), ProcedureMapper.toInterfaceType( omsProcedureAndAffectedPerson.omsProcedure.getProcedureStatus()), + MedicalOpinionStatusDto.valueOf( + omsProcedureAndAffectedPerson.omsProcedure.getMedicalOpinionStatus().name()), + WaitingRoomMapper.mapToDto(omsProcedureAndAffectedPerson.omsProcedure.getWaitingRoom()), omsProcedureAndAffectedPerson.affectedPerson, facility, mapToConcernDto(omsProcedureAndAffectedPerson.omsProcedure.getConcern()), physician.orElse(null), omsAppointmentMapper.toInterfaceType( - omsProcedureAndAffectedPerson.omsProcedure.getAppointments())); + omsProcedureAndAffectedPerson.omsProcedure.getAppointments()), + omsProcedureAndAffectedPerson.omsProcedure.isSendEmailNotifications(), + omsProcedureAndAffectedPerson.omsProcedure.getCitizenUserId()); } @Transactional(readOnly = true) @@ -255,6 +289,17 @@ public class EmployeeOmsProcedureService { return new EmployeePagedOmsProcedures(result, omsProcedureOverviewDtos.size()); } + // temporarily skip the current authentication + private <T> T disabledCurrentAuthentication(Supplier<T> supplier) { + SecurityContext oldContext = securityContextHolderStrategy.getContext(); + try { + securityContextHolderStrategy.clearContext(); + return supplier.get(); + } finally { + securityContextHolderStrategy.setContext(oldContext); + } + } + private Stream<OmsProcedureView> convertToProcedureViewStream(OmsProcedure procedure) { Concern concern = procedure.getConcern(); List<OmsAppointment> appointments = procedure.getAppointments(); @@ -427,7 +472,7 @@ public class EmployeeOmsProcedureService { @Transactional public void abortDraftProcedure(UUID externalId) { - OmsProcedure omsProcedure = loadOmsProcedure(externalId); + OmsProcedure omsProcedure = loadOmsProcedureForUpdate(externalId); if (omsProcedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Procedure is not in DRAFT status"); @@ -437,19 +482,43 @@ public class EmployeeOmsProcedureService { } @Transactional - public void acceptDraftProcedure(UUID externalId) { - OmsProcedure omsProcedure = loadOmsProcedureForUpdate(externalId); + public AcceptDraftProcedureResponse acceptDraftProcedure(UUID externalId) { + OmsProcedureAndAffectedPerson omsProcedureAndAffectedPerson = + getOmsProcedureAndAffectedPerson(externalId); + OmsProcedure omsProcedure = omsProcedureAndAffectedPerson.omsProcedure(); if (omsProcedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Procedure is not in DRAFT status"); } requireFacility(omsProcedure); requireConcern(omsProcedure); + AffectedPersonDto affectedPersonDto = omsProcedureAndAffectedPerson.affectedPerson(); + UUID personFileStateId = personClient.createPersonFromExternalSource(affectedPersonDto); + + // temporarily skip the access token authentication (and use a module authentication + // instead) in order to create a citizen keycloak user + CitizenAccessCodeUserDto citizenAccessCodeUser = + disabledCurrentAuthentication( + () -> + moduleClientAuthenticator.doWithModuleClientAuthentication( + () -> citizenAccessCodeUserClient.addCitizenAccessCodeUser(personFileStateId))); + String accessCode = citizenAccessCodeUser.accessCode(); + + omsProcedure.setCitizenUserId(citizenAccessCodeUser.userId()); omsProcedure.updateProcedureStatus(ProcedureStatus.OPEN, clock, auditLogger); + + NotificationService.NotificationSummary notificationSummary = + notificationService.notifyNewCitizenUser( + omsProcedure::isSendEmailNotifications, affectedPersonDto, accessCode); + omsProcedure.addProgressEntry( createSystemProgressEntry( - OmsProgressEntryType.PROCEDURE_STARTED.name(), TriggerType.EMPLOYEE)); + OmsProgressEntryType.PROCEDURE_STARTED.name(), + notificationSummary.toString(), + TriggerType.EMPLOYEE)); + + return new AcceptDraftProcedureResponse(accessCode); } @Transactional @@ -457,7 +526,7 @@ public class EmployeeOmsProcedureService { OmsProcedure omsProcedure = loadOmsProcedureForUpdate(externalID); if (omsProcedure.isFinalized()) { - throw new BadRequestException("Procedure is already in CLOSED status"); + throw new BadRequestException("Procedure is already finalized."); } if (omsProcedure.getProcedureStatus() != ProcedureStatus.OPEN) { throw new BadRequestException("Procedure is not in OPEN status"); @@ -474,7 +543,8 @@ public class EmployeeOmsProcedureService { OmsProcedure omsProcedure = loadOmsProcedureForUpdate(externalId); if (omsProcedure.isFinalized()) { - throw new BadRequestException("Affected person can not be edited in CLOSED status"); + throw new BadRequestException( + "Affected person can not be edited when the procedure is finalized."); } Person person = omsProcedure.findAffectedPerson(); @@ -509,7 +579,8 @@ public class EmployeeOmsProcedureService { OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); if (procedure.isFinalized()) { - throw new BadRequestException("Affected person can not be synced in CLOSED status"); + throw new BadRequestException( + "Affected person can not be synced when the procedure is finalized."); } Person person = procedure.getRelatedPersons().getFirst(); @@ -523,7 +594,7 @@ public class EmployeeOmsProcedureService { @Transactional public UUID addFacility(UUID externalId, PostEmployeeOmsProcedureFacilityRequest request) { - OmsProcedure procedure = loadOmsProcedure(externalId); + OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); if (procedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Facility can only be added in DRAFT status"); @@ -548,7 +619,7 @@ public class EmployeeOmsProcedureService { OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); if (procedure.isFinalized()) { - throw new BadRequestException("Facility can not be edited in CLOSED status"); + throw new BadRequestException("Facility can not be edited when the procedure is finalized."); } if (procedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Facility can only be synced in DRAFT status"); @@ -584,7 +655,7 @@ public class EmployeeOmsProcedureService { OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); if (procedure.isFinalized()) { - throw new BadRequestException("Facility can not be synced in CLOSED status"); + throw new BadRequestException("Facility can not be synced when the procedure is finalized."); } if (procedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Facility can only be synced in DRAFT status"); @@ -605,10 +676,10 @@ public class EmployeeOmsProcedureService { @Transactional public UUID modifyPhysician(UUID externalId, PatchEmployeeOmsProcedurePhysicianRequest request) { - OmsProcedure procedure = loadOmsProcedure(externalId); + OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); if (procedure.isFinalized()) { - throw new BadRequestException("A physician can not be set in CLOSED status"); + throw new BadRequestException("A physician can not be set when the procedure is finalized."); } UUID newPhysicianId = request.physicianId(); @@ -625,7 +696,7 @@ public class EmployeeOmsProcedureService { OmsProcedure omsProcedure = loadOmsProcedureForUpdate(externalId); if (omsProcedure.isFinalized()) { - throw new BadRequestException("Concern can not be edited in CLOSED status"); + throw new BadRequestException("Concern can not be edited when the procedure is finalized."); } if (omsProcedure.getProcedureStatus() != ProcedureStatus.DRAFT) { throw new BadRequestException("Procedure is not in DRAFT status"); @@ -641,6 +712,33 @@ public class EmployeeOmsProcedureService { } } + @Transactional(readOnly = true) + public GetDocumentsResponse getAllDocuments(UUID externalId) { + OmsProcedure omsProcedure = loadOmsProcedure(externalId); + return new GetDocumentsResponse(omsDocumentMapper.toInterfaceType(omsProcedure.getDocuments())); + } + + @Transactional + public void updateMedicalOpinionStatus( + UUID externalId, MedicalOpinionStatusDto medicalOpinionStatus) { + OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); + + if (procedure.isFinalized()) { + throw new BadRequestException( + "Medical opinion status can not be updated when the procedure is finalized."); + } + + procedure.setMedicalOpinionStatus(MedicalOpinionStatus.valueOf(medicalOpinionStatus.name())); + } + + @Transactional + public void patchEmailNotifications( + UUID externalId, PatchEmployeeOmsProcedureEmailNotificationsRequest request) { + OmsProcedure procedure = loadOmsProcedureForUpdate(externalId); + + procedure.setSendEmailNotifications(request.sendEmailNotifications()); + } + private OmsProcedure loadOmsProcedure(UUID externalId) { return omsProcedureRepository .findByExternalId(externalId) diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProcedureOverviewMapper.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProcedureOverviewMapper.java index d4f915c27..f37115070 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProcedureOverviewMapper.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProcedureOverviewMapper.java @@ -18,10 +18,13 @@ import de.eshg.lib.procedure.domain.model.TaskType; import de.eshg.lib.procedure.mapping.ProcedureMapper; import de.eshg.officialmedicalservice.concern.ConcernMapper; import de.eshg.officialmedicalservice.procedure.api.EmployeeOmsProcedureOverviewDto; +import de.eshg.officialmedicalservice.procedure.api.MedicalOpinionStatusDto; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureRequest; +import de.eshg.officialmedicalservice.procedure.persistence.entity.MedicalOpinionStatus; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsTask; import de.eshg.officialmedicalservice.procedure.persistence.entity.Person; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom; import java.time.Clock; import java.time.Instant; import java.time.LocalDate; @@ -48,6 +51,8 @@ public class OmsProcedureOverviewMapper { procedure.setProcedureType(ProcedureType.OFFICIAL_MEDICAL_SERVICE); procedure.updateProcedureStatus(ProcedureStatus.DRAFT, clock, auditLogger); + procedure.setMedicalOpinionStatus(MedicalOpinionStatus.IN_PROGRESS); + procedure.setWaitingRoom(new WaitingRoom()); OmsTask omsTask = new OmsTask(); if (currentUserId != null) { @@ -94,6 +99,7 @@ public class OmsProcedureOverviewMapper { return new EmployeeOmsProcedureOverviewDto( procedure.getExternalId(), ProcedureMapper.toInterfaceType(procedure.getProcedureStatus()), + MedicalOpinionStatusDto.valueOf(procedure.getMedicalOpinionStatus().name()), firstName, lastName, dateOfBirth, diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProgressEntryType.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProgressEntryType.java index c947331e2..1423ce0e9 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProgressEntryType.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/OmsProgressEntryType.java @@ -10,5 +10,18 @@ public enum OmsProgressEntryType { SYNC_AFFECTED_PERSON, SYNC_FACILITY, PHYSICIAN_CHANGED, - PROCEDURE_STARTED + PROCEDURE_STARTED, + DOCUMENT_DELETED, + APPOINTMENT_FOR_SELF_BOOKING_ADDED, + APPOINTMENT_ADDED_WITH_BOOKING, + APPOINTMENT_BOOKED, + APPOINTMENT_REBOOKED, + APPOINTMENT_CANCELED, + APPOINTMENT_OPTION_WITHDRAWN, + APPOINTMENT_CLOSED, + DOCUMENT_MISSING_BY_CITIZEN, + DOCUMENT_MISSING_BY_EMPLOYEE, + DOCUMENT_ACCEPTED, + DOCUMENT_INFORMATION_CHANGED, + DOCUMENT_STATUS_CHANGE_ACCEPTED } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/ProgressEntryService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/ProgressEntryService.java index 6d85cca93..468513594 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/ProgressEntryService.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/ProgressEntryService.java @@ -5,29 +5,32 @@ package de.eshg.officialmedicalservice.procedure; -import de.eshg.base.user.UserApi; import de.eshg.base.user.api.UserDto; import de.eshg.lib.procedure.domain.factory.SystemProgressEntryFactory; import de.eshg.lib.procedure.domain.model.SystemProgressEntry; import de.eshg.lib.procedure.domain.model.TriggerType; +import de.eshg.officialmedicalservice.appointment.api.BookingInfoDto; +import de.eshg.officialmedicalservice.appointment.api.PostOmsAppointmentRequest; +import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument; import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; import java.time.Clock; import java.time.format.DateTimeFormatter; import java.util.UUID; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @Service public class ProgressEntryService { private final Clock clock; - private final UserApi userApi; private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"); private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy"); + private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); - public ProgressEntryService(Clock clock, UserApi userApi) { + public ProgressEntryService(Clock clock) { this.clock = clock; - this.userApi = userApi; } public void createProgressEntryForUpdateAffectedPerson( @@ -72,4 +75,197 @@ public class ProgressEntryService { progressEntry.setProcedureId(procedure.getId()); procedure.addProgressEntry(progressEntry); } + + public void createProgressEntryForAddingSelfBookingAppointment(OmsProcedure procedure) { + String note = "Ein neuer Termin zum Selbstbuchen wurde hinzugefügt."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_FOR_SELF_BOOKING_ADDED.name(), + note, + TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForAddingAppointmentWithBooking( + OmsProcedure procedure, PostOmsAppointmentRequest appointment) { + + String note = + "Ein neuer Termin mit Buchung für den " + + dateFormatter.format(appointment.bookingInfo().start().atZone(clock.getZone())) + + " um " + + timeFormatter.format(appointment.bookingInfo().start().atZone(clock.getZone())) + + " Uhr wurde hinzugefügt."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_ADDED_WITH_BOOKING.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForRebookedAppointment( + OmsProcedure procedure, OmsAppointment oldAppointment, BookingInfoDto bookingInfo) { + String note = + "Der Termin vom " + + dateFormatter.format(oldAppointment.getStart().atZone(clock.getZone())) + + " um " + + timeFormatter.format(oldAppointment.getStart().atZone(clock.getZone())) + + " Uhr wurde auf " + + dateFormatter.format(bookingInfo.start().atZone(clock.getZone())) + + " um " + + timeFormatter.format(bookingInfo.start().atZone(clock.getZone())) + + " Uhr umgebucht."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_REBOOKED.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForBookingAppointment( + OmsProcedure procedure, BookingInfoDto bookingInfo) { + String note = + "Ein Termin wurde für " + + dateFormatter.format(bookingInfo.start().atZone(clock.getZone())) + + " um " + + timeFormatter.format(bookingInfo.start().atZone(clock.getZone())) + + " Uhr gebucht."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_BOOKED.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForCancelingAppointment( + OmsProcedure procedure, OmsAppointment appointment) { + String note = + "Der Termin vom " + + dateFormatter.format(appointment.getStart().atZone(clock.getZone())) + + " um " + + timeFormatter.format(appointment.getStart().atZone(clock.getZone())) + + " Uhr wurde abgesagt."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_CANCELED.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForWithdrawingAppointmentOption(OmsProcedure procedure) { + String note = "Eine Terminoption wurde zurückgezogen."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_OPTION_WITHDRAWN.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForClosingAppointment( + OmsProcedure procedure, OmsAppointment omsAppointment) { + String note = + "Der Termin vom " + + dateFormatter.format(omsAppointment.getStart().atZone(clock.getZone())) + + " um " + + timeFormatter.format(omsAppointment.getStart().atZone(clock.getZone())) + + " wurde im Buchungsstatus " + + omsAppointment.getAppointmentState().getGermanName() + + " abgeschlossen."; + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.APPOINTMENT_CLOSED.name(), note, TriggerType.EMPLOYEE); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryAddDocumentEmployee( + OmsProcedure omsProcedure, + OmsProgressEntryType omsProgressEntryType, + OmsDocument omsDocument) { + + String action = ""; + if (omsProgressEntryType == OmsProgressEntryType.DOCUMENT_MISSING_BY_CITIZEN) { + action = "für Upload durch Bürger:In hinzugefügt."; + } else if (omsProgressEntryType == OmsProgressEntryType.DOCUMENT_MISSING_BY_EMPLOYEE) { + action = "hinzugefügt."; + } else if (omsProgressEntryType == OmsProgressEntryType.DOCUMENT_ACCEPTED) { + action = "hinzugefügt und Dateie(n) hochgeladen."; + } + + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + omsProgressEntryType.name(), + "Dokument %s %s %s" + .formatted( + omsDocument.getDocumentTypeDe(), + !StringUtils.isBlank(omsDocument.getHelpTextDe()) + ? "- " + omsDocument.getHelpTextDe() + : "", + action), + TriggerType.EMPLOYEE); + omsProcedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryUpdateDocumentInformation( + OmsProcedure omsProcedure, + OmsDocument omsDocument, + String oldDocumentTypeDe, + String oldHelpTextDe) { + + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.DOCUMENT_INFORMATION_CHANGED.name(), + "Dokument %s %s wurde in %s %s umbenannt." + .formatted( + oldDocumentTypeDe, + !StringUtils.isBlank(oldHelpTextDe) ? "- " + oldHelpTextDe : "", + omsDocument.getDocumentTypeDe(), + !StringUtils.isBlank(omsDocument.getHelpTextDe()) + ? "- " + omsDocument.getHelpTextDe() + : ""), + TriggerType.EMPLOYEE); + omsProcedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryCompleteDocumentFileUploadEmployee( + OmsProcedure omsProcedure, OmsDocument omsDocument) { + + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.DOCUMENT_STATUS_CHANGE_ACCEPTED.name(), + "Für Dokument %s %s wurden Dateien hinzugefügt." + .formatted( + omsDocument.getDocumentTypeDe(), + !StringUtils.isBlank(omsDocument.getHelpTextDe()) + ? "- " + omsDocument.getHelpTextDe() + : ""), + TriggerType.EMPLOYEE); + omsProcedure.addProgressEntry(progressEntry); + } + + public void createProgressEntryForDocumentDeletion(OmsProcedure procedure, OmsDocument document) { + String changeDescription = + new StringBuilder() + .append("Dokument ") + .append(assembleDocumentDescription(document)) + .append(" gelöscht.") + .toString(); + + SystemProgressEntry progressEntry = + SystemProgressEntryFactory.createSystemProgressEntry( + OmsProgressEntryType.DOCUMENT_DELETED.name(), + changeDescription, + TriggerType.SYSTEM_AUTOMATIC); + progressEntry.setProcedureId(procedure.getId()); + procedure.addProgressEntry(progressEntry); + } + + private String assembleDocumentDescription(OmsDocument document) { + StringBuilder documentDescription = new StringBuilder(); + documentDescription.append(document.getDocumentTypeDe()); + String helpTextDe = document.getHelpTextDe(); + if (helpTextDe != null && !helpTextDe.isEmpty()) + documentDescription.append(" - ").append(helpTextDe); + return documentDescription.toString(); + } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/AcceptDraftProcedureResponse.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/AcceptDraftProcedureResponse.java new file mode 100644 index 000000000..4f880b525 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/AcceptDraftProcedureResponse.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.procedure.api; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +@Schema(name = "AcceptDraftProcedureResponse") +public record AcceptDraftProcedureResponse(@NotNull String accessCode) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureDetailsDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureDetailsDto.java index 19b3c676c..b5c719966 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureDetailsDto.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureDetailsDto.java @@ -8,6 +8,7 @@ package de.eshg.officialmedicalservice.procedure.api; import de.eshg.base.user.api.UserDto; import de.eshg.lib.procedure.model.ProcedureStatusDto; import de.eshg.officialmedicalservice.appointment.api.OmsAppointmentDto; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomDto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -18,8 +19,12 @@ import java.util.UUID; public record EmployeeOmsProcedureDetailsDto( @NotNull UUID id, @NotNull ProcedureStatusDto status, + @NotNull MedicalOpinionStatusDto medicalOpinionStatus, + @NotNull @Valid WaitingRoomDto waitingRoom, @NotNull @Valid AffectedPersonDto affectedPerson, @Valid FacilityDto facility, @Valid ConcernDto concern, @Valid UserDto physician, - @NotNull @Valid List<OmsAppointmentDto> appointments) {} + @NotNull @Valid List<OmsAppointmentDto> appointments, + @NotNull boolean sendEmailNotifications, + UUID citizenUserId) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureOverviewDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureOverviewDto.java index b2e1824d7..9f64ed0bb 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureOverviewDto.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/EmployeeOmsProcedureOverviewDto.java @@ -17,6 +17,7 @@ import java.util.UUID; public record EmployeeOmsProcedureOverviewDto( @NotNull UUID id, @NotNull ProcedureStatusDto status, + @NotNull MedicalOpinionStatusDto medicalOpinionStatus, String firstName, String lastName, LocalDate dateOfBirth, diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/FacilityDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/FacilityDto.java index 72b658cbb..80c1af83f 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/FacilityDto.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/FacilityDto.java @@ -5,9 +5,9 @@ package de.eshg.officialmedicalservice.procedure.api; -import de.eshg.CustomValidations.MandatoryEmailAddressConstraint; import de.eshg.base.address.AddressDto; import de.eshg.base.centralfile.api.facility.FacilityContactPersonDto; +import de.eshg.validation.constraints.MandatoryEmailAddressConstraint; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/MedicalOpinionStatusDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/MedicalOpinionStatusDto.java new file mode 100644 index 000000000..3d4499f29 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/MedicalOpinionStatusDto.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.procedure.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "MedicalOpinionStatus") +public enum MedicalOpinionStatusDto { + IN_PROGRESS, + ACCOMPLISHED, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/PatchEmployeeOmsProcedureEmailNotificationsRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/PatchEmployeeOmsProcedureEmailNotificationsRequest.java new file mode 100644 index 000000000..a031e63ec --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/api/PatchEmployeeOmsProcedureEmailNotificationsRequest.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.procedure.api; + +import jakarta.validation.constraints.NotNull; + +public record PatchEmployeeOmsProcedureEmailNotificationsRequest( + @NotNull boolean sendEmailNotifications) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/MedicalOpinionStatus.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/MedicalOpinionStatus.java new file mode 100644 index 000000000..74465fb63 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/MedicalOpinionStatus.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.procedure.persistence.entity; + +public enum MedicalOpinionStatus { + IN_PROGRESS, + ACCOMPLISHED, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedure.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedure.java index afe5c74b7..400d3d883 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedure.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedure.java @@ -5,12 +5,18 @@ package de.eshg.officialmedicalservice.procedure.persistence.entity; +import static de.eshg.lib.common.SensitivityLevel.PSEUDONYMIZED; + import de.cronn.commons.lang.StreamUtil; import de.eshg.lib.common.DataSensitivity; import de.eshg.lib.common.SensitivityLevel; import de.eshg.lib.procedure.domain.model.Procedure; import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment; import de.eshg.officialmedicalservice.appointment.persistence.entity.OmsAppointment_; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocument_; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom_; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -19,11 +25,14 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderBy; import jakarta.persistence.Transient; +import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.JdbcType; +import org.hibernate.dialect.PostgreSQLEnumJdbcType; @Entity public class OmsProcedure extends Procedure<OmsProcedure, OmsTask, Person, Facility> { @@ -45,6 +54,37 @@ public class OmsProcedure extends Procedure<OmsProcedure, OmsTask, Person, Facil @Column private UUID physicianId; + @OneToMany( + mappedBy = OmsDocument_.OMS_PROCEDURE, + cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, + orphanRemoval = true) + @OrderBy + @BatchSize(size = 100) + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + private final List<OmsDocument> documents = new ArrayList<>(); + + @Column(nullable = false) + @NotNull + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + @JdbcType(PostgreSQLEnumJdbcType.class) + private MedicalOpinionStatus medicalOpinionStatus; + + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + @Column(nullable = false) + private boolean sendEmailNotifications = true; + + @OneToOne( + optional = false, + cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, + mappedBy = WaitingRoom_.PROCEDURE, + orphanRemoval = true) + @DataSensitivity(PSEUDONYMIZED) + private WaitingRoom waitingRoom; + + @DataSensitivity(SensitivityLevel.PSEUDONYMIZED) + @Column + private UUID citizenUserId; + public Person findAffectedPerson() { if (getRelatedPersons().isEmpty()) { return null; @@ -76,4 +116,41 @@ public class OmsProcedure extends Procedure<OmsProcedure, OmsTask, Person, Facil public List<OmsAppointment> getAppointments() { return appointments; } + + public List<OmsDocument> getDocuments() { + return documents; + } + + public MedicalOpinionStatus getMedicalOpinionStatus() { + return medicalOpinionStatus; + } + + public void setMedicalOpinionStatus(MedicalOpinionStatus medicalOpinionStatus) { + this.medicalOpinionStatus = medicalOpinionStatus; + } + + public boolean isSendEmailNotifications() { + return sendEmailNotifications; + } + + public void setSendEmailNotifications(boolean sendEmailNotifications) { + this.sendEmailNotifications = sendEmailNotifications; + } + + public WaitingRoom getWaitingRoom() { + return waitingRoom; + } + + public void setWaitingRoom(WaitingRoom waitingRoom) { + this.waitingRoom = waitingRoom; + waitingRoom.setProcedure(this); + } + + public UUID getCitizenUserId() { + return citizenUserId; + } + + public void setCitizenUserId(UUID citizenUserId) { + this.citizenUserId = citizenUserId; + } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedureRepository.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedureRepository.java index 88a320e19..84d711233 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedureRepository.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/procedure/persistence/entity/OmsProcedureRepository.java @@ -6,5 +6,47 @@ package de.eshg.officialmedicalservice.procedure.persistence.entity; import de.eshg.lib.procedure.domain.repository.ProcedureRepository; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; -public interface OmsProcedureRepository extends ProcedureRepository<OmsProcedure> {} +public interface OmsProcedureRepository extends ProcedureRepository<OmsProcedure> { + @Query( + """ + select o from OmsProcedure o + where exists ( + select 1 from o.relatedPersons p + where p.centralFileStateId in :centralFileStateIds + ) + order by o.id + """) + Stream<OmsProcedure> findByRelatedPersons( + @Param("centralFileStateIds") List<UUID> centralFileStateIds); + + @Query( + """ + select o from OmsProcedure o + where exists ( + select 1 from o.relatedFacilities f + where f.centralFileStateId in :centralFileStateIds + ) + order by o.id + """) + Stream<OmsProcedure> findByRelatedFacility( + @Param("centralFileStateIds") List<UUID> centralFileStateIds); + + @Query( + """ + select distinct o.physicianId from OmsProcedure o where o.physicianId is not null + """) + List<UUID> findDistinctPhysicianIds(); + + @Query( + """ + select o from OmsProcedure o + where o.waitingRoom.status in ('WAITING_FOR_CONSULTATION', 'IN_CONSULTATION') + """) + Stream<OmsProcedure> findAllByWaitingRoomStatusInWaitingOrInConsultation(); +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsDocumentTestHelperFile.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsDocumentTestHelperFile.java new file mode 100644 index 000000000..56d855ec6 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsDocumentTestHelperFile.java @@ -0,0 +1,65 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.testhelper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import org.springframework.web.multipart.MultipartFile; + +public class OmsDocumentTestHelperFile implements MultipartFile { + private final String name; + private final String contentType; + private final File file; + + public OmsDocumentTestHelperFile(String name, String contentType, File file) { + this.name = name; + this.contentType = contentType; + this.file = file; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalFilename() { + return file.getName(); + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public boolean isEmpty() { + return file.length() == 0; + } + + @Override + public long getSize() { + return file.length(); + } + + @Override + public byte[] getBytes() throws IOException { + return Files.readAllBytes(file.toPath()); + } + + @Override + public InputStream getInputStream() throws IOException { + return new FileInputStream(file); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + Files.copy(file.toPath(), dest.toPath()); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsTestHelperController.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsTestHelperController.java index 773aca0b8..88ecbc7e3 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsTestHelperController.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/OmsTestHelperController.java @@ -5,7 +5,7 @@ package de.eshg.officialmedicalservice.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.officialmedicalservice.testhelper.api.PostPopulateAdministrativeResponse; import de.eshg.officialmedicalservice.testhelper.api.PostPopulateProcedureRequest; @@ -16,7 +16,6 @@ import de.eshg.testhelper.TestHelperController; import de.eshg.testhelper.environment.EnvironmentConfig; import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; -import java.io.IOException; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.service.annotation.PostExchange; @@ -24,7 +23,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class OmsTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { public static final String TEST_POPULATION_PATH = "/population"; public static final String TEST_POPULATION_URL = TestHelperApi.BASE_URL + TEST_POPULATION_PATH; @@ -58,12 +57,7 @@ public class OmsTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/TestPopulateProcedureService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/TestPopulateProcedureService.java index 987edab21..aae239a79 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/TestPopulateProcedureService.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/TestPopulateProcedureService.java @@ -11,17 +11,34 @@ import static de.eshg.lib.procedure.model.ProcedureStatusDto.OPEN; import de.eshg.officialmedicalservice.appointment.OmsAppointmentService; import de.eshg.officialmedicalservice.concern.ConcernMapper; import de.eshg.officialmedicalservice.concern.ConcernService; +import de.eshg.officialmedicalservice.document.OmsDocumentService; +import de.eshg.officialmedicalservice.document.api.DocumentStatusDto; +import de.eshg.officialmedicalservice.document.api.PatchDocumentReviewRequest; +import de.eshg.officialmedicalservice.document.api.ReviewResultDto; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocumentRepository; +import de.eshg.officialmedicalservice.document.persistence.entity.OmsDocumentStatus; import de.eshg.officialmedicalservice.procedure.EmployeeOmsProcedureService; import de.eshg.officialmedicalservice.procedure.api.ConcernDto; import de.eshg.officialmedicalservice.procedure.api.PatchConcernRequest; +import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedureEmailNotificationsRequest; import de.eshg.officialmedicalservice.procedure.api.PatchEmployeeOmsProcedurePhysicianRequest; import de.eshg.officialmedicalservice.testhelper.api.AppointmentPopulationDto; +import de.eshg.officialmedicalservice.testhelper.api.DocumentPopulationDto; import de.eshg.officialmedicalservice.testhelper.api.PostPopulateProcedureRequest; import de.eshg.officialmedicalservice.testhelper.api.PostPopulateProcedureResponse; +import de.eshg.officialmedicalservice.waitingroom.WaitingRoomService; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.population.PopulateWithAccessTokenHelper; import jakarta.transaction.Transactional; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -29,6 +46,7 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; /* Entities still to handle: @@ -96,19 +114,25 @@ public class TestPopulateProcedureService { private final ConcernService concernService; private final OmsAppointmentService appointmentService; private final PopulateWithAccessTokenHelper populateWithAccessTokenHelper; - private final OmsAppointmentService omsAppointmentService; + private final OmsDocumentService omsDocumentService; + private final OmsDocumentRepository omsDocumentRepository; + private final WaitingRoomService waitingRoomService; public TestPopulateProcedureService( EmployeeOmsProcedureService employeeOmsProcedureService, ConcernService concernService, OmsAppointmentService appointmentService, PopulateWithAccessTokenHelper populateWithAccessTokenHelper, - OmsAppointmentService omsAppointmentService) { + OmsDocumentService omsDocumentService, + OmsDocumentRepository omsDocumentRepository, + WaitingRoomService waitingRoomService) { this.employeeOmsProcedureService = employeeOmsProcedureService; this.concernService = concernService; this.appointmentService = appointmentService; this.populateWithAccessTokenHelper = populateWithAccessTokenHelper; - this.omsAppointmentService = omsAppointmentService; + this.omsDocumentService = omsDocumentService; + this.omsDocumentRepository = omsDocumentRepository; + this.waitingRoomService = waitingRoomService; } @Transactional @@ -119,17 +143,26 @@ public class TestPopulateProcedureService { UUID procedureId; UUID facilityId = null; Map<String, UUID> appointmentMap; + Map<String, UUID> documentMap = new HashMap<>(); // 1. create procedure procedureId = employeeOmsProcedureService.createEmployeeProcedure((request.procedureData())); - // 2. add facility + // 2. Deactivate email notifications + if (request.sendEmailNotifications() != null) { + employeeOmsProcedureService.patchEmailNotifications( + procedureId, + new PatchEmployeeOmsProcedureEmailNotificationsRequest( + request.sendEmailNotifications())); + } + + // 3. add facility if (request.facility() != null) { facilityId = employeeOmsProcedureService.addFacility(procedureId, request.facility()); } - // 3. add concern + // 4. add concern if (request.concern() != null) { ConcernDto concern = concernService.getConcerns().categories().stream() @@ -152,32 +185,47 @@ public class TestPopulateProcedureService { procedureId, new PatchConcernRequest(concern)); } - // 4. add physicians + // 5. add physician if (request.physician() != null) { employeeOmsProcedureService.modifyPhysician( procedureId, new PatchEmployeeOmsProcedurePhysicianRequest(request.physician())); } - // 5. start procedure + // 6. start procedure if (Arrays.asList(OPEN, CLOSED).contains(request.targetState())) { employeeOmsProcedureService.acceptDraftProcedure(procedureId); } - // 6. add appointments + // 7. add appointments appointmentMap = addAppointments(procedureId, request.appointments()); - // 7. cancel appointments + // 8. cancel appointments cancelAppointments(request.cancelledAppointments(), appointmentMap); - // 8. close appointments + // 9. close appointments closeAppointments(request.closedAppointments(), appointmentMap); - // 9. close procedure + // 10. add documents + documentMap = addDocuments(procedureId, request.documents()); + + // 11. update medical opinion status + if (request.medicalOpinionStatus() != null) { + employeeOmsProcedureService.updateMedicalOpinionStatus( + procedureId, request.medicalOpinionStatus()); + } + + // 12. update waiting room + if (request.waitingRoom() != null) { + waitingRoomService.updateWaitingRoom(procedureId, request.waitingRoom()); + } + + // 13. close procedure if (Objects.equals(CLOSED, request.targetState())) { employeeOmsProcedureService.closeOpenProcedure(procedureId); } - return new PostPopulateProcedureResponse(procedureId, facilityId, appointmentMap); + return new PostPopulateProcedureResponse( + procedureId, facilityId, appointmentMap, documentMap); }); } @@ -188,7 +236,7 @@ public class TestPopulateProcedureService { appointmentPopulations.forEach( population -> { UUID appointmentId = - omsAppointmentService.addAppointmentEmployee(procedureId, population.request()); + appointmentService.addAppointmentEmployee(procedureId, population.request()); appointmentMap.put(population.key(), appointmentId); }); } @@ -221,4 +269,67 @@ public class TestPopulateProcedureService { appointmentService.closeAppointmentEmployee(appointmentId); }); } + + private Map<String, UUID> addDocuments( + UUID procedureId, List<DocumentPopulationDto> documentPopulation) { + Map<String, UUID> documentMap = new LinkedHashMap<>(); + if (documentPopulation != null) { + documentPopulation.forEach( + document -> { + List<MultipartFile> filesToAdd = new ArrayList<>(); + String note = null; + if (document.targetState() == DocumentStatusDto.ACCEPTED + || document.targetState() == DocumentStatusDto.SUBMITTED) { + document + .files() + .forEach( + config -> { + try { + Path filePath = + Paths.get( + getClass() + .getClassLoader() + .getResource("documents/" + config.getName()) + .toURI()); + File file = filePath.toFile(); + + filesToAdd.add( + new OmsDocumentTestHelperFile( + file.getName(), Files.probeContentType(file.toPath()), file)); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException( + "Fehler beim Laden der Testdatei: " + config.getName(), e); + } + }); + + if (!document.files().isEmpty()) { + note = document.note(); + } + } + + UUID documentId = + omsDocumentService.addDocumentEmployee( + procedureId, document.request(), filesToAdd, note); + + // TODO: use document service once citizen portal document service functions exist + if (DocumentStatusDto.SUBMITTED == document.targetState() + || DocumentStatusDto.REJECTED == document.targetState()) { + omsDocumentRepository + .findById(documentId) + .orElseThrow() + .setDocumentStatus(OmsDocumentStatus.SUBMITTED); + } + + if (document.targetState() == DocumentStatusDto.REJECTED) { + omsDocumentService.reviewDocumentEmployee( + documentId, + new PatchDocumentReviewRequest( + ReviewResultDto.REJECTED, document.reasonForRejection())); + } + + documentMap.put(document.key(), documentId); + }); + } + return documentMap; + } } diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/DocumentPopulationDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/DocumentPopulationDto.java new file mode 100644 index 000000000..c688185d9 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/DocumentPopulationDto.java @@ -0,0 +1,23 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.testhelper.api; + +import de.eshg.officialmedicalservice.document.api.DocumentStatusDto; +import de.eshg.officialmedicalservice.document.api.PostDocumentRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(name = "DocumentPopulation") +public record DocumentPopulationDto( + @NotBlank String key, + @NotNull @Valid PostDocumentRequest request, + DocumentStatusDto targetState, + String reasonForRejection, + List<FileTestDataConfig> files, + String note) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/FileTestDataConfig.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/FileTestDataConfig.java new file mode 100644 index 000000000..1ed5eef2b --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/FileTestDataConfig.java @@ -0,0 +1,22 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.testhelper.api; + +public enum FileTestDataConfig { + PDF("testHelperSampleDocument.pdf"), + JPG("testHelperSampleImage.jpg"), + PNG("testHelperSampleImage.png"); + + String name; + + FileTestDataConfig(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureRequest.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureRequest.java index 6d0413795..fd6cad8c2 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureRequest.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureRequest.java @@ -6,8 +6,10 @@ package de.eshg.officialmedicalservice.testhelper.api; import de.eshg.lib.procedure.model.ProcedureStatusDto; +import de.eshg.officialmedicalservice.procedure.api.MedicalOpinionStatusDto; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureFacilityRequest; import de.eshg.officialmedicalservice.procedure.api.PostEmployeeOmsProcedureRequest; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomDto; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.util.List; @@ -21,4 +23,9 @@ public record PostPopulateProcedureRequest( @Valid List<@Valid AppointmentPopulationDto> appointments, List<String> cancelledAppointments, List<String> closedAppointments, - ProcedureStatusDto targetState) {} + @Valid List<DocumentPopulationDto> documents, + MedicalOpinionStatusDto medicalOpinionStatus, + ProcedureStatusDto targetState, + Boolean sendEmailNotifications, + @Valid WaitingRoomDto waitingRoom, + String citizenUserId) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureResponse.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureResponse.java index c838f507d..440a6cfa5 100644 --- a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureResponse.java +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/testhelper/api/PostPopulateProcedureResponse.java @@ -11,4 +11,7 @@ import java.util.Map; import java.util.UUID; public record PostPopulateProcedureResponse( - @NotNull UUID procedureId, UUID facilityId, @NotNull @Valid Map<String, UUID> appointments) {} + @NotNull UUID procedureId, + UUID facilityId, + @NotNull @Valid Map<String, UUID> appointments, + @Valid Map<String, UUID> documentMap) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/user/CitizenAccessCodeUserClient.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/user/CitizenAccessCodeUserClient.java new file mode 100644 index 000000000..243900c24 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/user/CitizenAccessCodeUserClient.java @@ -0,0 +1,35 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.user; + +import de.eshg.base.citizenuser.CitizenAccessCodeUserApi; +import de.eshg.base.citizenuser.api.AddCitizenAccessCodeUserWithDateOfBirthCredentialRequest; +import de.eshg.base.citizenuser.api.CitizenAccessCodeUserDto; +import java.util.UUID; +import org.springframework.stereotype.Component; + +@Component +public class CitizenAccessCodeUserClient { + private final CitizenAccessCodeUserApi citizenAccessCodeUserApi; + + public CitizenAccessCodeUserClient(CitizenAccessCodeUserApi citizenAccessCodeUserApi) { + this.citizenAccessCodeUserApi = citizenAccessCodeUserApi; + } + + public CitizenAccessCodeUserDto addCitizenAccessCodeUser(UUID personFileStateId) { + AddCitizenAccessCodeUserWithDateOfBirthCredentialRequest request = + new AddCitizenAccessCodeUserWithDateOfBirthCredentialRequest(personFileStateId); + return citizenAccessCodeUserApi.addCitizenAccessCodeUserWithDateOfBirthCredential(request); + } + + public CitizenAccessCodeUserDto getCitizenAccessCode(UUID citizenUserId) { + return citizenAccessCodeUserApi.getCitizenAccessCodeUser(citizenUserId); + } + + public void deleteCitizenAccessCodeUser(UUID citizenUserId) { + citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(citizenUserId); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/PagedWaitingRoomProcedures.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/PagedWaitingRoomProcedures.java new file mode 100644 index 000000000..ea3d2156b --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/PagedWaitingRoomProcedures.java @@ -0,0 +1,17 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom; + +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomProcedureDto; +import java.util.List; +import java.util.stream.Stream; + +public record PagedWaitingRoomProcedures( + List<WaitingRoomProcedureDto> proceduresPage, long totalNumberOfProcedures) { + public Stream<WaitingRoomProcedureDto> stream() { + return proceduresPage.stream(); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomController.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomController.java new file mode 100644 index 000000000..a240e9a60 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomController.java @@ -0,0 +1,46 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom; + +import de.eshg.api.commons.InlineParameterObject; +import de.eshg.officialmedicalservice.waitingroom.api.GetWaitingRoomProceduresResponse; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomProcedurePaginationAndSortParameters; +import de.eshg.rest.service.security.config.BaseUrls.OfficialMedicalService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path = WaitingRoomController.BASE_URL, produces = MediaType.APPLICATION_JSON_VALUE) +@Tag(name = "WaitingRoom") +public class WaitingRoomController { + public static final String BASE_URL = OfficialMedicalService.EMPLOYEE_API; + public static final String WAITING_ROOM_URL = "/waiting-room"; + + private final WaitingRoomService waitingRoomService; + + public WaitingRoomController(WaitingRoomService waitingRoomService) { + this.waitingRoomService = waitingRoomService; + } + + @GetMapping(path = WAITING_ROOM_URL) + @Operation(summary = "Get all procedures in waiting room.") + public GetWaitingRoomProceduresResponse getWaitingRoomProcedures( + @InlineParameterObject @ParameterObject @Valid + WaitingRoomProcedurePaginationAndSortParameters paginationAndSortParameters) { + + PagedWaitingRoomProcedures pagedProcedures = + waitingRoomService.getWaitingRoomProcedures(paginationAndSortParameters); + + return new GetWaitingRoomProceduresResponse( + pagedProcedures.proceduresPage(), pagedProcedures.totalNumberOfProcedures()); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomMapper.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomMapper.java new file mode 100644 index 000000000..4d47c0dd2 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomMapper.java @@ -0,0 +1,59 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom; + +import de.eshg.base.SortDirection; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomDto; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingStatusDto; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingStatus; +import de.eshg.officialmedicalservice.waitingroom.util.WaitingRoomPageSpec; +import org.springframework.data.domain.Sort; + +public class WaitingRoomMapper { + + private WaitingRoomMapper() {} + + public static WaitingRoomDto mapToDto(WaitingRoom waitingRoom) { + if (waitingRoom == null) { + return null; + } + + return new WaitingRoomDto(waitingRoom.getInfo(), mapStatusToDto(waitingRoom.getStatus())); + } + + private static WaitingStatusDto mapStatusToDto(WaitingStatus waitingStatus) { + return switch (waitingStatus) { + case null -> null; + case WAITING_FOR_CONSULTATION -> WaitingStatusDto.WAITING_FOR_CONSULTATION; + case IN_CONSULTATION -> WaitingStatusDto.IN_CONSULTATION; + case DONE -> WaitingStatusDto.DONE; + }; + } + + public static WaitingStatus mapStatusFromDto(WaitingStatusDto waitingStatus) { + return switch (waitingStatus) { + case null -> null; + case WAITING_FOR_CONSULTATION -> WaitingStatus.WAITING_FOR_CONSULTATION; + case IN_CONSULTATION -> WaitingStatus.IN_CONSULTATION; + case DONE -> WaitingStatus.DONE; + }; + } + + public static WaitingRoomPageSpec mapToPageSpec( + int page, int pageSize, WaitingRoomSortKey sortField, SortDirection direction) { + return new WaitingRoomPageSpec(page, pageSize, sortField, mapDirection(direction)); + } + + public static Sort.Direction mapDirection(SortDirection sortDirection) { + return switch (sortDirection) { + case null -> Sort.Direction.ASC; + case ASC -> Sort.Direction.ASC; + case DESC -> Sort.Direction.DESC; + }; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomService.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomService.java new file mode 100644 index 000000000..2c7bca811 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomService.java @@ -0,0 +1,385 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom; + +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.DATE_OF_BIRTH; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.FACILITY; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.FIRSTNAME; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.LASTNAME; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.PHYSICIAN; +import static java.util.Comparator.comparing; +import static java.util.Comparator.naturalOrder; +import static java.util.Comparator.nullsLast; + +import de.eshg.base.SortDirection; +import de.eshg.base.centralfile.api.facility.GetFacilityFileStateResponse; +import de.eshg.base.centralfile.api.facility.GetFacilityFileStatesRequest; +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.GetPersonFileStatesSortParameters; +import de.eshg.base.centralfile.api.person.GetPersonsSortKey; +import de.eshg.lib.procedure.domain.model.FacilityType; +import de.eshg.lib.procedure.domain.model.PersonType; +import de.eshg.lib.procedure.domain.model.ProcedureStatus; +import de.eshg.lib.procedure.procedures.ProcedureQuery; +import de.eshg.officialmedicalservice.facility.FacilityClient; +import de.eshg.officialmedicalservice.facility.FacilityMapper; +import de.eshg.officialmedicalservice.person.PersonClient; +import de.eshg.officialmedicalservice.person.PersonMapper; +import de.eshg.officialmedicalservice.procedure.api.AffectedPersonDto; +import de.eshg.officialmedicalservice.procedure.api.FacilityDto; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedureRepository; +import de.eshg.officialmedicalservice.user.UserClient; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomDto; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomProcedureDto; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomProcedurePaginationAndSortParameters; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom; +import de.eshg.officialmedicalservice.waitingroom.util.WaitingRoomPageSpec; +import de.eshg.rest.service.error.BadRequestException; +import de.eshg.rest.service.error.NotFoundException; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Stream; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +@Service +public class WaitingRoomService { + private final OmsProcedureRepository procedureRepository; + private final PersonClient personClient; + private final FacilityClient facilityClient; + private final ProcedureQuery procedureQuery; + private final OmsProcedureRepository omsProcedureRepository; + private final UserClient userClient; + + public WaitingRoomService( + OmsProcedureRepository procedureRepository, + PersonClient personClient, + FacilityClient facilityClient, + ProcedureQuery procedureQuery, + OmsProcedureRepository omsProcedureRepository, + UserClient userClient) { + this.procedureRepository = procedureRepository; + this.personClient = personClient; + this.facilityClient = facilityClient; + this.procedureQuery = procedureQuery; + this.omsProcedureRepository = omsProcedureRepository; + this.userClient = userClient; + } + + @Transactional(readOnly = true) + public PagedWaitingRoomProcedures getWaitingRoomProcedures( + WaitingRoomProcedurePaginationAndSortParameters paginationAndSortParameters) { + + WaitingRoomPageSpec pageSpec = + WaitingRoomMapper.mapToPageSpec( + paginationAndSortParameters.pageNumberOrFallback(0), + paginationAndSortParameters.pageSizeOrFallback(25), + paginationAndSortParameters.sortKeyOrFallback( + de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.ID), + paginationAndSortParameters.sortDirectionOrFallback(SortDirection.DESC)); + + WaitingRoomSpecification waitingRoomSpecification = + new WaitingRoomSpecification(pageSpec.sortKey(), pageSpec.direction()); + + if (List.of(FIRSTNAME, LASTNAME, DATE_OF_BIRTH).contains(pageSpec.sortKey())) { + return getSortingForPersonAttributes(pageSpec, waitingRoomSpecification); + } + + if (Objects.equals(PHYSICIAN, pageSpec.sortKey())) { + return getSortingForPhysicianAttribute(pageSpec); + } + + if (Objects.equals(FACILITY, pageSpec.sortKey())) { + return getSortingForFacilityAttribute(pageSpec, waitingRoomSpecification); + } + + Page<OmsProcedure> omsProcedures = + procedureRepository.findAll( + waitingRoomSpecification, PageRequest.of(pageSpec.pageNumber(), pageSpec.pageSize())); + List<WaitingRoomProcedureDto> procedureData = + augmentWithWaitingRoomData(omsProcedures.getContent()).toList(); + + return new PagedWaitingRoomProcedures(procedureData, omsProcedures.getTotalElements()); + } + + @Transactional + public void updateWaitingRoom(UUID externalId, WaitingRoomDto request) { + OmsProcedure omsProcedure = loadOmsProcedure(externalId); + + if (Objects.equals(ProcedureStatus.DRAFT, omsProcedure.getProcedureStatus())) { + throw new BadRequestException( + "Waiting room cannot be updated when the procedure is in DRAFT status."); + } + + if (omsProcedure.isFinalized()) { + throw new BadRequestException( + "Waiting room cannot be updated when the procedure is finalized."); + } + + WaitingRoom waitingRoom = omsProcedure.getWaitingRoom(); + waitingRoom.setInfo(request.info()); + waitingRoom.setStatus(WaitingRoomMapper.mapStatusFromDto(request.status())); + } + + private PagedWaitingRoomProcedures getSortingForPersonAttributes( + WaitingRoomPageSpec pageSpec, WaitingRoomSpecification waitingRoomSpecification) { + List<UUID> personIds = + procedureQuery.findAllRelatedPersonFileStateIds( + waitingRoomSpecification, OmsProcedure.class, PersonType.PATIENT); + + List<UUID> pagedAndSortedPersonIds = + fetchPersonsBulk( + personIds, + mapToGetPersonsSortKey(pageSpec.sortKey()), + pageSpec.direction(), + pageSpec.pageNumber(), + pageSpec.pageSize()) + .stream() + .map(GetPersonFileStateResponse::id) + .toList(); + + List<OmsProcedure> result = + omsProcedureRepository + .findByRelatedPersons(pagedAndSortedPersonIds) + .sorted( + Comparator.comparingInt( + procedure -> { + int index = + pagedAndSortedPersonIds.indexOf( + procedure.findAffectedPerson().getCentralFileStateId()); + Assert.isTrue(index >= 0, "Unexpected index: " + index); + return index; + })) + .toList(); + + List<WaitingRoomProcedureDto> procedureData = augmentWithWaitingRoomData(result).toList(); + return new PagedWaitingRoomProcedures(procedureData, personIds.size()); + } + + private PagedWaitingRoomProcedures getSortingForPhysicianAttribute(WaitingRoomPageSpec pageSpec) { + List<UUID> physicianIds = omsProcedureRepository.findDistinctPhysicianIds(); + + List<UUID> pagedAndSortedPhysicianIds = + fetchPhysiciansBulk( + physicianIds, pageSpec.direction(), pageSpec.pageNumber(), pageSpec.pageSize()); + + List<OmsProcedure> result = + omsProcedureRepository + .findAllByWaitingRoomStatusInWaitingOrInConsultation() + .sorted( + Comparator.comparing( + procedure -> { + if (procedure.getPhysicianId() == null) { + return null; + } + int index = pagedAndSortedPhysicianIds.indexOf(procedure.getPhysicianId()); + Assert.isTrue(index >= 0, "Unexpected index: " + index); + return index; + }, + nullsLast(naturalOrder()))) + .toList(); + + List<WaitingRoomProcedureDto> procedureData = augmentWithWaitingRoomData(result).toList(); + return new PagedWaitingRoomProcedures(procedureData, result.size()); + } + + private PagedWaitingRoomProcedures getSortingForFacilityAttribute( + WaitingRoomPageSpec pageSpec, WaitingRoomSpecification waitingRoomSpecification) { + List<UUID> facilityIds = + procedureQuery.findAllRelatedFacilityFileStateIds( + waitingRoomSpecification, OmsProcedure.class, FacilityType.OTHER); + + List<UUID> pagedAndSortedFacilityIds = + fetchFacilitiesBulk( + facilityIds, pageSpec.direction(), pageSpec.pageNumber(), pageSpec.pageSize()) + .stream() + .map(GetFacilityFileStateResponse::id) + .toList(); + + List<OmsProcedure> result = + omsProcedureRepository + .findByRelatedFacility(pagedAndSortedFacilityIds) + .sorted( + Comparator.comparingInt( + procedure -> { + int index = + pagedAndSortedFacilityIds.indexOf( + procedure.getFacility().orElseThrow().getCentralFileStateId()); + Assert.isTrue(index >= 0, "Unexpected index: " + index); + return index; + })) + .toList(); + + List<WaitingRoomProcedureDto> procedureData = augmentWithWaitingRoomData(result).toList(); + return new PagedWaitingRoomProcedures(procedureData, facilityIds.size()); + } + + private Stream<WaitingRoomProcedureDto> augmentWithWaitingRoomData( + List<OmsProcedure> procedures) { + Map<UUID, String> physicianNamesMap = userClient.getPhysicianNamesMap(); + return procedures.stream() + .map( + data -> { + AffectedPersonDto affectedPerson = + PersonMapper.mapToAffectedPersonDto( + personClient.getPersonFileState( + data.findAffectedPerson().getCentralFileStateId()), + data.findAffectedPerson().getVersion()); + FacilityDto facility = + FacilityMapper.mapToFacilityDto( + facilityClient.getFacilityFileState( + data.getFacility().orElseThrow().getCentralFileStateId()), + data.findAffectedPerson().getVersion()); + + return new WaitingRoomProcedureDto( + data.getExternalId(), + affectedPerson.firstName(), + affectedPerson.lastName(), + affectedPerson.dateOfBirth(), + facility.name(), + physicianNamesMap.get(data.getPhysicianId()), + WaitingRoomMapper.mapToDto(data.getWaitingRoom()), + data.getWaitingRoom().getModifiedAt()); + }); + } + + private OmsProcedure loadOmsProcedure(UUID externalId) { + return procedureRepository + .findByExternalId(externalId) + .orElseThrow(() -> new NotFoundException("Procedure not found")); + } + + private List<GetPersonFileStateResponse> fetchPersonsBulk( + List<UUID> personIdsToFetch, + GetPersonsSortKey sortKey, + Sort.Direction direction, + Integer pageNumber, + Integer pageSize) { + if (personIdsToFetch.isEmpty()) { + return List.of(); + } + GetPersonFileStatesSortParameters sortParameters = + mapToSortParameters(sortKey, direction, pageNumber, pageSize); + + GetPersonFileStatesResponse response = + personClient.getPersonFileStates( + new GetPersonFileStatesRequest(personIdsToFetch, sortParameters)); + + int expectedResponseSize = + sortParameters == null + ? personIdsToFetch.size() + : Math.min(pageSize, personIdsToFetch.size() - (pageNumber * pageSize)); + if (response.personFileStates().size() < expectedResponseSize) { + throw new IllegalStateException("Some persons were not found in the central file."); + } + + return response.personFileStates(); + } + + private List<GetFacilityFileStateResponse> fetchFacilitiesBulk( + List<UUID> facilityIdsToFetch, + Sort.Direction direction, + Integer pageNumber, + Integer pageSize) { + if (facilityIdsToFetch.isEmpty()) { + return List.of(); + } + + List<GetFacilityFileStateResponse> facilities = + facilityClient + .getFacilityFileStates(new GetFacilityFileStatesRequest(facilityIdsToFetch)) + .facilityFileStates(); + + Comparator<GetFacilityFileStateResponse> comparator = + comparing(GetFacilityFileStateResponse::name); + + if (direction == Sort.Direction.DESC) { + comparator = comparator.reversed(); + } + + facilities = facilities.stream().sorted(comparator).toList(); + + int start = pageNumber * pageSize; + int end = Math.min(start + pageSize, facilities.size()); + + if (start > facilities.size()) { + return List.of(); + } + + return facilities.subList(start, end); + } + + private List<UUID> fetchPhysiciansBulk( + List<UUID> physicianIdsToFetch, + Sort.Direction direction, + Integer pageNumber, + Integer pageSize) { + + if (physicianIdsToFetch.isEmpty()) { + return List.of(); + } + + Map<UUID, String> physicianNamesMap = userClient.getPhysicianNamesMap(); + + List<Map.Entry<UUID, String>> physicians = + physicianNamesMap.entrySet().stream() + .filter(entry -> physicianIdsToFetch.contains(entry.getKey())) + .toList(); + + Comparator<Map.Entry<UUID, String>> comparator = comparing(Map.Entry::getValue); + + if (direction == Sort.Direction.DESC) { + comparator = comparator.reversed(); + } + + physicians = physicians.stream().sorted(comparator).toList(); + + int start = pageNumber * pageSize; + int end = Math.min(start + pageSize, physicians.size()); + + if (start > physicians.size()) { + return List.of(); + } + + return physicians.subList(start, end).stream().map(Map.Entry::getKey).toList(); + } + + private GetPersonFileStatesSortParameters mapToSortParameters( + GetPersonsSortKey sortKey, Sort.Direction direction, Integer pageNumber, Integer pageSize) { + if (sortKey == null) { + return null; + } + return new GetPersonFileStatesSortParameters( + sortKey, + switch (direction) { + case ASC -> SortDirection.ASC; + case DESC -> SortDirection.DESC; + }, + pageNumber, + pageSize); + } + + private static GetPersonsSortKey mapToGetPersonsSortKey(WaitingRoomSortKey sortKey) { + return switch (sortKey) { + case DATE_OF_BIRTH -> GetPersonsSortKey.DATE_OF_BIRTH; + case FIRSTNAME -> GetPersonsSortKey.FIRST_NAME; + case LASTNAME -> GetPersonsSortKey.LAST_NAME; + case ID, INFO, STATUS, MODIFIED_AT, PHYSICIAN, FACILITY -> + throw new IllegalArgumentException("Unexpected sort key: " + sortKey); + }; + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomSpecification.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomSpecification.java new file mode 100644 index 000000000..ef25585a3 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/WaitingRoomSpecification.java @@ -0,0 +1,110 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom; + +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.DATE_OF_BIRTH; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.FACILITY; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.FIRSTNAME; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.LASTNAME; +import static de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey.PHYSICIAN; + +import de.eshg.lib.procedure.domain.model.ProcedureStatus; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure_; +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingRoom_; +import de.eshg.officialmedicalservice.waitingroom.persistence.entity.WaitingStatus; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Order; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import java.io.Serial; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; + +public class WaitingRoomSpecification implements Specification<OmsProcedure> { + @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<OmsProcedure> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { + List<Predicate> conjunctions = new ArrayList<>(); + conjunctions.add( + criteriaBuilder.equal(root.get(OmsProcedure_.procedureStatus), ProcedureStatus.OPEN)); + conjunctions.add( + criteriaBuilder.isNotNull(root.get(OmsProcedure_.waitingRoom).get(WaitingRoom_.status))); + conjunctions.add( + criteriaBuilder.not( + root.get(OmsProcedure_.waitingRoom).get(WaitingRoom_.status).in(WaitingStatus.DONE))); + + Set<Order> orders = new LinkedHashSet<>(); + if (!List.of(FIRSTNAME, LASTNAME, DATE_OF_BIRTH, FACILITY, PHYSICIAN).contains(sortKey)) { + orders.add(getOrder(root, criteriaBuilder)); + } + orders.add(getFallbackOrder(root, criteriaBuilder)); + + query.orderBy(orders.stream().toList()); + return criteriaBuilder.and(conjunctions.toArray(Predicate[]::new)); + } + + private Order getFallbackOrder(Root<OmsProcedure> root, CriteriaBuilder criteriaBuilder) { + Expression<?> fallbackOrderExpression = root.get(OmsProcedure_.id); + return switch (sortDirection) { + case ASC -> criteriaBuilder.asc(fallbackOrderExpression); + case DESC -> criteriaBuilder.desc(fallbackOrderExpression); + }; + } + + private Order getOrder(Root<OmsProcedure> root, CriteriaBuilder criteriaBuilder) { + Expression<?> sortOrder = + switch (sortKey) { + case ID -> root.get(OmsProcedure_.id); + case FIRSTNAME, LASTNAME, DATE_OF_BIRTH, FACILITY, PHYSICIAN -> + throw new IllegalArgumentException("Unexpected sort key: " + sortKey); + case STATUS -> root.get(OmsProcedure_.waitingRoom).get(WaitingRoom_.status); + case INFO -> + nullsLastString( + root.get(OmsProcedure_.waitingRoom).get(WaitingRoom_.info), criteriaBuilder); + case MODIFIED_AT -> root.get(OmsProcedure_.waitingRoom).get(WaitingRoom_.modifiedAt); + }; + return switch (sortDirection) { + case ASC -> criteriaBuilder.asc(sortOrder); + case DESC -> criteriaBuilder.desc(sortOrder); + }; + } + + private Expression<String> nullsLastString(Path<String> instantPath, CriteriaBuilder cb) { + String valueWhenNull = + switch (sortDirection) { + case ASC -> null; + case DESC -> ""; + }; + return nullsLast(instantPath, cb, valueWhenNull); + } + + // This is a workaround because the CriteriaBuilder currently does not support + // generating SQL’s "NULLS LAST" + // It’s supposed to be added in Java Persistence 3.2 / Hibernate 7.0 + private static <T> Expression<T> nullsLast( + Path<T> instantPath, CriteriaBuilder cb, T valueWhenNull) { + return cb.coalesce(instantPath, cb.literal(valueWhenNull)); + } +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/GetWaitingRoomProceduresResponse.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/GetWaitingRoomProceduresResponse.java new file mode 100644 index 000000000..85f015e58 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/GetWaitingRoomProceduresResponse.java @@ -0,0 +1,17 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +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/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomDto.java new file mode 100644 index 000000000..16727f7bc --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomDto.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "WaitingRoom") +public record WaitingRoomDto(String info, WaitingStatusDto status) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedureDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedureDto.java new file mode 100644 index 000000000..8f0084fb9 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedureDto.java @@ -0,0 +1,24 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import java.time.LocalDate; +import java.util.UUID; + +@Schema(name = "WaitingRoomProcedure") +public record WaitingRoomProcedureDto( + @NotNull UUID id, + @NotNull String firstName, + @NotNull String lastName, + @NotNull LocalDate dateOfBirth, + @NotNull String facilityName, + String physicianName, + @NotNull @Valid WaitingRoomDto waitingRoom, + @NotNull Instant modifiedAt) {} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedurePaginationAndSortParameters.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedurePaginationAndSortParameters.java new file mode 100644 index 000000000..cc7608b5c --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomProcedurePaginationAndSortParameters.java @@ -0,0 +1,18 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +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/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomSortKey.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomSortKey.java new file mode 100644 index 000000000..915154244 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingRoomSortKey.java @@ -0,0 +1,18 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +public enum WaitingRoomSortKey { + ID, + FIRSTNAME, + LASTNAME, + DATE_OF_BIRTH, + FACILITY, + PHYSICIAN, + STATUS, + INFO, + MODIFIED_AT, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingStatusDto.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingStatusDto.java new file mode 100644 index 000000000..df5090b63 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/api/WaitingStatusDto.java @@ -0,0 +1,15 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "WaitingStatus") +public enum WaitingStatusDto { + WAITING_FOR_CONSULTATION, + IN_CONSULTATION, + DONE, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingRoom.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingRoom.java new file mode 100644 index 000000000..b17247b97 --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingRoom.java @@ -0,0 +1,66 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.persistence.entity; + +import de.eshg.domain.model.BaseEntity; +import de.eshg.lib.common.DataSensitivity; +import de.eshg.lib.common.SensitivityLevel; +import de.eshg.officialmedicalservice.procedure.persistence.entity.OmsProcedure; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +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.PSEUDONYMIZED) +public class WaitingRoom extends BaseEntity { + + @MapsId + @OneToOne(optional = false) + private OmsProcedure procedure; + + private String info; + + @JdbcType(PostgreSQLEnumJdbcType.class) + private WaitingStatus status; + + @NotNull @LastModifiedDate private Instant modifiedAt; + + public OmsProcedure getProcedure() { + return procedure; + } + + public void setProcedure(OmsProcedure 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/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingStatus.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingStatus.java new file mode 100644 index 000000000..3ecbe3d2e --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/persistence/entity/WaitingStatus.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.persistence.entity; + +public enum WaitingStatus { + WAITING_FOR_CONSULTATION, + IN_CONSULTATION, + DONE, +} diff --git a/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/util/WaitingRoomPageSpec.java b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/util/WaitingRoomPageSpec.java new file mode 100644 index 000000000..1d92253fa --- /dev/null +++ b/backend/official-medical-service/src/main/java/de/eshg/officialmedicalservice/waitingroom/util/WaitingRoomPageSpec.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +package de.eshg.officialmedicalservice.waitingroom.util; + +import de.eshg.officialmedicalservice.waitingroom.api.WaitingRoomSortKey; +import org.springframework.data.domain.Sort; + +public record WaitingRoomPageSpec( + int pageNumber, int pageSize, WaitingRoomSortKey sortKey, Sort.Direction direction) {} diff --git a/backend/official-medical-service/src/main/resources/application-health-department-frankfurt.properties b/backend/official-medical-service/src/main/resources/application-health-department-frankfurt.properties index b36ec31b1..9243a0c6b 100644 --- a/backend/official-medical-service/src/main/resources/application-health-department-frankfurt.properties +++ b/backend/official-medical-service/src/main/resources/application-health-department-frankfurt.properties @@ -25,3 +25,8 @@ de.eshg.official-medical-service.department-info.phoneNumber=069 212 33622 de.eshg.official-medical-service.department-info.email=info.amtsaerztlicherdienst@stadt-frankfurt.de # de.eshg.official-medical-service.department-info.latitude= # de.eshg.official-medical-service.department-info.longitude= + +# notifications (by mail) +de.eshg.official-medical-service.notification.fromAddress=tba@stadt-frankfurt.de +de.eshg.official-medical-service.notification.greeting=Ihr TBA-Team der Stadt Frankfurt +de.eshg.official-medical-service.notification.templates.path=notifications/ga_frankfurt/de diff --git a/backend/official-medical-service/src/main/resources/application.properties b/backend/official-medical-service/src/main/resources/application.properties index f4aa01a17..10340045f 100644 --- a/backend/official-medical-service/src/main/resources/application.properties +++ b/backend/official-medical-service/src/main/resources/application.properties @@ -14,7 +14,7 @@ spring.liquibase.enabled=false logging.level.org.zalando.logbook=TRACE -spring.security.oauth2.client.registration.module-client.client-id=system-official-medicine-service +spring.security.oauth2.client.registration.module-client.client-id=system-official-medical-service 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 @@ -37,6 +37,16 @@ de.eshg.lib.appointmentblock.createAppointmentBlockForCurrentUser=false eshg.population.default-number-of-entities-to-populate.appointment-block-group=0 +eshg.citizen-portal.reverse-proxy.url=http://localhost:4001 + de.eshg.official-medical-service.concerns.templates.path=concerns de.eshg.official-medical-service.concerns.config=classpath:${de.eshg.official-medical-service.concerns.templates.path}/concerns.yaml + +# standard notifications (by mail) +de.eshg.official-medical-service.notification.fromAddress=tba@stadt-frankfurt.de +de.eshg.official-medical-service.notification.greeting=Ihr TBA-Team der Stadt Frankfurt + +de.eshg.official-medical-service.notification.templates.path=notifications/default/de +de.eshg.official-medical-service.notification.template.new_citizen_user.subject=Bestätigung +de.eshg.official-medical-service.notification.template.new_citizen_user.body=classpath:${de.eshg.official-medical-service.notification.templates.path}/new_citizen_user.txt diff --git a/backend/official-medical-service/src/main/resources/documents/testHelperSampleDocument.pdf b/backend/official-medical-service/src/main/resources/documents/testHelperSampleDocument.pdf new file mode 100644 index 0000000000000000000000000000000000000000..89551d242906e200b6dc2212962337c2bded29fb GIT binary patch literal 21731 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 B53v9M literal 0 HcmV?d00001 diff --git a/backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.jpg b/backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..83f9478af1700702804d85cfac05d33cd9b9e0f4 GIT binary patch literal 730 zcmex=<NpH&0WUXCHwH#VMur3+WcdG(A;q;KGmU}4*Ox(yfq{X8frU|uffdMN1Y$cz zX*k=BQG<aQEY1X!G-Lph417TB2gD$d0K_0YU^*Z%Gp|I?&oxBBRL_v%|7`|mhX0fT zpdpNun(_YtgCGacnT*Vgf(%T8jLd?J|Bo=p16|Gv3=>eWKmj8YGYcylI|nBhH&DS= z0R|>UW@aW9W>!`f7NF`{pgaSMAghp~p(C4cU?RIxp@>oA#DyHnP8$!323`E1Vw_ae z#K|QlE+HwUs-~`?sbyknW^Q3=<?Q0>=I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA z)!fqB*51+CHEHscsne#<m^o|F;w4L$Enl&6)uzo`wr<<LW9P0zhmRaRcKpQ2Q<pAZ zxq9vTjhnX~K6?D*>9glAUcUPH>GPMb-@gC&`3vMPMh0exx8OcQ^A|7>7@1gDm|56C z{$gY*2V!PH7FI<=HX+AA_QXPAC8I_T5vPd@Hy-3vHV*nAnpAX=OH9S&q3TDF*T6m_ g&SOnv`3&wcguiYv@Gvt1Bac~-!Jc7KgZ=-T0E7rdCIA2c literal 0 HcmV?d00001 diff --git a/backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.png b/backend/official-medical-service/src/main/resources/documents/testHelperSampleImage.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2f9e4521862951c878578010b0e0fcb4f4303b GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2s6ii6yp7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctjR6Fz_7)VaDV6D^h@hVkNE-CBgY=CFO}lsSE*$nRz98ey$-3rh10o zo4-#6s@Rem;hE;?sl~tn<ghYGF|vZ~^#bywp=^*_G#Ht|;vm-;GBPpn0qH0p&TMA^ zi)R4YAdmpWAa}!P6e}5+fnH%}U;)Y-7#SNdE`XQ`QqQ^oV$LKW8w8kurZ9n323cAF zSx{Yu1_mJ6<RxlHg8crT0Wx_!T^vIs!jpge|8LL0`j6?~a?{2ZkQz@{KbLh*2~7Y< Cvpux{ literal 0 HcmV?d00001 diff --git a/backend/official-medical-service/src/main/resources/notifications/default/de/new_citizen_user.txt b/backend/official-medical-service/src/main/resources/notifications/default/de/new_citizen_user.txt new file mode 100644 index 000000000..22e24f6a6 --- /dev/null +++ b/backend/official-medical-service/src/main/resources/notifications/default/de/new_citizen_user.txt @@ -0,0 +1,17 @@ +Sehr geehrte(r) %s %s, + +wir möchten Ihnen mitteilen, dass ... + ... bitten wir Sie, dies über unseren Online-Service vorzunehmen. Nutzen Sie hierfür bitte den folgenden Link: +%s + +Ihr 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/official-medical-service/src/main/resources/notifications/ga_frankfurt/de/new_citizen_user.txt b/backend/official-medical-service/src/main/resources/notifications/ga_frankfurt/de/new_citizen_user.txt new file mode 100644 index 000000000..4718bbf4d --- /dev/null +++ b/backend/official-medical-service/src/main/resources/notifications/ga_frankfurt/de/new_citizen_user.txt @@ -0,0 +1,16 @@ +Sehr geehrte(r) %s %s, + +wir möchten Ihnen mitteilen, dass ... + ... 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/opendata/gradle.lockfile b/backend/opendata/gradle.lockfile index bd266a768..b37e5539a 100644 --- a/backend/opendata/gradle.lockfile +++ b/backend/opendata/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -86,7 +86,7 @@ org.antlr:antlr4-runtime:4.13.0=annotationProcessor,compileClasspath,productionR org.apache.commons:commons-collections4:4.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-compress:1.24.0=testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.17.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-text:1.12.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.commons:commons-text:1.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.httpcomponents.client5:httpclient5:5.4.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5-h2:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -117,10 +117,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -139,16 +139,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/relay-server/gradle.lockfile b/backend/relay-server/gradle.lockfile index e1315dd52..7b4890c1b 100644 --- a/backend/relay-server/gradle.lockfile +++ b/backend/relay-server/gradle.lockfile @@ -58,8 +58,8 @@ io.netty:netty-transport:4.1.116.Final=testCompileClasspath,testRuntimeClasspath io.projectreactor.netty:reactor-netty-core:1.2.1=testCompileClasspath,testRuntimeClasspath io.projectreactor.netty:reactor-netty-http:1.2.1=testCompileClasspath,testRuntimeClasspath io.projectreactor:reactor-core:3.7.1=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -90,10 +90,10 @@ org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClass org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath @@ -108,9 +108,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=testCompileClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath diff --git a/backend/rest-client-commons/gradle.lockfile b/backend/rest-client-commons/gradle.lockfile index 75c1e6344..520cb944a 100644 --- a/backend/rest-client-commons/gradle.lockfile +++ b/backend/rest-client-commons/gradle.lockfile @@ -21,8 +21,8 @@ com.squareup.okio:okio:3.6.0=testCompileClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.quarkus:quarkus-junit4-mock:3.17.7=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.quarkus:quarkus-junit4-mock:3.18.0=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -43,10 +43,10 @@ org.apiguardian:apiguardian-api:1.1.2=compileClasspath,productionRuntimeClasspat org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt 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 @@ -64,9 +64,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/rest-oauth-client-commons/gradle.lockfile b/backend/rest-oauth-client-commons/gradle.lockfile index a67c67be8..a77310932 100644 --- a/backend/rest-oauth-client-commons/gradle.lockfile +++ b/backend/rest-oauth-client-commons/gradle.lockfile @@ -79,7 +79,7 @@ io.netty:netty-transport-rxtx:4.1.116.Final=testRuntimeClasspath io.netty:netty-transport-sctp:4.1.116.Final=testRuntimeClasspath io.netty:netty-transport-udt:4.1.116.Final=testRuntimeClasspath io.netty:netty-transport:4.1.116.Final=testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -115,10 +115,10 @@ org.checkerframework:checker-qual:3.43.0=testRuntimeClasspath org.freemarker:freemarker:2.3.33=testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains.kotlin:kotlin-reflect:1.9.25=testRuntimeClasspath org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=testCompileClasspath,testRuntimeClasspath org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=testCompileClasspath,testRuntimeClasspath @@ -137,9 +137,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=compileClasspath,jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/rest-service-commons/gradle.lockfile b/backend/rest-service-commons/gradle.lockfile index daefac1db..022e137af 100644 --- a/backend/rest-service-commons/gradle.lockfile +++ b/backend/rest-service-commons/gradle.lockfile @@ -28,11 +28,11 @@ org.apiguardian:apiguardian-api:1.1.2=productionRuntimeClasspath,runtimeClasspat org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt -org.jetbrains:annotations:26.0.1=compileClasspath +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt +org.jetbrains:annotations:26.0.2=compileClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -45,9 +45,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/rest-service-errors/gradle.lockfile b/backend/rest-service-errors/gradle.lockfile index b7c338140..d331f44c0 100644 --- a/backend/rest-service-errors/gradle.lockfile +++ b/backend/rest-service-errors/gradle.lockfile @@ -8,7 +8,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspat de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -23,10 +23,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -39,9 +39,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/school-entry/build.gradle b/backend/school-entry/build.gradle index eae704f2e..36d42bbae 100644 --- a/backend/school-entry/build.gradle +++ b/backend/school-entry/build.gradle @@ -36,6 +36,7 @@ dependencies { testImplementation testFixtures(project(':lib-document-generator')) testImplementation testFixtures(project(':lib-xlsx-import')) testImplementation testFixtures(project(':lib-statistics')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/school-entry/gradle.lockfile b/backend/school-entry/gradle.lockfile index 5f8d79fdd..8a0a7f775 100644 --- a/backend/school-entry/gradle.lockfile +++ b/backend/school-entry/gradle.lockfile @@ -79,10 +79,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -177,13 +177,13 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath -org.jetbrains:annotations:26.0.1=compileClasspath +org.jetbrains:annotations:26.0.2=compileClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -200,16 +200,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/school-entry/openApi.json b/backend/school-entry/openApi.json index cb19d208d..73a5ea074 100644 --- a/backend/school-entry/openApi.json +++ b/backend/school-entry/openApi.json @@ -4056,18 +4056,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -4757,9 +4746,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -9160,9 +9149,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, 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 4b16ef795..cbb8e4fec 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 @@ -143,7 +143,7 @@ public class SchoolEntryController { } @GetMapping("/{procedureId}") - @Transactional(readOnly = true) + @Transactional @Operation(summary = "Get school entry procedure by id.") public ProcedureDetailsDto getProcedure(@PathVariable("procedureId") UUID procedureId) { ProcedureDetailsData procedureDetailsData = diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java index a22a0b86e..7f2106ea2 100644 --- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java +++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/CreatePersonDto.java @@ -9,6 +9,7 @@ import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -25,7 +26,7 @@ public record CreatePersonDto( GenderDto gender, @NotNull @Size(min = 1, max = 80) String firstName, @NotNull @Size(min = 1, max = 120) String lastName, - @NotNull LocalDate dateOfBirth, + @NotNull @DateOfBirth LocalDate dateOfBirth, @Size(min = 1, max = 40) String nameAtBirth, @Size(min = 1, max = 50) String placeOfBirth, CountryCode countryOfBirth, diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java index 119891b97..c6e522c0d 100644 --- a/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java +++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/api/UpdatePersonRequest.java @@ -9,6 +9,7 @@ import de.eshg.base.GenderDto; import de.eshg.base.SalutationDto; import de.eshg.base.address.AddressDto; import de.eshg.lib.common.CountryCode; +import de.eshg.validation.constraints.DateOfBirth; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -22,7 +23,7 @@ public record UpdatePersonRequest( GenderDto gender, @NotNull @Size(min = 1, max = 80) String firstName, @NotNull @Size(min = 1, max = 120) String lastName, - @NotNull LocalDate dateOfBirth, + @NotNull @DateOfBirth LocalDate dateOfBirth, @Size(min = 1, max = 40) String nameAtBirth, @Size(min = 1, max = 50) String placeOfBirth, CountryCode countryOfBirth, 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 a4a7cab28..ed40755a0 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 @@ -16,6 +16,7 @@ import de.eshg.lib.xlsximport.RowReader; import de.eshg.lib.xlsximport.model.AddressData; import de.eshg.schoolentry.business.model.ImportChildData; import de.eshg.schoolentry.business.model.ImportCustodianData; +import java.time.Clock; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @@ -55,8 +56,8 @@ class CitizenListRowReader extends RowReader<CitizenListRow, CitizenListColumn> SALUTATION_CUSTODIAN_2, GENDER_CUSTODIAN_2)); - CitizenListRowReader(Sheet sheet, List<CitizenListColumn> actualColumns) { - super(sheet, actualColumns, CitizenListRow::new); + CitizenListRowReader(Sheet sheet, Clock clock, List<CitizenListColumn> actualColumns) { + super(sheet, actualColumns, CitizenListRow::new, clock); } @Override @@ -76,7 +77,7 @@ class CitizenListRowReader extends RowReader<CitizenListRow, CitizenListColumn> String lastName = cellAsString(col, LAST_NAME, errorHandler); String firstName = cellAsString(col, FIRST_NAME, errorHandler); AddressData addressData = readAddressData(col, CHILD_ADDRESS_COLUMNS, errorHandler, true); - LocalDate birthDate = cellAsDate(col, DATE_OF_BIRTH, errorHandler); + LocalDate birthDate = cellAsDateOfBirth(col, DATE_OF_BIRTH, errorHandler); String placeOfBirth = cellAsString(col, PLACE_OF_BIRTH, true, false, errorHandler); CountryCode countryCode = cellAsCountryCode(col, COUNTRY_OF_BIRTH, errorHandler); GenderDto genderDto = cellAsGender(col, GENDER, errorHandler); @@ -93,7 +94,7 @@ class CitizenListRowReader extends RowReader<CitizenListRow, CitizenListColumn> String firstName = cellAsString(col, custodian.firstName(), errorHandler); String lastName = cellAsString(col, custodian.lastName(), errorHandler); AddressData address = readAddressData(col, custodian.address(), errorHandler, false); - LocalDate dateOfBirth = cellAsDate(col, custodian.dateOfBirth(), errorHandler); + LocalDate dateOfBirth = cellAsDateOfBirth(col, custodian.dateOfBirth(), errorHandler); String title = cellAsString(col, custodian.title(), true, false, errorHandler); SalutationDto salutation = cellAsSalutation(col, custodian.salutation(), errorHandler); GenderDto gender = cellAsGender(col, custodian.gender(), errorHandler); 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 fbf504551..2846e0fb0 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 @@ -40,6 +40,7 @@ import de.eshg.schoolentry.util.ProgressEntryUtil; import de.eshg.schoolentry.util.SchoolEntrySystemProgressEntryType; import jakarta.persistence.criteria.Path; import java.io.IOException; +import java.time.Clock; import java.time.Year; import java.util.*; import org.apache.poi.xssf.usermodel.XSSFSheet; @@ -55,6 +56,7 @@ public class ImportService { private static final Logger log = LoggerFactory.getLogger(ImportService.class); + private final Clock clock; private final Validator validator; private final SchoolEntryService schoolEntryService; private final LabelService labelService; @@ -67,6 +69,7 @@ public class ImportService { private final SchoolEntryProcedureRepository schoolEntryProcedureRepository; public ImportService( + Clock clock, Validator validator, SchoolEntryService schoolEntryService, LabelService labelService, @@ -77,6 +80,7 @@ public class ImportService { SchoolEntryProperties schoolEntryProperties, ProcedureTypeAssignmentHelper procedureTypeAssignmentHelper, SchoolEntryProcedureRepository schoolEntryProcedureRepository) { + this.clock = clock; this.validator = validator; this.schoolEntryService = schoolEntryService; this.labelService = labelService; @@ -148,7 +152,7 @@ public class ImportService { List<CitizenListColumn> actualColumns) { return new CitizenOrSchoolListImporter<>( sheet, - new CitizenListRowReader(sheet, actualColumns), + new CitizenListRowReader(sheet, clock, actualColumns), new FeedbackColumnAccessor(actualColumns), importType, new CitizenListRowValueMapper(), @@ -168,7 +172,7 @@ public class ImportService { List<SchoolListColumn> actualColumns) { return new CitizenOrSchoolListImporter<>( sheet, - new SchoolListRowReader(sheet, actualColumns), + new SchoolListRowReader(sheet, clock, actualColumns), new FeedbackColumnAccessor(actualColumns), importType, new SchoolListRowValueMapper(schoolYear, procedureTypeAssignmentHelper), @@ -186,7 +190,7 @@ public class ImportService { List<PastProcedureListColumn> actualColumns) { return new PastProcedureListImporter( sheet, - new PastProcedureListRowReader(sheet, actualColumns), + new PastProcedureListRowReader(sheet, clock, actualColumns), new FeedbackColumnAccessor(actualColumns), schoolId, 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 35cfe705a..dcae94634 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 @@ -15,6 +15,7 @@ import de.eshg.schoolentry.api.CountryCodeDto; import de.eshg.schoolentry.business.model.*; import de.eshg.schoolentry.domain.model.*; import de.eshg.schoolentry.mapper.AnamnesisMapper; +import java.time.Clock; import java.time.LocalDate; import java.time.Period; import java.time.YearMonth; @@ -35,8 +36,9 @@ class PastProcedureListRowReader extends RowReader<PastProcedureListRow, PastPro static final List<PastProcedureListColumn> CHRONIC_DISEASE_ICD10_COLUMNS = List.of(CHRONIC_DISEASE_ICD10_1, CHRONIC_DISEASE_ICD10_2, CHRONIC_DISEASE_ICD10_3); - PastProcedureListRowReader(Sheet sheet, List<PastProcedureListColumn> actualColumns) { - super(sheet, actualColumns, PastProcedureListRow::new); + PastProcedureListRowReader( + Sheet sheet, Clock clock, List<PastProcedureListColumn> actualColumns) { + super(sheet, actualColumns, PastProcedureListRow::new, clock); } @Override @@ -356,7 +358,7 @@ class PastProcedureListRowReader extends RowReader<PastProcedureListRow, PastPro return new ImportChildData( cellAsString(col, FIRST_NAME, errorHandler), cellAsString(col, LAST_NAME, errorHandler), - cellAsDate(col, DATE_OF_BIRTH, errorHandler), + cellAsDateOfBirth(col, DATE_OF_BIRTH, errorHandler), cellAsGender(col, GENDER, errorHandler), readAddressData( col, 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 38ba1d533..a68784b90 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 @@ -11,13 +11,14 @@ 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.time.Clock; import java.util.List; import org.apache.poi.ss.usermodel.Sheet; class SchoolListRowReader extends RowReader<SchoolListRow, SchoolListColumn> { - SchoolListRowReader(Sheet sheet, List<SchoolListColumn> actualColumns) { - super(sheet, actualColumns, SchoolListRow::new); + SchoolListRowReader(Sheet sheet, Clock clock, List<SchoolListColumn> actualColumns) { + super(sheet, actualColumns, SchoolListRow::new, clock); } @Override @@ -35,7 +36,7 @@ class SchoolListRowReader extends RowReader<SchoolListRow, SchoolListColumn> { return new ImportChildData( cellAsString(col, FIRST_NAME, errorHandler), cellAsString(col, LAST_NAME, errorHandler), - cellAsDate(col, DATE_OF_BIRTH, errorHandler), + cellAsDateOfBirth(col, DATE_OF_BIRTH, errorHandler), cellAsGender(col, GENDER, errorHandler), readAddressData( col, diff --git a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java index 3e4cc69b5..ccc538a2b 100644 --- a/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java +++ b/backend/school-entry/src/main/java/de/eshg/schoolentry/testhelper/SchoolEntryTestHelperController.java @@ -5,7 +5,7 @@ package de.eshg.schoolentry.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.appointmentblock.LocationSelectionMode; import de.eshg.lib.appointmentblock.api.CreateAppointmentBlockGroupResponse; import de.eshg.lib.appointmentblock.spring.AppointmentBlockProperties; @@ -24,7 +24,6 @@ import de.eshg.testhelper.api.PopulationRequest; import de.eshg.testhelper.environment.EnvironmentConfig; import de.eshg.testhelper.population.ListWithTotalNumber; import jakarta.validation.Valid; -import java.io.IOException; import java.util.UUID; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PathVariable; @@ -37,7 +36,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class SchoolEntryTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final SchoolEntryTestHelperService schoolEntryTestHelperService; private final SchoolEntryFeatureToggle schoolEntryFeatureToggle; @@ -126,12 +125,7 @@ public class SchoolEntryTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/school-entry/src/main/resources/migrations/0077_add_auditlog_entry.xml b/backend/school-entry/src/main/resources/migrations/0077_add_auditlog_entry.xml new file mode 100644 index 000000000..28652f29b --- /dev/null +++ b/backend/school-entry/src/main/resources/migrations/0077_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/school-entry/src/main/resources/migrations/0078_convert_duration_columns_to_interval.xml b/backend/school-entry/src/main/resources/migrations/0078_convert_duration_columns_to_interval.xml new file mode 100644 index 000000000..0f9b823d4 --- /dev/null +++ b/backend/school-entry/src/main/resources/migrations/0078_convert_duration_columns_to_interval.xml @@ -0,0 +1,30 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="use_interval_for_durations"> + <sql> + ALTER TABLE appointment_type_config + ALTER COLUMN standard_duration_in_minutes TYPE interval second(6) + USING (standard_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_type_config" + oldColumnName="standard_duration_in_minutes" + newColumnName="standard_duration"/> + + <sql> + ALTER TABLE appointment_block_group + ALTER COLUMN slot_duration_in_minutes TYPE interval second(6) + USING (slot_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_block_group" + oldColumnName="slot_duration_in_minutes" + newColumnName="slot_duration"/> + </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 e440340e1..1fbfda436 100644 --- a/backend/school-entry/src/main/resources/migrations/changelog.xml +++ b/backend/school-entry/src/main/resources/migrations/changelog.xml @@ -84,5 +84,7 @@ <include file="migrations/0074_add_cemetery_delete_at.xml"/> <include file="migrations/0075_add_previous_file_state_id_to_system_progress_entry.xml"/> <include file="migrations/0076_add_waiting_status.xml"/> + <include file="migrations/0077_add_auditlog_entry.xml"/> + <include file="migrations/0078_convert_duration_columns_to_interval.xml"/> </databaseChangeLog> diff --git a/backend/service-commons/gradle.lockfile b/backend/service-commons/gradle.lockfile index 4b85b0791..641c789b0 100644 --- a/backend/service-commons/gradle.lockfile +++ b/backend/service-commons/gradle.lockfile @@ -25,10 +25,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -41,9 +41,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/service-directory/gradle.lockfile b/backend/service-directory/gradle.lockfile index 1a69c45e0..36b9881a3 100644 --- a/backend/service-directory/gradle.lockfile +++ b/backend/service-directory/gradle.lockfile @@ -60,10 +60,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -114,10 +114,10 @@ org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=productionRuntime org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -135,16 +135,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/spatz-dns/gradle.lockfile b/backend/spatz-dns/gradle.lockfile index e1de784a1..205a9bf9b 100644 --- a/backend/spatz-dns/gradle.lockfile +++ b/backend/spatz-dns/gradle.lockfile @@ -20,10 +20,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -36,9 +36,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/spatz/build.gradle b/backend/spatz/build.gradle index 8e36fefa9..90c5a3f24 100644 --- a/backend/spatz/build.gradle +++ b/backend/spatz/build.gradle @@ -57,7 +57,7 @@ tasks.named('test').configure { } ext.listenPort=80 -ext.additionalAptPackages = "netcat node-ws nmap dnsutils" +ext.additionalAptPackages = "netcat-openbsd node-ws nmap dnsutils" createDockerfile { healthcheck(new Dockerfile.Healthcheck("nc -z 127.0.0.1 ${listenPort}").withStartPeriod(Duration.ofSeconds(60)).withInterval(Duration.ofSeconds(5))) } diff --git a/backend/spatz/gradle.lockfile b/backend/spatz/gradle.lockfile index 65b35f5bd..8bb76ef2a 100644 --- a/backend/spatz/gradle.lockfile +++ b/backend/spatz/gradle.lockfile @@ -50,7 +50,7 @@ de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeCla de.cronn:postgres-snapshot-util:1.4=testRuntimeClasspath de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath -dnsjava:dnsjava:3.6.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +dnsjava:dnsjava:3.6.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.fabric8:kubernetes-client-api:6.10.0=testCompileClasspath,testRuntimeClasspath io.fabric8:kubernetes-client:6.10.0=testCompileClasspath,testRuntimeClasspath io.fabric8:kubernetes-httpclient-okhttp:6.10.0=testRuntimeClasspath @@ -103,8 +103,8 @@ io.netty:netty-transport:4.1.116.Final=compileClasspath,productionRuntimeClasspa io.projectreactor.netty:reactor-netty-core:1.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.projectreactor.netty:reactor-netty-http:1.2.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.projectreactor:reactor-core:3.7.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.mail:jakarta.mail-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -151,10 +151,10 @@ org.glassfish.jaxb:xsom:4.0.5=productionRuntimeClasspath,runtimeClasspath,testCo org.hamcrest:hamcrest-core:2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.java-websocket:Java-WebSocket:1.6.0=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.6.1.Final=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -186,9 +186,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.reactivestreams:reactive-streams:1.0.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath diff --git a/backend/statistics/build.gradle b/backend/statistics/build.gradle index 637ff8c4b..d541c057d 100644 --- a/backend/statistics/build.gradle +++ b/backend/statistics/build.gradle @@ -19,10 +19,18 @@ dependencies { implementation 'org.apache.poi:poi:latest.release' implementation 'org.apache.poi:poi-ooxml:latest.release' + // arx + dependencies + implementation files('lib/org/deidentifier/arx/libarx-3.9.1-min.jar') + implementation files('lib/de/linearbits/jhpl/jhpl-0.0.1.jar') + implementation 'com.carrotsearch:hppc:0.6.0' + implementation 'colt:colt:1.2.0' + implementation 'org.apache.commons:commons-math3:latest.release' + annotationProcessor 'org.hibernate.orm:hibernate-jpamodelgen' runtimeOnly 'org.postgresql:postgresql' testImplementation testFixtures(project(':business-module-persistence-commons')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/statistics/gradle.lockfile b/backend/statistics/gradle.lockfile index 18feab529..34fb44c6f 100644 --- a/backend/statistics/gradle.lockfile +++ b/backend/statistics/gradle.lockfile @@ -3,6 +3,8 @@ # This file is expected to be part of source control. ch.qos.logback:logback-classic:1.5.12=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath ch.qos.logback:logback-core:1.5.12=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +colt:colt:1.2.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.carrotsearch:hppc:0.6.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-core:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-databind:2.18.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -46,6 +48,7 @@ com.zaxxer:HikariCP:5.1.0=compileClasspath,productionRuntimeClasspath,runtimeCla com.zaxxer:SparseBitSet:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath commons-codec:commons-codec:1.17.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath commons-io:commons-io:2.18.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +concurrent:concurrent:1.3.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath de.cronn:commons-lang:1.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath de.cronn:liquibase-changelog-generator-postgresql:1.0=testCompileClasspath,testRuntimeClasspath de.cronn:liquibase-changelog-generator:1.0=testCompileClasspath,testRuntimeClasspath @@ -66,10 +69,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,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 @@ -97,7 +100,7 @@ org.apache.commons:commons-collections4:4.4=compileClasspath,productionRuntimeCl org.apache.commons:commons-compress:1.27.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-lang3:3.17.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.commons:commons-text:1.13.0=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.httpcomponents.client5:httpclient5:5.4.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5-h2:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.apache.httpcomponents.core5:httpcore5:5.3.1=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -132,10 +135,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -154,16 +157,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/statistics/lib/de/linearbits/jhpl/jhpl-0.0.1.jar b/backend/statistics/lib/de/linearbits/jhpl/jhpl-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..94f8d9470d00f9f2331b24e7fc408f81997caff0 GIT binary patch literal 75235 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 literal 0 HcmV?d00001 diff --git a/backend/statistics/lib/org/deidentifier/arx/libarx-3.9.1-min.jar b/backend/statistics/lib/org/deidentifier/arx/libarx-3.9.1-min.jar new file mode 100644 index 0000000000000000000000000000000000000000..e52b715f1d4cb47ec1f2b6845152030d9d67cd54 GIT binary patch literal 1065407 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	N-DT_U=tWFRsotBBJcx7@?-1S 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	u)f!k<7`m9=S>+&03dQ> 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	r7y!ZqDcC^753k9D*qVk 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	BU=gFT_C#iP$KJJh` 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⁡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ξ(80%a9<IyTg+J4_^K8x&k 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¦W@i23<lVnN_U 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�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%0D$sjg<bpYQ{GENu?lP2q_yOm)MmLR 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�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�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⁢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�#`8psB<BD)pZ8)Ju!S> zt?=suG9dY{*US;U$ToMG(xcB|vD(qTew{SIOj*EHL4~Fz-24oZ&I%Y&E=`gsho&tK zm@2<qBGzCi#!0boK2#;bJX{q>`N4--rK?JEZ6?f&1O8Lcy*x`pLJ3t(O?j6zEiHU# zKxptJ*W?5>X|he|SjkUeW)D7)Mv2{BVqc+KpEi1J{3-Efb=F8zvHSs$Vj?8pB{4M( zcPOeIDrzR}>>#7H7MR^P{X;Zo#lqE8hOKd{W%*{dkzSTl!_M3rc}9_zNg7OZFT6SG zn@I1CI?9?tJlzERiUwJQ!*UUI*+UMjYO}&8iwF_3%)#>*lMp*BrT*mAQf%ce6mLzO zdda3*ot4bYr(_e?W{gz{(U+Q{zWlt!lU!_>s48-$7ihvz$p(#Zm*N$LaMiXj-B>_* zo#v+#R@RwGGp?Ua69uA9RW4%c#3JS_(+myk8_=494<y&#f#jH%Y^G!&N@QJBV}X`W z2!5FG6CsJ`IPYx*tL<e~!=l$13&;xenv~x8mupWhL=~vVI<GWXv9O3nSN{lyaqd#g z6i^xW(*a+~qMq*?DRr&&wed&C^(Eb`hcUQI^If5dq465KvF$E}dHKq($cXj?J@(U8 zw)66?+P8hJ9EABBv1SGf&6mX4CiWk_w7jA-<+z-p+~gp!Y?$2P^Z4C~WncW{I)9zH zfWLO|bFM9+J%wVugnvLg>U4|nt$4NL`7ln+z*#d^G2O~H!eW>&SIdN;qih!~qeCRn zOy9cF)$NM9D&4G=z+jS?)k)643VWe=bFM)qCJ2>Cv@ED7987_pl!PxlIhoTk$_oBP z5Tg*2lcfvL_Rcn#)Eu9>i}+_lY+@I_YFSWkc8WzswpOPAVeFa@KAH}O|26aMm>R#~ zbJ($|6~Eyl*3#Jg`#uB)=dYEZVekxW9rN!DL9<hdED3c|m|7Q7r|ula?NlkEeXH0F z9rxYcS(K1bdxU+|t&>IehG-~seP^po{8=+>jCiE8H(Yv{q}%X|6}%P;YDqQ~c%=tv zzLFz$H$r{T9#y0+6f|iak0JQwi>&6UUgF`?G5Kn#i3Ek=wm9xwpQo+|ynG8}?!hv& z)iSQGY-xNNKO2-Q`baj{6jTOjbw8~PAunt6+8U2K*nGL)wCog9#Nwy6Wd45eY^!^h zqWNS%%(?4uO^x`2=fdz3PYKGaVWKf8E52NY!1X2<WFYf;r2(PG)6`Q$PC-Z2wZs-U z*{#KmNbXE@)!oow$i{P6*$)rnLoOJb?T6X?(b?;ovS$R=5oTLchoV+i;n>cVPI%i$ ztflyoLACzkFV4`*%BYJun(N1o^K-&ADeF;|FhXTm9y1MfH4{(<Cc^B~X6_L39*ub7 zHgj}*tlQC7H?7g9DCYIFj=@*;XvYPd{Ny)fx}GfHs57#gc~mMky(#r2Cb8zlk9QcV zL);>@?F&VSr-$K5GQSLP_Ck$`CHHe8P9Uycupe?A*K4QqVL@Bv)(-^{BFA;5HtE_l zax)y82lidY#bSjE+~QVUkWq3AgvUxigpl3?Q0OphFcdRiNF+fnWwTl2T~ICnXY`wW zAHOm7NL0mAxi{@|`lPyoAm*?HRF#D_W|o8|o9<AtT<#2`R9ICHUgdrVQHWJnO>2ou zyhu%KOI=`LGGLZl1B)Y+9?&=%6!`)P-{PPCWh*yVV^NjY@;jP@zCk~<v_%wDoLrWm z9+K0OjyFuKb_49^^u9Qm0!F7Pq<op=^sX@XhXQ#{f9g?_2W3EpgmXqRT~s+$@RUSl z48ZAgrWaIoL_yAil%mg}NX_6paOQ~jzuqO<Vr)2OY@xA*d&CoSGfwY4bii;hkF!!p zBe)%++&Mt=bY6BY0FCIWzFbD&Kp7jX=GfcYEanN-1Sa7v?BhL!W`}tbi-n!+c|Tw( z6cTE!2t+-ecV%la7gJ0^g*N8U7Vg_d$z~?t<s&6|^M6}Ekm?7~@Jf|fQ+?o<8rOK> z7k<D}nKUfK=}|^#mvl#Sq8vWt+Og1U;X@>Uy@bx_cC|xdu5x7vuvAj}M3t;E2OpAH zReY$ZHt!_ObNZ<==&S&jLPy|E-87^(m&-zFIZf<bvdh*b(-nO^0krqp-_|ML?4wGo z4B!jC2=RI?0-6+5#?hzBnnO$-yL>g#;sRzgSEk4fh^{eCc^dMk_mClEU8j!uY68Vx z--)8dCZt{GeX;}2R1nZg{plU^8={pz&!eHcp;0#nvj19t)~73`*JC_Jtfv3k=JiWW zowIs!eeCd9-Ue00eBrW3TjWH0mx0^+;a8j_<QA3M`x9hQYb;_7SoS5+Zsdswdnfpb z39(cem+4h0NLfR4CtF>aVD*)C9U7U$gR$k%^oRY)?2yviOur??EvRCs=sIL)u-co$ zW#WL68b6{!oCttUA58$O_BRp$91LjVj+Eb}qS~V#T~853YY+^UZS=V+qqj-GE>G|7 z2BImq!N}@|IXg1ZBg8|dVfGJ31sw*@P@!DSR%N~<Bgtv`EhG6_7D^Aytdw?`RJb(U z!<XBENkZDprl-gAaeO!0d)bHYsd(n4$7qL4Uq1XR760LmZw~>S9Gig)UjNUzAZ1(N zE&_A+Kkvd0kC9RWPL2Jq?2&y57xHjHVCWfn=m($*m8E)LvNB;Y*_#-w4WW$`;XARk zo6o5C0uRVGW3UMPV3!*%H19~Y_U<^yi)1~gdQa0mUUt1tM)tqlZeMeK%}J8Y!ts-^ z>O4Wa(X++5(dXZMN8F8RV1&ilC(y@W=pSv0Yk+HltB*UwP`sYhkzc&efnME25tUq~ z?$+;2J5@6OqS&UOUQzrKvyFtK<5j#|%tIPQF<*9^2xgDN2Ct1O|1&&A#PlItWT^I3 zsn9h4!*H|7D&vZ~*X*rTJ-%w)*n?3DqwQESdOr+3cm8_AC;Qp5=KS`fXilXhoXCap zK(P#}=XA*&Nb<>fn6$-Q0Lmsdh59GPaFk6JvdJW))OG7*Kms5FkPh&f2@Rv{BkeOz zMZ&z_eP^QCsdX63>r<wCdaFc*?fccSmFA<VE2gzq4v(#SY`W=c4xve)+G2ZUBWz*4 z)lktyN^;7E&4Komdj5V9dWlW*GQIRrf|N+rU>vri%tGx6Qd*RgNmo;~6_=C|;0LKj zO_OOdpLdK&^YqM)Qt_p#6SCixR`dS7B0WC02ud&S9;r=^HvciH26uJ&$EpqRT&su* z82@}et4NqUQ~9XOnS>BY%3G9+2BOHKrp1CE{P89aXxezkO%uWwK2#w4dNusxJ?cW) zxn&rjMJ{W|?p0%iSWOnwao;WFjllL^DD!?sg@8t8QNAC{`_!@v@vP7;jrzS&<MUip z6jD$<BY=X;f(C*{HjrJ}QVzgF#rz#}N4$}9ahr{Cpi&Z03e8(ZK3gHt2dV-i9IlBA zt4T3U^r6<Q#%j6iQ9s8p)(X`J^ChPgsXBh6PT8pY+v{QoKvQ$*W+$?4@|V?q!m+-U zbMO4oqPeqLSa3kneCI}dl3GY`8V$Zl<@M{SM&}8IWSO2OTAu3o`N}vcVap9FiSORF zr;f2T{C9BGZpe8#T%Q+o;4NkhEO1a+`rqEU*WWmjk^5U=X4sj8c?Igj74Qj?&W)a> zoj&{}D)V;Pay|tyiY<`Jzysf{|9a@?pL-aD1#NAujP-5)IAgCkB-;hN=S2xWKm$$L zvwfZ-PUHg}Aqn3GOgG4e*H^)iTGG6zJIWVuo9PCIrt6$A7d;j*MzaqtvZtI5JHsO_ z-aPH^I7CH-;LOo2v1co@1bXu!z%@avOXd|ShmdF&%kbhb1s9glh3<weHQ+RcZf&Tp zBW9p5BdWF$2<)cTZI*D^VH<;Ii*`9NTwvjXEjn=K5?eGkopy)vvG3}Z(yOr8y(>9W zbK<b_{h4FFz;3EEA75kwU!_p{DJHz7+6LWvzx4fZVcwyvgL{)$NS=mc4c^OC`EYrF z=zyYt7%sNZ0c3%JTt6RzBMl)X5;`6<#AW%eWRCYylmu4Hl%JtI<nq2|{Ol88P=Qzo zkp$vg1PNmftQ+Jx&&)X6lE-%?5=>f4W)gyzE@0O21^y?V!To0nA>vp6anP>rXxC^9 zC~0!>?&J#eM<SAND7+YQ;R2FY8tWWUod+nLf)_YGuDD9oLMpZAznndNdxEAt7N0%< zNeC8@g#1^(@GnnLSl{uFrRniXmbNHB@X>9lze1EJQdwtFt2!_8d7D@URf(0+d`dJ( zf**llZPqxEMyQm(*V`u;!w{7~_gSHjyCV0K)wC5;B=>H*$BCi$%fkiwCoE$;^W||n zB{8siMYWEmz$LB|J)wdjZR6ExJ2@eNZxPu{DcCgdq=Cvqh+JBd$P7PD@XrlxqyR+6 z#Z&uD8<dF~_fYcs;#QpL#PwMA0gU6@I!k3gJI)v5H>d;WBYvU5xl^BUKVB10M8V-q z%~w_`;(mXa1X!c{G<+zIVP8Euw+WwHf$mRok1#~BP4`1Fns346q}4;<pti(FD(13| z9P;{<?d<%$%NbAZEc0X_{W$Uy@A~8cq;sqk-7hB*Ip_^mF6)ud<uXjQo-cv%q12^F z>Y$0y`r=~`macjoeL|q0quI}(+yb9Cr`{vFZ!Aa%Ip%G8#D2{2)Vbl^cN`^9iRy<) zh&H5nwp5h_@q5k?(F?+ub~M`I?#{$>sHeZt^gb1vmkM<KOe$e6$33kB`J&0n!4jxe za4tI)*%_rGlB<NvsG?LwGhrQWgS_}*gfPi1a3G6S_zKm4;A=DvI;npOnDTh)HR$=R zxW+GJf}~kf64@+x(KYHC(M~unMLEHCrsV7z`eXv+YT0Pqqcj128hF4yj=)zE#jEgE zR6TkYtsx#l-}tDHI1sW)s+iDMZDEJr?(7g*XDS-RYL9=hrQgadNz!tg6WAomA^x{U zA?svqY;SJ(N25^QR|I~6Uu<AoS->bh`etv0jAsLWg;3NfE3i>!Ht0$MSJE?>AKZYv zT)cM$c{zZ?5H->AV%!ue+RgVt>lBDh{_fH4(B?48D+Rm&^$EN~9SZ!>jw?(xHF`)r z!U2ZLE`1ZEUj!UC5zXH&c{2nA91vjxPE}@qMN!zx3O+Ycf^-?EEHyw%34cW?PB;jN z-NX=U_r+LFuyEmk;DEpqbn9muRu2sH+1<z+uj)#jnWV!Y-w!k1_3y4IAaS~yi%6Je z7L_$pDif!2!pUF1VgTdjBFCgcnXMTepRSzAM4QXSi*{qA*y-<4R&@c20C^KDYIdG$ zt+A-;8+!}rFqZ@YiIPv#4gHcHAIyG@uB&47pN%%9Po)Vh$io$ct1EGUe=l2QXDGE) z@173eHRf;|v5&uSPf#72%?}_>WxsUSk(#s#b6(NxJ_;`3@b9L&+58&9hHGT@A_bkh zp%6Hb!G?+bg@TdXDjMLZ*$Z`2j46_mRYn8tkRX%L$1o~?+WnlLGbT9+c49p#XH2_) z&qr3|HMtCmuF$g;k?_qmcY`tAj>p*SQe7xuInE}r>%=2%Vi&KQ2}|SYdpHz#d1CO* z!`6iFJ|?Fw#p$!O2qYI)jrRzLNam904nL+&I2);J>+Vk!Eer<^p}};d1>ciVVPBv2 zxPo;oPK?20O-_2)wb()~l|Xfft0Ct^iQBB{(dS0S!I`Nt@{ClY!qmjURX1!&l~CKC zpP+Ll;~n~UtPbOGEmIt+WT~I0zP@Xn?m3BgNNOW|U{tX6TZc;1fHu7<4W;uFSs(}7 zLV1F^7w^qU-*}e>auqJwJC9A&c0rVq!|0|a(re@h?PIL$&W0H=fi1B1Aq8p8Y?lLp z1w}S|Q{C$j0y=2k#UDc+{V{MHa`6&;dLsRU!nF;!J~Sn%IAyjwo}yHghVTfNvZk*^ zXrTvvmYEfJG|>US6<Ci#5!<+s7&Z=l1ORM5o?T1O4xY8gf+8D$T#FKfURe@EW^QYT zP8+K|G=Di0u9@#te`|e%>yP}S9Z%ZMQN(?)7kW&@&t*oQWv3T3#$|2G`J%Nfl_W*8 zRjK_iR?6GEh?!Y?qy+qEHvrrIe-*=jY57Wy_U1OGf4qv(L$V-@Xo0T?i9Y)BJ&(ql zf$w7H1EipW6<jbekZ!?Yuy9QW<i$PFeu1!CXko5A$oDx@^nHb1>^1jzc}?}Q=I7JZ zd7rv5;2TVfx`Y9Dq$Y0gIe-`SkqiMUMrF=LB>bZ)jW%k}m>(T^g}c7pk{P0_7!q2@ z_51G(2aN4<rh}6EV#Sn#bElu7s$@F3*gsp%8(7<}8t2nFPLQ@cs~b;)l{B`6@8>Vw zCS!h!*{@pNo_6>$+Eqs_$`T|^d@_jXUo!-T>LSFK{lR`FrsRN0rqqBqfJMMYp$Y)C z)Q3H5ZKQ{q`gC1$&phG1ft-Z86YiGt&G5dQY+C$d**n7aPbX|!(#myDU>8VOLD^IG z0;7RdoTm&Y(e|r)(rKnW`O%EjhH%1-+qI4(DP@ef#)2o(CiWUbQX=MqRhL0`T4TbP zQtk%DZWqNkY=1q;@b-}W+tX$qfe#4<II;XM$_lvaN*TE8>JQ!vRGd@tm|ml*i=`Mw z1F4t38{ph}XgyNdu+WB~gbSql@&1v<ymX{_tlEt0y5=|f!BS7}Unn*`nwa31Ah93U zTM{q%#vW7a3!bkZ`{mv>yOt0^Y70n+z#PJ`DT{a66h~#5Rt8lK2(PL_1Xe7;I4ciF z#+IWUQMrzMbQ#X+o)rr`4jh4>J8wWw<<kFkCO78Fe&mp-%Fkd$UDGv0-(ow>rN8Fd ziU=ops8sFc=-N@?U}$%^kqRlBIId{@>j$&d3Z_Q2AYGH{(y{25_V`@OnOkieH~$3c zh+TgVIP+FVX(9yA@65{AeaY7|Oj<*SSm*bW5&<)>p<AbV(_=o~s}V3*voEB0<iU<q zy-~*;ykju*AY8Kg7y~Tj4gHH{06DO+HM(J{usf#sG8{zZ@}$#(PLC-{_}lZZ`pko_ z;5+bs(RL}0tuZSW@KOyq`dDr$Go-xRCe>C;*eBUhnO{7Au`CNQz7OMx<q7OP6fztb zbBFAor1~<<`9LmC0kgk_9`G;_vojC_RmGHq$y16*`7_$sXm|FT-Y*}_Gm6zwVmxb8 zG4V8ci3d%ss<8Nd6p6TD5mJ<HL!PW~6&OWV$#Z6&j2`-^&PCxK?rfy7KB$X9q@VAw z1lWX~->Eq2G>Mc#j*BK09Cf{`?^i6}(|P&JEB}@xlpNW`asyfie=ef?|8-CQ`O5!a z7tyV4;70oyi`5Aju)Zy#^Oxw=<XL%qTSRxm00HYqs_zt0Z*;xag2I+FUvkytw2tE9 zw;W)%7uO0)RpdnKU<FxAT2kKElZHkaslfbN?tg7-LZ7ue<;e0bi%BF&1!uVZpY#0N z+p(YXInWsRb`%3!82f*-MBY}n2^csy+UpxS{;Ovf{>S72x=rbecm|%*K&>)`>5>qY zNsU4T3kB-}3YVK(XT$~E(J4(rM)&lN|EFs~A!7`t3FGb6Fz@X$ciL^_9Q(V^9D_tY zesFNO2geiv_AuyyjtuBAkKIu|toyNu5JYgGgL-v=T{l{~b_*^lW{}Upn1%ALHIS1w zY;8<<fk0pXQEMbuuR%>o_0Tzu%RH8K&P63{-P5|;q_$sg8A7P4S?hD{m8N8Gss|it zHS4;AjmI*<(y{oC@@WxqtRm|`WI)q*-WqQk3LDjHyrRsU+`~xo`Cp_7TO(F1D3j{z zc*j<=O>-sr*D6%sa3dVViQ<K@u~II7QTmyiu|H(RcxGlh$G`Y#dJl=BUI7y}@1Wh$ z8PRG%v$EOV3$Yon?=5FSutDzzQ!0fi(WueusT%tvIn{l7f_A4R1@}9ucvdp_Hcl6B ztnhw>IRyF;t5U2q`wHa-$`kg6|KBRHM0Y_`0{GN~|1U!D?a2w6+vwZ7{p)i}Q?|C9 zlt=TTv!zwrot0tJ?RTV-H%V*Yq>O>%rzNBV2ye2(YE?)ni7zGJm78p+Wd{QnR~qvq z`fgCz<PePRH+~uA9CMRh%4lzUekboA-Gz%!20)MWAB2gOB1DuC73dO$LBWU38FD3h z$`^N~%A#1u5*psI&d5@;1gzBI+BCrr-Co&jMzMd1Trz4dTRnDru<5?kuHHYrZLplo z>Wt#4;ELEMXiU}Ly+HqY-K3Dk1eiPUBtZczS=oITD8)<3kX~U+i||@&PM9K;&$i-B z(A9TMe)cYCT``IEz(-D{iS>{|Z9k|Bvc1kW21!`2%|*Di1bts$)tF2|GZMp&BAb6> z3vx_i+hg>Y+9?<uq#uTb0!l`tdGlfy{cMwo+%4)X*l?iGJW@bw8oULi{&HyF%HrI{ z|ItL*WV_^JI4!tTL)jYk4myp2PFJ1p?!3MpW#G2NY{JS$*?=dZ9ciE$S*(H`;$hda z>G+tjQZ4S(GtWj>g`4>cTmWqQi-+L2W5d%GZ*nWXi-w<XY)MibYYG?j&%h&A7W0oO z$eKABleo`i*+I~qofV%Hz^HmdUwew@VOFFZ6Z)9aO66+=lNBx`bwV?Iw_(YHJbfB` zTX6i_f;EM%Kt#ekd0x>cHW}xwx!O=+Lem0PdJmBDV<MyEwrWv@`<qRm>M<fh(|RXx z1Xb7g#)l0OnpF3QUu1JmqC3M=kYw#<+l1V`VUpiTu!`hIQh%ZFMy{j`$}HKd<2z#G zGum>!<H)>TWB+GYdFzt0MT5pzz}ox>oQ40Z(fZp$NPT-_8%Hx^2XlviEzo!+P1`93 zw3pNxDeT40pIkP6PMcL%zEof+D5X@^LEj|&2y4I2e~)!1&9Tq!NxP$d0?~1uC(%O@ zKO2n{#3f%zQnsl!dc=KvY;!m=^nN|Rr~cY(#${Kb_UZj+TCM8Jtq$RtP9{|=t$ILr zmkew@=0!RtM2wF$^ROcbRw13x)Cz9UEJc)WzfNY1>fSMoE~$aAA!a?>tfwxC1ZI7` z<ro%l*N%@K#g%W?mIZF?mtAjku4k`i?r3BW&mMUIyURVkZFmE2U7eyvbAGi0bTK@& z_-u8q%;(2C7!N9ht*G@I37fhGzSRX}fQLnEsZfG@$YVLdW#L#WavtyLY`zbi9M?1C zl!Vi(R}X`vhNVun!AVJ}c#jrdT3rx|YAA;vQTGA;f+-W0m;xp><G`5!1wre%NG^O} zUI7n=Vk=6pcrjlhld_7O@^xM~C!nU-GD}q$51y(}s<qGVG(!Ua5ModvUAaRk<Cta4 zL`c`mxJk`i0mDmI(EUA6h(B(wPkyp@F;D9~$QOvOKTA$ifAB1j4tI5^U(ij?F__cM z;_MKreK{u%1Ko3uX!79{OayrO>U{hmP<;u$Gh}~)bZ<*uap_-*6qu(rV#KBM3>tPz z8MY1rK3sQ-Dgdc7yJX8aXXe!<y^y`$%ROw8UfSvx=NjCH8x|59qLm)>L;C+C?mghK ze!u^5Dx0#SNJb@@S(V7%WD^xPZrtIvcVs1zU0F?AMoVZ@N*XjoAraa}+C%-%OX>4| z@w(kc-{1d!Jno0;{dS(`oa<cYI@h_*buRn8t6826ruUCHMA8+!+vIOKzC~q^WCgu5 z1bFM=sZ_F&IAU<-7{1WryAPW=`%gK8(+&O+l@;vF?nT-UWSEuBgwC|BFw@I%r-*NI zKSjro{iOPQu?BnHXB&2ncV*Iv8|e4sEtP(@ab3^l-aWxY{|9mnoUG1mA^uIBK8*p5 zUkA>9eRJ4$$6QbOFlOfKg6o%$tXWzU6-W`PeWLFk=lYnHmHMY!9VuwMV(X#lJOi^- zPd&@>!$UnflpDJVluccmHV73Q;J9mlURR2S5aEVj!cBSc2K}S(+}qjD>ZO*{Qx)Af z8E|L8L9MOI3l2HN*X}Lg<4lSSU%S4^;ZQx@{O!7}qCuU{?kWZ8XQkyF%fshB*niaJ zF&E8oKLH7!M{6_p=N`*xNR=wFaxy!txV%u*)PnFZ!NvNNQ02WniT6!Y58aTFEOV4? z5aDvXeBStb#L&{SE*ANlHuZHaHj=rvs5hDRNyoi9Bavm<1;n~-X~hpe@aD|Z`XEEO zS=L>rxQ8ZhyGfRLpNZGjBPL$jEsMRDX6|8X;kCGNv*kp2$*DZAmrP>qdxgIyeH1fY zySRAi8#VT8BW&;2m<-^xikix@*Y_W#H@#$IF@Mtu_I)2CKA!N|bfNDXg-_nXA~Uz7 z_!8r7)(!s6oo;GfCq8l(i>vk@YxI2={}dW2R_@#;$GC7c_v5mW(%0`IdOO>`M9B}H zI3%B|cDewsy>eAbI#Yv)yiJ|>f!@2Pd%aZ6`%3fXb3ax!Y$-kYb?<>H_JhG4EUi(E z#td0OYx5o1*WTf|Ae_U;!lJ;!-?%%Fu2B8J^;-g!IE5r>fvvSAJaXbxnIbOxQ`6Gd z4jwCu%qhS}ech38mD)cia_zO-Iu@^8By$(5#FZCBT@=__zu5OfNEMBikii477%xM) z(xplA8W(m}1ziw)w&m_)S32w9yWQEbBV6q7mUJ$BN2$c_#mVR8Qqr9tn<Uk^-$y{H zwy=5q^SXkr*SZ$()<`^)-Y))Z&Lz*BYsGOfsn717>wR2sFjnFnhtH2Qu^vNW_YU_< zyw5pvZpr#f4#i<jxbI>ip+;$Y*!nadymZarsT=;LNxe(^$$PiRcU7nN4=&ppAnX+6 zne|}j_O0@wYwsLg*&I-(^r7KCJD-pAFTOOzwj*>nE<>T^^)b&9I)wCc6NZlng_<Zy zxDr}T2Uo8=0G$@?qC%hTj&ZX(mP+upvXK(e(>=AxG(Tr?q=D43bj!Z#y}5gT+_zu& zhM2?qB{`4tM(vVx<G}ZI2Mp9d?K%45=Gl&IdE(aZd_+A3O#Qm6=UIzf4Dc0Rt65ku zP`-AddS1$ip=#fQ^LV`Pt-+;AVM8^Kn(MR$4|r44Z>q}E^Qn79CFNFeEL9wTQfmSC z_kuK2ws)aC{>{8W@4i{T2y@uyq_U+#{E0{Boexox`WqCo@74}12;Szrkou+X{nIoG zFGHPm23I^0{}%MRmKdy;wlm#Pv}14!#lo*_CFNJ8A9Pkw3?_K?(q~<5FRy9c!L(j0 zty-*u?=1CY8cXIcbU!7vhFLRxs&}=B%Z%u~ySRR!&x<b!+G5YUI6vk)p?z!Jfg3M` z%XjrQ3`x-bh`=ouSNp;eQl5){zOF8ls&t#}mbyjX?(64Vd>A&L{ho!o!h_QM{MbY) z-_8>k-RvWOwtke(b$z)_mF3Nj`k45AH%lG`a#U^EtUeI+!edW>f-;TvcM56(Ub$V{ zTPQnIRvM?d;2Hmlr1yqP3Ppw=H5)CdYR#_aT_}-$$-KZs{j+W{V~RhQ;QSVb`7P2M zR#8m2Cz}(_giw_#YIJ-(t@pS}GL)lZ3C(~op<Z&JM)6I}!Q<f}mTI0=EjDeP)^Vj~ z&7pZulIV8vW$QdpKXrP8iTVxAE73`H-FowFKb^ndU|EpX|G9g7N!IyTc4|M^cf`S0 zdMf)5teup?e0b73y8H3a33VzOx>e#TdbM<NPP=(TJsfG~Yt>S&VZV}RA9p<K*GSMM z3Z5<=i}|isp1Xa1`;jY$)j#)3_J_6@-!mgi7w}gs?Bhz+9k^ol=$y)knT&<l>4crX z1h0Hb_uAY1p72u+Hc?3@isY=q#>L_SPZt$<1NSlTl^`d@L<tl=dbBE0Q&X!^`}<Q{ z_*2ViQkQrAxS!K&R(1P;nI?5@WL@vux({YgZeOU_ezCOfl)5JMoxNrw2X0?zuFJ{0 zooYYwBd@f%H}zRF&(ciWyrj~I6cvg*jSVZLB(^-|D^1)T5s^Zv9Zo4YS323z-i~ry zU7V!t<BevJQ{Do#ab!~@qYrSaczff5#=WF%ZUd*f%s&lHpe1<xJ^}5Ewz@V6EsO74 zYiJY~t$8p%akwj5uEowkp<$)q;7@wK(C%wA2j0aru2K3v_XpiiTE1pQ0zK7gfyA_i z#zQ{-pTGN`4jKOVqP~g3u!qPTdYAnGqiSSJf}|l`%6x(72mA+WH}N;pd2OvNiez}^ zs9!2ge@f-l#iFH!U8#|`7>7+SHlFBdxp3)-D-+S;Onb26)w^3HSM6_HTv_W$93fPm zEq!gFvC6Z(LL{^D;Du`maX4phc6AY-m=K$gbf&Ux)-lAU%#M58eBNvBR?yt%c&Fv& zy4J7uPdqDJ^Eh;FE%zpVX)GmVuCHfKXd))ATYZ(e+Ip4g97U?~^h3;n#)nH55kK`X zriE=i-M&n14Sl(!{z$rufUL@e2Z2uOI&d$OzB_&jS1IP&yhc+0%O*Dk2IItE%vwXv zF9p1%gU_hE6)#<NtK!Y!cXQhu>yl%eIOBJ?Uby9JEp$LNckA)T9m1wN>NU>1^i*Xx ztJ2kM?{@id`0O3Wc!%uIK1%O?TGjptEOuODc&9PW%!ivJs`}>LD!J8Z=iX<mv(%V( z-i(pfi;0cCyreopha;%NOlqeMW1HM%bB2OYH}j6`yc`d-SF3NobRf<l)`O~VcuAFe z=*fM&YJm>IF}XV|Gnv#cBwb1F{8@CaU-?14XzcR?VM4^jAF)k+8kX13>BaNBVY-CB z8mMrIiB068XnFR2X}l<r>&^bfvd8q()b_no_{h+Pw=Pr3y*<2_TlU+7b+&bs=U#lT zIg$30HT5O${qL`61o(9amD_{m&uR13F*hhyyk}mmSY!1}(drpn-uFTN%NlkD{mByZ zS8TXkXY`C|PDA&HAsXLu<rJ^MvvvE!LU$N+GF}xDOb$yCSQWeS(XDX43Rb)Cv{vDd zY46VM_%YW?e0dG4xRofc4OMQ!{5I`t_m@=bcRg*X$u$^KX4_mFws1pVerR~<{AbDO zAHr>xFYV1KJ)(DIRSlCLm)81IYKM%E$6tsf(%CX^_V3S9NlH~@igmu+RN^SKGN)ku zuDc3Fulx&Hymgnpl44mU5O;8nBb6ef#F2UXnUihHoLVSXZr_xVZ}B8pQGR9D@cbmU zq@K=A%{?soLYI%of1Izd|6)hhJj3zr6qGbh+WmlBO+4(X$X4!;PSZc9!&tM`bPKYt zQY7j<aO8f&wdyI~V#)gz%$h8!+supS&STptz|?;<bl93L=w_;j->QCFrf9h?%Kl8g zoz(>ClX0}y)rD*oTI*kQz6tp;Qs3%9ArhF^Ge>-pq}R&T>3*v%ojf^OWj4zvUt^jp ze=-mExQ3BPs4nZCuU5G{wD>h6Rmp>n=Yj))9RUZKICP(uamsVA-WYYr4qgs?O86Ar zqhmYdsdp@b&CNZ&d=YJu8}mBF^6Iw!_=xv=ECyX<otowI-uTEe?*8;}$I-IKAEkU6 zIku&Rs0mb-8Y&z;;!xg_apvYw-M1$pS8_jYa9(G2-={OiTT5guYl39&`2?aRBejav zn~FE)Oskd;Tv;s@Z|^v~+-<MSUIVC-j`?aU)f&2NGfS4|kn+`IyLS~y=B;yQV_4HP z^kp!!lqzCH^@e>k`*yUkavj+5mMyBXWt;3_`A*5wL)8slZN5(=Qk%U+m<j>{595ee z<jUX44w^<kUP;rfZOpiej$Wr(&G4Jn9DSQ>AB(B4<L~nB5m0{<`r&0;Jt2|)8mpo) z+r4zkzUb4AFJ5}E5b|7F`tNI<n471xM}<Xb<6;)ah^2%q+4E*f%E5Co3RAAEos(*2 zU*5-MCH(ks^2!~xne6jZp6s&tx~F5SujtdIKkWBN)CFD@=T(&$y!t3UYU3k@@p+`> zCzpMg1AFy7U{!0$tzDy5rRIY3#xkk2Ey(e8%u`B7yYpwvL|Z&;&WJOdvk1n`;pZ<g zw%@SlVW+3;PL{%mos4sJewoV_m`ms%htuoR;gt#Mu06{`ocy0>1~g^{eQy6OVN1z= zBDHSWZJQHbYZ)z5*~<;P7nS>ToM5hpP5OzNlP)-V=t1k*fsai$JEEV--_vXnP^TOy z#v*gkAlLk^s8bb}eOMts_o0nh$IImMT1_I@gKaVowLb1l@q3Ua7RTE!8_nWWwJM~O z?e)ciP9v$a#aR)Op$4ZUe#W~sh%MO1W<aP^FWGn{(e(Wac3(Fahazjoz_&k?f^X*c zZE$XrKh}4#`B>SWMK|JF?5j+U-`aXSS5Nlx=3c8)UJDWv2#!~iPjw5_r&M|IE#PuW z`cz7-DVGmD4f*R&C1?pts&BiLAS1q-{pX?GzYJfp=@|7p-#J|Sy<X40>y1atp-acU zG2SY?)crBcW}A88=`gtl5$Dx*6=LTYr3KI65?pii6!Z^sElqq|cS5Fi3EQ1yquPz1 zH4eJ%;}4y;Q%mBt=KZtpRBpIkz-I*f=uy3#A;9^P?+lBQl+o5T8@*yKJ!jU4Zp+L5 zPT?PZ<2ygQz{)Jz6d8WwZD+G@@hx39$LUo<*PIA=Jfh_p*X`bpq}aX<J)7t4iXDtP zzFpN`V)OoE%p2sNOFMLW`|Q$w{u$SwAkSCq95ybh*kzwHmjVnc77R;aN`|HG;b-rL zCt+IPtzvAe@(HK*`0d$0n8d_IFrmdS<v(-GOpI3KQv}!B0!yN+z(#^~J~4l!B;qdh z<|A9)=Q6Kwzb9v0n-RO$p+4-#H=gfzM*A3A)w1!Xdp-`|y~?Zlm}#|K)SK#~-31Xc zMDFSY4JJKmCr_=bTbMp`-RI!eHOJquNjSr+)Jv>Ud-}vAg>l3im(72s&7C18{lW9a zuI}B`tJ&9Y(|s6l{toXwlTa3;x0=N4RptI6IfKE?A$k{6axN_!NiI=QKXn{z>soo@ zUg=9l?d!RcPvB)Qtfb;y+{RP9pZn0J%Sng42_Ks>=kI0gXia&my=+$wCtPZ5*|t0E z@?!HvThEl@9@m9hEq5}GRcnc6(o(xuH+Q{b#)h`jOGLWkZ$!syEz(`S#9=4@y&-+k zE`h5LE;~Q-qq)=NxX5f|(<(d9U+=QGDiQ+TG(By$<kl^`)RUm1rG84IaKt`qQ=?<8 z<F3zK&#!OGUDO(s<e#A^rOd+}hYN~RDQM#tylVSX&wA%NwNJE*_>_2SC8_0}O81Hm zR^Q|jc1ZcTk?mdD9DMY9_7^W#?Gj>beqH&V<t@eT1%kqwr({?J8Q@*hhi-*|;k{fL zh4K*$^HMi1x7K5m9DH;*StP9X?7aCYox3c)?YX`6f~bw4l6?1jw$2j=)m~l;<qAo= zQ~&zwVRUx!{+&msf5AGqb1K=zQIDnR=7ICYvMW=Yjch9HKb@-l+wmy^MuQ8Lz0Q}3 z(=J%<o+aawpAt{mm%mX>K~Amm{2l+c=p8$+hkvsR{G`R*M05>*B+M%RSo-vf?e7oX zLSFNPMR5Pg)a!m7lDKXsM`C_M$C9hO9kQNH>a3p)*4LCqtYve^kRQ5RBN*eT^UY)* z_bLC-(8Hh4zVW);H&A5st<0X7&VO=|4As@PqQKCLD>rZa7P%|L#P5lnhpmj6WuRl! zU3c$m4^^~$pDe5h(L0|Zby3hp$?{^di|wz4ZK<Y-nWkb#{m-@v+P9ZmYRULsKf`gF zllxHn<q(O$iXSg@Lv(`&vW+V9Q$vmE)WVDiGU`=}qw|B$Y?e7;*DdA7&~rm$xkIf= zr*iETw)Oao^>;SC-{-RXE_D2utH*f=m#I=>DW;Fh!u1XAGrJgG;$v>~KHWXMcBJ0M zUQ+I<`-P73velwnzQoCgxU<}(X8Qh6xNNg-L~wQh*K%q%ucXBn)VQxVt)<lpwzuLs znv}9lTjy8$4_^h#T*ks9fuCd|!q}e%H%Yo%Hl^0R6L5O<qJmkG&PcIZ*FW--(-E~? z+5X-Qk-Tc(nWF@>W9G0p#w}e}7Lz8yqsB+E{MAFxgm9r${?-oWj5`X4lZCg}axksX zUQ%oIV^1^ASJagM$8F}ryw40)?1=R3rhVQm`88be`?!}r-3O^9hrz5o!K~Iy$zSOZ zv5l%7d0$vDJ9>@(mMoJ+1L>T?iM-paw<fb{#L>?wkyn?H2x?m%f5!Zo`lsb4zm6?! zp0l8+X*;|90j_)W47xk>4_-XDR=Q{H_x|tc$0-isHr2~+mbZJR=|+6*NHpk@8*r(1 zU78ef^6YV3W6iwhvg_{-CG*>+MVR@;q(0o-4j0e-1G~=Ki0qB96&qaLYZ2^TJG8F< z(%BYHq82q`QW``L58Dbep>>WSN86vrl|)^N?O0gRR`4*^_-Sfb&BLWaZ4ZB`+MIqP z_&idgS$vQ&_R*=Vt-30OiE>({<}oi?1`A*Kmi=1$AR?9bvw`q=lZK-%(fk`+Dy#+K zf^lW99+oUxl4(`rz?OEx-c-gormR-VxRx#Em95l8WjfpF<(%DFfpN=X^dCq|MU^n# z+OaX}N0X4UpRG&)^=H1tbw}LamR{cerc6@w;Q8~5qsnRV@ju^}$YgHT-u_s8P+C9X zv8HVKdnJvG^*+jLQdzI6#>trMG)k`B*oQAWrS)-s&y(lpzu<!kx1=grNNEk;RsYea z{LU-$EqlLHibjmn6Mv;}UcvWa^Wyk5&c*Ue&uh3}`q;3K>#>wwmL|0&t#+z*^>a46 zhLalvEWhs2<36x-$2|9UZ1lTM+MHDR@_3%D?odqs0p;U7wQKYvlOF4B7`N_S)af|- z0@+Q|PazjQ>YnroKEC5O(@*@7Ty&$*r;3IOc3sob1YwP=4ZKW|kD^_ob=Bg{*@}%g z3LCiKT%hGP-L2sMw7{N(h1HxUxq6(fO5sUeYJvQ3r8D!=oZcqIJNDgfPzu@c^rzzc z7nd(9&hcDwM%X?u+re1LhvzG;VfSjSh#uavq4JS0jC%tmB5C6sgw6`2m#`eDF<~F< z$=bZX&*WLP__bB6+st)|p4*qIw5(C;{Sw>b5Oa5br~R7BV9U#euI@Owip9aJ>Lo4Y z-s~4|gI%`Qkj&GO_Pb^0EI2C(4}Fw$8dx=-obaKVW4tXAH)8iD>!_^$t-JjOrfaNy z-j^G5B;Gr6hTtl@<)!_3{F*lNHglKsjU1c5q+B3m=F~{;8}cg6O1!bmV|Da#8Q0#C zE1g#<7SNa1{$SQ;e%o?%D8~1v<(^cD{g1eoC)4!ck6K<ny4T{vofs8ayw&{sPu7T3 z#a|lMKjO&6>R$I{|EVrX{S|W}&)%td6O&BMsHd_>Yre|!XWU)dzqmCU&SnSaiaB?0 z2zE@yTUI<@&8$f*au?Ybc9_Q{^uBMf_}V`Il7|&LjdNca+~~O1ovMGFXwLMsJHJly zKL4xNUvEA;ae+C!%0o%-+O~(D2MP`@*mE)2)L3&T&ilBfh!UmW9W!NyV}aZZjBP9o z4ath%c(lv+Zf@aD9Mn8fs$l!zQE?!pkBH%|KGTk8)n)c`4;O#ou`Q~Ok_gfK8DhLM zF>d`6GvjMiF}rJT7X{AU6w~_2@67Qhw+5Bz&*$UT1XQ=()p(Whl$$8M&xKjzv`hOp zIW_w(>LL0=jLByU%*4YsHr{Bn9Bvd@Hn)?`*O}=8!;KRUIcfa2C9R8HYwd2WDlg`I z?NO)C`46{UR8?ha_9h>Bu=K9+gV%Syf3On@<uH`4ddS+|%v1Ps-)%{TotK(fDv}<Y z-&`C2L81TqFV>5vxL+MUI<j8pIZF<&WSVZFf*s=y<yTAdx}>fs1P(ag*x34^)b&f8 zb08&dHB~BQOy-^y-0y_8)8vK~`G_~qDPw&-pHipjTJ!wtCYJ@hU1{#w9vL9ktEIoV zpA=T(_L{Q!!)E^hOMPEY(ddtZ9G`m9E-#b&NMB}ZbjYZhKT$|4gYs8(?E~7Nu=d`7 zj6<!T`B&8~+jMhTymL?JYSukzOWB%&d-(-^xu&mgS+*dcVQuxT!|x&u(=4cu2dgwy z=g-Lz*P}_J5A&8*_o2Dt<KJSM<(a9lMm%k;_tW%-R-MB!4=oKc8!{`@?*vk>SYm%H zDvka}%!`I0gJbrs*<6YZo79DjX&#gg-oAFJF}g4}`pM}A0hY@Ts|H!MOkUN;;+NPz zzvTQBfBpkII<C25$-xo~0kaF7=#$NbjyetloG03EoH5&i1T>1L#oRVn2%n+SFf*$4 zXDGMja>&-q5#n;)?h=(!MNlY?zg79lkZEMqPfFXs8ueu^_DE1OxK~5B-A?LUL*v1N zYd?4RG`)ppy|$O<R_~%=;mT+|$s7^$RWhfVTZ3z*Y@T0+h?B3Vri-&qy%_KQ#CFCA zu}(WK3Af>Zj%_;aC$|o1?&sfJ;VPb9R$v$C=Pe)b<)vqp69@i*zw2XFbF0LD`cBdI z`=?*`85pM=@^mScFB3`MT)Ftl=|lHR)lVPDY<gaN)1}ehJ#z(j@rQM4Lpm3AKRzp7 zvUZz?;K##e!o$WV-9zp>Xcv4VJXp*aY@ogG%87NfUM)A9zqMWNGUk<x*6vsrV4X)i z+j>q#BQbreKRvTXchkUrUB{MWU%GaM)R!DK37QvIYMk-8+1qzEr*~hKSzg_(#Fycg z`$js-V#SB9nr(QW+*jq3uQl@KE9LgWZzFE`CM<_6J)Xx%uQ)8rTptv(+u_Ti&z(kb z5mI{=!<*;Sp{1X>OQjE0MBs0y2CRtY%BaQ1<eQ#NtkJq7Sr;5?XT{Y>8{?7>?^~Sj z^tm_ib@TR(4_RnlCJ5bEIUm)iS14~+!rz{edpV7ATN=?|lctpBFL@?5+It5&@}*Q( zNz>kCjLi(OdoJ&C{4?{HJzZKwdi-g7^OJp|n}wH$Bs)Eq|8b&DDBqwv($+4{!pyq) zN3@+2pWq_@?E~={pUxJB-pyFB=%FV?XjS|N*6vry+qXyBhuaIEenc4{c$|4y#A#%h z5O?fywSA{nY`5ZvE}D6B6?mC^WD=F*6)jG<?nvRZ;os6aQlVp#@2he9HszIgTjs|f zLlm3+d&XT85{}I}TL^K~Y6c1lxhcg`8|}B@9mcPM7Mn~LYcc=SWL=clCs!-bRp+m| zMr`ql4DETl)VS=K=h4wVTFXgbj8ESZ6o6L~f8iOXyySh>!pnIkb^DapmU|B$FzXBG zUGZgip4P5=k?Kc8j@`d~?DC<3Lj%V{KGt_0-$=Rny8J$udf|GjW$U@hYD!|w?0H&6 zq#|_AUbyPD<+Z+f7h^#Q^EJkL(fac|K0H#1)$^XO<?Ow5<K3Z-bP=P(K6+vA;{AFF zo457d562Up$808gUyvx<vGib=@JLFp;QTYJFHT-b>C*P5rQUcSS9H+Ya`0GfMeeIw zf-HwqQ7oY(=5?N!$tK_2sK<xTZ7ZdANX+Lh=`PNreVxT`=BnZI_Q=^&;;e~czSah- zGR$}%KGvmESfjboD|pe{Mf3|&7XCWo<kHA|wK#E3Ztl5l?cI%3)K7~t^C*rvBxY3` zl=r-j6JK5X^qEzkRj#Piy!pIK_S83As_0snoz?${(SUc?)q%}UT_smi_@&h22nRkm z`4;c~bv5xev)Wp!sw1xnal|8cU+X^)tj;*BrfcRO(>VX;jg!sAoLL8xt|aE0u6*FF zo?6?^|3Je|-N{UrHHYE=-=1Cb+ZTzj4yOcV@E8h3yJuTehSLWhk!l`%;Gue^HI{~c z-CZ3aqZ5hk>T{{q$+0um+;DZ?Zn!AXievvAHs9X)qVsJcqGY7PY+X0Yo7XSfU3Tvb z*CGb1GX>V$uCQ>bJ-Ot2Tc3?@OOBg82fTn|DJ)n}%<$0Y#BQ;etdYgW$96_0z1tnR z@m*$Q6Tay4J1VuI=)J~6%Wl+Kere+fc9iBfNpv#VX8kGOhj8_tT%C_m!8gjQ*91eI z<}`AywiRyeJI$>2{axJKn?VI0E9(|h9XMGh7PNd#Ih~@;Z8vdB{0*HuTQ~NvUe0=M z+ipkdrBP+P2emTy_Vby`NAsv0+_gA*^($6qtu&rh0r!uEd6xFBImEr{-q%+*I(mh# z<~vk{$z0?Y6;pX$vSn_1dAhE`ueEcAJL5lAY~Vbcy=QCg96p7t7u;Qf($c0W-7j^@ zQ*#v#eoA1f7i3(R7-}XLy?&u>R=&?ija1uxl1y3;8NZ)9vUS*mYPa{b*G8gj-g(cf zEdr9o882!$2N1p;5N}R8zUjH**O(mn1&#L?U*=^zey>%qo}+)uho+h+;+I!vX)b=) z9x;z;=)MB)+vDY1PL!`0679e8oniYE1>x+0pmn|dpJl(cU2<+$G`MuhXwwllnxDzG z#PxM^FR=6}6n&X*uw}#5g+m)u`y9-IPJT&t*D+d2*}2!wk8bGLJXhWD7eZDxb41hZ z4lXWjiuhs^$wWLMwczK<T2tbQl?#4ybtMg{dL8~^5qW2svQ}iFUU)n!<6a6iR=1Uu zl{!P$7$13di7eRuw!8D~Ml;u}?ut)G2ly(E?CBAxUzVtJJzVjF&*tZW2i41{4m;DX zSKE}h(j$3i*yf}A0wbML16_`%{yfK*`!I?xd`~vzh0JAaUmHXo_U>(4|Ap7NQaX53 zF8}=n4w8jCcHVfAc73<I#K*T?__ijZ*5R%nCWGQzzb5Qe*&$crB_6Q&*?>&F%~}UF zp^I@<J}fPF#Xl-u+HQAM%We~c;IDBVQ8s3H@c)2S{45PT_dX@pXXH(AC-@TlF*9F# z^-S4RnCpL?-eje-|Fd37(U%&bBPB;-g*2IsmRMWuqs>3C-*Hbo?(`XjqrC%#LoatS z(3IU*t++Y-mgd9{(Is`A%OaHL#Cm5QeD&(<=i|;H-@Xqv*;0P!xx{ydc2(>be*VpK zITQ!(zNzQf%%pr(HlTW7eklF7Cxc8>@*Ir9)z`Xg?u*5I+ZF%qRC(Xdl!r@RS?oQ! z9OtONrPsYt@<fPy<(b>Boiz1m+a;y%vEIsC85q-kZ_RFPLak)Lmsc{ar(zoXLTZ^4 z_uE+YuIwHXO;~HA_eOQ$D$alksjOuL&iFdTudg$3iv${nmdgD4b?n}dXw>>FJ=5!F z=#Ca{Wpy{Pz2QB0j{4OwS3#m+>a9WQ>>?q#Ukf_uiPHLf@zyD2S(jJybW2vm+%&)1 z#I=`k;Q(dKJKZH(jaM!vI^6a8%5BY9=;Fy5bnE8h$im`BYT|3%6VEria*9iLQ`)_3 zmoA0V1?_u*XJ6UnHKdruDHt{;E=x1Z7qOPAyIcL9db#flD)aqu!7@cIno)Cx<GK?g ztK-gdSx7~Oh-gY(Kf)^&%F*-EiEI5(#;$jd<n}Y-MBlTtw^+vAWn>C@kAL~|d^C5~ zDzl|(Pw(lqeK$XS&2?4(Dzm^_!E#N`homcZF2BmLZh_*{ExfzxO4sL0aUC0yxFy={ z&8D_=pY%>*Mx%`F<M%>w^+hMPe-L_EDbjDjYF;!yUG$_tfbr#WDm7j|&&Og6S@I@6 z>37xNJWuswI7aA<lgp|~yB3+WK799$bymF$d4+F=LZdH8mknQwQnY<4!2Xru?21PV ze?-4#5>~pMax{+dqD@JwQsud??-dDB2l7$`+;Z4XMVivDV?W?jBcV>ub8v~q31u8x zl%ABIu!f^;oSiLQebUI<#z3BnREej1{pN3xImgrQnDTP*P>x$ybhK<u+PWkLsz-^D zrUw@W&6Q=nRQs#(eLL$#?Vss)w=gq3NVm^v`1n*gyqk$Vh~t!p`{GmdCRb0q@elfL zwWT{lXdz2wo!X9Z+v?cEWm~3~^)q$x@WxKi*dPZLKdF;<_P3a{KO4Dk@#fSKS9b&b zr9yd&^xj_cJtMK+09Qw_<3Ii9pn^~TFmh1Apt)#w%XNcKx8JUPy8XrKr(xfJ_N@x% zC@t{fw$!((UX<lZSlzn9bFMs7$Lf?iceVgY54+mBd7Mm;22A4LlzOQdA5*%e{n3li zfy(NqzKUgePeq^mbF;c0jIp!gTX@`ECb~WKQrEVPuWS0>_Q#)_uO9EFb(X38tkTQ4 zno!O2trhWH4-UC~+x(gR-lZQB70U{FqzbcC2Zt?^V%^U_PawF|o#8zRNx!zsAx*!N ze%6&8mb#Vs`$LV5<M_;8`?1X9H9r>AmlLs!;WN%ALtR{kn_>PiH9hB&F8-?@jvp|$ zjW00b+`NAE3Ab)33od)vrYnq2+!nvK?*3e!`=MB*=<9pAny#NcVp3c-9&(2V&04wk zH*LRWbFtFsl;PDdDZ^DSOzbY)InQW1x3Z|-pfjH_t&k^LP*F-W;a<I!Y+l|k(=aau z%X~(`zL?X6bRVvM`D7EbseSRX-On^`KWHNGZ)ZIc?3wbGJ@gKLB3Bt<#V^XYTqi%X z2uC{#Gpjn$vKBt{9Ny_rM-xs6cBocYW^G7z#nIk=elSU!U8zTv5y|=m4$~E19kMD= z{c@IHNue&=@aMc&S6FUe#SK%2e4;^bx=itpQjr3bc=(d7930(rO|0KAxP_`f;s0BK zB4;J%%|o&R<v_vPk1i^7=g&h0%DJjQgZ|$YD0arn0xUEQpB%ZgXVI~h@6AMqc4$2M zxo$pdh|--G)Jx5tvv5A7z9nI#M>&UmF65Y?XhZv8usQJm_d`bkvG?eQnWA3>`GbLm znTnW>p{B&on-nmy#(tHSLJNNNhlaK97WkhC@MTB-??0csh*j{XzrSjrVyL63VQMCB zp!q)v@JjDE@G)o#3IzNdQ1I|}MzK5oH(161M}MQ^&u=*5364+{OmHILy-~luVjLg` z>em?{g^K{9ejVo>I4OyxC=yDfNZ_2Ez46X)Vb^C;YDfvt?+>DZ?NgxU;D+<@0ZAQj zz61~Aq{M~CXpbtZVG6{({BUjr-=ImU@}a0MfcOPdhd4K94{w66i~FR+_))}=_HdZQ z9Pr-A=z`4nNy&+z$i+{E9NrD@4z@G-Fd@eM`-2SAgQ?K<#`}2qc{`99f1WYAqeH|4 zxnOMhJ>1>N4E7QfB@2?2U`*h4FK$vba-nFYlA`74IVqK;C@MEdQNaf~;5`vro_q+< zW=9RK15;B@-Z*!BfQPs1q^vDNv8G0zn1c)6VRDklMgHF(#6)7IO41P$`rZV4ST4wn zulN}GQA=r=Dt%{f4?j<W)8tcH3`JW2s^iJ%_P07*fOik?$w^D0NV`v!G+Z1b`Z#%b z!%g%_2T}+{{sMXO$VIzJDXu_K{7Igo8_w64;4rygLQg0)$l78mnBXxfYiMT?M2d<# z9xTdXQcCE_beaq$|J9QcL;LU{QpCLRZUme?f$aLS2sHo_us6okl{dj>@(GDH4d+Se zOwC!NO`~{<6#f3|*Jvr0Lr4-lxb(9zEF#u0i~qM|QBdSX43HwKVq&F2^dJVgBh8V< zn(F>9w)#!ijg#`#bD;SW?za3dvOwtWg6I!ZjcB?icppDE-`^LR#}iNTyZ+Eakb=T_ zD+R^>A`680xdl@pj!jjTCAI}InLyUxHDzOipBLc)2OFeVRzn3rY92%<g0mkqh6N`& z5eA7N70&#+{|W*i0<1#+^L7aHm2m5fB+P#aV<{aWO|RXypaPWM#{@Sl$JmFTc@eLn z^m1yXVH8k8K)C?kn}~D67UtaCd4|`)eA>Y|siF{(zfKYHk1>tvOl2%q!^X3}xS-S# z@ZN!xNV&t<Cof{T##HePJcu5?9z=ozgqPSTU%M_i8Ue}&K-n+`0YCF1y0oT>Vv5Ik zJGhwo;yq3A?l{;HIQU?LDK4h>NP?-i$cKXBf59CBmY_RDu!)3`sjaCMvx^bNg^%I) zamJV+c@cQnJCYt7A752G5${CsMcDy1Mo$r4&pDVgYhd1qjsc8n>RK>q3{5=F*UuYo z>Hs1BSWAiVbNu@gJ0qks(GM7d|9Lw^Ee#Q*DeM1U0kI|sqLN2X>zR<qmIqiy6I~Wf zp6F_99kjO%bFKwFuY(;SvKb%y<VD2nB2N_No1upzUg-CcC^iBiY2`j`X1L}6Aq?7O zup~?w0h_eUw*5OlgElt778*T(c?YLJ+5vVI7<P^5I&#IwvLi5n%Agwb3ecA~1<Jpj z0@e&gh>M2YyQl(A6uGafHl}d+nHO=tkUa4}R-%sgLG~YRgy2!v1Lp3F4<xbV)H;Qo zjS-@t@P;L484A$1m@FW+>R+s7%8LO~`{BDBMN+YG5|R+<!u0&pc6|J-$rk>?m;(xr zg+&%^i!@E7X&QNW`a$dnOO^qcqMMKj-WhZGLNvcdos;n(%t?PhK-=Q?<|!j!D};V) zXjU;KhCV=U1kHTU<4H(+dmu(=>I(w+AZ}I&ak|(DtoMwYD`9b50f8dgCOe-^0|A?O zoP@l`L9oeAuu1fIb`Oyz{yWYg<*)|asai6D=F<r@uMCr=Ny1<C^FI8F@Yyqg=HU-y zY5sX#dA!{P+xh>>2b!-Z&<y`Xmgb+AcgNGT=d+eVXuh35lkkNs%|B1Kj;DD&e)td* z(E9?50D6o}zLBN*=W);RG<y#W?z=BQL1D5PUaUsV<J=$QXkuHEs;-gL3L1!S!RP;P z*NS+zDn3e*E4QhKn?K%r3X3+*caxkXD4_tRF$5leKY0;iD@c*|{hb=XBi)5a8Efof zwR3Vnw{-&D{FPIpOTu8^4yDT?{N9~FS5S0HbV(TP!tjIJ#eweq33Ri?r$m>8;hs6P zMPC}|Hcp_+A~hwtB#gK2l84bMpnGQm-96G%qD#Vn*F9~yz7HnE7ce=Mzt(`5+b`KE z(ba}zHxlgz35}Mk|NSerb$8Z_@yv%c>N>0-=(YR(+G)b6<DGD@OC$q$i|2?ZEda&? zpgKxE`wi0s)Pc<q4z7EUL8Q6vl>HgB*}3$0Tg@@hyolTC(?i5oVR}y8T?LRR2OI=? z#Mlg`MBjkm;O(LE=U@QVdQCr~1NN2`S@WrD=KJpg!|MP@{V`eKXI?~^$*kZY0(0;t zjHX<%1zpbc>ZB*29)mp$dZ&NLd@9J;RNE+v_8kHKZ^D-!zGI)f2s0ZJRMjC_N6PPo z(&fE+1nLO}M$tPs>CKao8=WkZ5%w_-7(gx~DZzwaImRUX1U_x2L>OEA;@2!5J+QDk zz??_(n~j@{?4Q|&aEwj3h9zn94&YaYdi-Xh-eEGrW3!4hWx5FB>W_4=T)`gYe;GqW z6Uk1KP{w9+Z8Aan(qiaFsyAM(S3D;n2I3QlQ1XJ6*g<BAM`jm(C#cjOCcZXmwCeXv zLRJfj=KpR&Od+4`ju%4zqdFc=@cuOQ8y|gS?>$*33qkW&w%_%)W59V4r;?|Mimff0 zU!M(*fVChE)K`pAA2q@?5Se185s-1z#t|LeNQMgRPt%1S0KsR#IhrVMHhH2X;)Hi^ z77D+D!>3mk<M(K?hshGf7T&5~cSRWR88X45ahJ%F)FFEM`I2%PbK=S!C1GC2fVH4E z-Yc(6kunK2Xw|Rd7lIsS2L!n2@GkfI)F@-?LfNY(tr7;F8;n5-We5u%Pl4|5G@{UG zKGECH)AzT5cw=L^l<s(O7j%9b<UsE-`P-(9g-!Y^UEYQgV1EF<XwvJu$&%ja=R3)m z1~0>>xWFQy%kaB{*qD;?B33_}D(T<*!BLO+_i?fDbDl#pcvmtU<P<>jL$m`QelbNH zY~942cAw`7<{l2JN9*PmtV3kxx~ZSN51ypGGlVOiN(R#BCy<VrH&xOk41R9-vR$ts zwu^!D8nilav{NLFtq$w!IM)rZp0Q0>fA_LYLKsP(z)@-tsi|KXwsEcrsNx!^0==v+ z;+zUu5^87;YB1FVZp9~Xdm7SYBnR&=w<LB+%J_#5J%C$Qa8zh+m3gK@mc%CM-pPfc zSK)BTU1<CY@QH5<WU&V<Xz-w^KJXd=ngzOlKY0;9VWUS<1xO;A#Pa$rXJEh#B)<%f z2%VGKvwAX$P|M)}^H&I23emS+#!u!-j=hWSfz#-R^f7uVT)l1z2-u1!ZitF@g|$^2 zro^(pYCz&8ZRN>`j@F4_lk|%@f;R#48UX#GXW1w1$w>b83K~S15ZHt-?2go>WrQNG z3B0E0ktK{xa?_5?Z}}j03w+VycUVqBa<b$GUP>Zs4v5|l#(;K>bynm^LXIEofMhVl z7-=XV4?DR{Ful;4W!XGM(%5QHcl)rN8F;1xOOQlaL5kZ{2;&_H?l?D6Zp@BKD(D29 z`%npkouQ}?dQ6=<Hs`|z`jQKP^IxECH0N(%gF$j4j}9Pc8woO;Ngqz{o`1mc3HYAe z<>NWmj+#1kY|h1kdQQ>+=YpVfIh0nsQl~(Bqqig88}Dce={BM>Db1%Vrf~SdRCEBh zfyTJKZ`v3nRue(x=MTLxG4^Fl8-v8s68W(pTbGG~q5-yaXy<B^JzWg!(a7xZ_CoUU zPr#1Q3IESUQ$Uz}7`&P-d%0i;qQH5fU4lUAl!;^Wo#d%mw-VOzZw$XXT#p%2Uc}Cm z<VYh+4l)tQCRa9Ie7Ea1P?m#$9&JW*E2j!#33Zj4P#lFLac-6wTAg4A1Av5m>&B#j zpLr32Rnx&RL5`;;S*I7Se{qr@rZf*Mp@Jx!_OoPguty-EiLX};2DbnLOMVnV#Tz6D zns@}5;z{U%+F;esUa*reFm<$#zkG)TIdk993MCCV)B^R6Jv2N%e4q}0M|&Ljqk%z0 zIw6gH@*>{0ks$n^g5a?fAbu<Xh~;vS(h|!YBd@PukgV)bi9Ln^Kl37n+h+nZ0a(?K z;0CU5;$R@?`+51#y#WDN!hl$y&^LAe6LiFkjc`c8CTvr&c`!~??iBzlW`-rt0L4Q! zoTOncNs}siG{r-3P#LXE#>2s-!QUsb#*cBub)C~<p!}nd0W(KIYruiqKZ4%qr0U^E zbo7}hJ(AptEAb8Z2J?6wI8qH13w;B#U|}*ycDlU$LQps#C|nN(sX9ChNVU-dW+QLN z<^^KQN)`L)dnYX6jWG4KP{1Z%X9Z03@r6ntY|s~b-vu~=v_2rMJ_>Z-$Sgp$@E*U5 zFiBfiX4B3m(qLVgU|nV?WQTckXKr06<jIZec1ml(8{nXLg2HL(X2HQ|s>vU#bPcg+ zX3LQXBN*jlV9}~5v90FM0+i$mU`*e}BMScOJFEie0j6c09o|N#-`0#LF?Sn0OTv)K z`Arjoi9-u#fQ)UwtqSRlN5MfLHjG25bLV36Ad^&4>U$vB6oLcfDWEpi2h#6CaQD>1 zdu+wRW+(CE!kz<Q&@7PWH$}0NzvLgYgUvuHwdsYeppR%6a`b|EN_bWb7~<g4ttu`E z?<0gndD!S)+5^<GFcJn6d}r9Ie}s+=EO!yV0y!#t2Vz^I^v<(-R=_53p5WmQp(ftP zSIE>8YPU`NNF{AL)`&`$!n9rv!3uh*=92wqOkm4zwzY9B9_HF}P(IQ>aO{&8A)!bf z`tPywpJ-^35q1vka+Crk+k-nn2k-sL(+9#9MxjgFz8>_rl6m|wv$y8-F-B7$lkFAi zUq*W02a%XzOQ<=fIQW?tkzqJPI4!um0i|saW06Qct*5U$y9f3SS0IUj-k&r=b7^v+ z)%fv3M8sAl58wS`=`b&RL7f^X*-pVR>~wLoMz7KkomFu@ct@;|9a-RRg<9Rb3PCd; zn4>WYveap|kYf)jjWU8QajmmwW;Z~e2QRFHg70*hE&L>~AMR*|jlkp!h9W{V>~i<n z!s_Gw@fd~hh_YMTf|qlE9W7rzzNjV6cb2f&>fIL0o?Qu}%MWoA+Mp<+rVlt4Dotuo z3%<VA7X#I1!%;KZt#ZS;_jGL-4XHzP#0MgU(Ad&8sf}dlLv(lyj(5?))5-YRL&Khr zWSD5;rGES}=(q~}EZUx&_s<p?)--1l0!t~Mc`YbJK@mV0Z%+x?vxLRguyo*-+K=F% zkVK6tYPz!2OdoJGjX9|?`L8j{lmz!6G9mnSx;iU768TKKket^W054?%-lyxc!XuH} ze80f+cm&vu7TAp;N}u~~&I(V>&BF(;@#m^A_E@H;_7tB4-vEwaTo4x7G%IY=v3hwZ zfrEXtCuB@WCZ;Mycp7XW{JaL~E@hMgitf#Z32c%3Lv}8c199g;+8yl<@vsO@*EfxB zFd$}_)PgMsGu~?fB=ZEwpS`n%Bw@tiPxq{}g-J?;C<*Nbi(bwaa!h7y{faOvj}$;$ zHcw={Kk|ArOJr<7{qKek;q;4Q6NsvdGM~V&(+4!cJHqpiuy_1DqNDB!8`mn$bnqCG zNQQ-69fiC8$82%25%tz*2P3smVszs(xY`W!$eQl|?mdj{8Kkgbw(nTdfjlW&4N&Mv z*L%T?U?j0|9OpWnM>=^}PY~xC%S>=cOa&L~xa0RgoZTP}I`7THITIKXZv5<)GX+R( zv;$0XG>kgeOkhY<L#Ui~E9L@gUkr*v!$|VX1cpQf`PxD2d?b%vI6-NLm&^o)L_vAA zbLMMXfGC4l0xeDV(wV@>V_VvfJ4YEuKpI%T$HAlv%>-r*Hq5(xZZ+_66nPLiprv`U zd?qk!v0*Ze_r4~IQ&4>NAAjU0wrU12*dTj^d08Gp94)zGymuSgGy{;)7#=C+BS1ia zjVSWC<;iLAqdYKB$|!%j0zVx@bcT@Bp0{e)&<M(hD9m9Vp`-ORPP0HW*{G)G=j~6j zf@AR4Go>v6i(I}&!(MXv7qBFJAm7|O!RNs-4MA~|7D~5~uKxfQdq5eCE6Uz*!ei2k z<I6yEd}j`djm2s7>dQ9J>uS)eCQ86p@zcVBtDR6!fhRfynIYFVgeECR$`ozT<N(Y_ zz(gBtRpM+gu_csKk6=M^^Mx>sswfFXQl^8Z;pInwJuFCwADt<r&H_d{5ATK|cM7EE z13FIT**6_LO>exHAEHlnq}1_G*BEROqjs*hsRDyJ1sC>_*Sg0(c@cyArvqvHyCW1f zh@(>Yo@XG_Yglqj#t`6VUc~pZ>42E}xx2%I({P7$>^kY6e}Rw@o}wmM43#1gId?UT zVIt&4(CL1Ilm8MQTl$Km65T-9XePn>fmU(<*%_c?gV^0tdie-UM++u~1xm%|u9F9G zK=sLxK)Z9KWX04I%FV`_iIMW}Z+oxl#KSSxWvCTIpH)&f{A(VtwZL)4NFBL=cOO<! zw4u#?I7<%xv_&w$`{EoyA=voGG+5ZZVH8-vX`q)H<+fSk1F(ySBR1^m9U4EIVbH|@ z8V$?cF-zFr74O)vIr!tt6<}Hqg2vIucxF$g4Ql{b1qq{uhjFGzV%J1nx8e%&CdMu> zICK)7qU)bBfGvOfCKozO80mg+Yk1V0yZ!o~!lUQoWJ407?y&bFumV?P$CYIi49tS1 z{~T+Q7>a7HnAKsR2M0LPMbEt3U>@XBJmdV&|7_tuz=o<8qgC73W8s-DmS|1~6=4g< zPl0ov{s}%dY{FYVuPZPG8X*ya^t&AU<VE;XQ2h(Br#|$(hM%{#!muN|A@l*j>AACD z1$DGbV({N<nD3APEyRH%Ktq3|`d82-qU<c8*1;ZFb8dnQ&>Pb}{LG6;f?LHiw!8mI zjXm<#62?l%$T!2t+oFKm+5Zvn)W_b##sBns828IC1JqH>EP@)?8Ee5B@9klNEq+_# zCxh!Cx()<3RwzUdp4lQ!O>U%TtL-{sUO1p<z*hynW1qZ;5`k%RF|iBpe@p!kxL-%- z-iHJL#cuen9>ap4c@c4f<l!c#kDOB;XVxl18ekwb3fd_8MndH2k2Pie6N*nL;v^Ne z3A{KeA_}OP6Hu=%{|BffCN3WZZ8R&OdQCtzheA?vCQDXU5*b6$j=9;gfNBSx3cY8& zvugIJ*i)tI97T8#IOYq0tBM*`C-G@xPN9Ry1NRlYaMO^Y2sFD9g||U+c6g&lqELwo zota1(6FiC6up0!e0R^MM(^mgGa1u6k*7m8b2!JC6lW6eWvi}a8glWAa`frzmETk6e z_(gE(ntuk4ZDCf|*OxZJXs3buN3XxN8)gS>fphc2{~mE{XvUijG$+BxUcgrwWo+M| zK!2LkSlz=NM<ANQW56WxmGZAm3t@SoI0b^Lq0s0wW`kye3&0j{3B7+Y6o673fY4S4 z1sJ0{E5NZMFKpOm4t}ToFoiS0n4vl3-w!l>F_<<i6c_tILqBY!BE`#1d7w>WsK!H| z<PC0|DUzCpo1Z&zqc;KiW#dU~S%Xg1I2nL+aN0aBJNg|C6{l%8e<Xw}9+QJ!A8fI4 zhGf?c+3}}rWt*oDigaVcR%(?~D<>yd%tbh1N1p_{`OFLltx|mtQhoi*o-d~T3@5?4 zOW|<#uh}3G%G;oSfo$kOG$#^}hRaBj*29xT;hwo#d4d!q+{z*0hTfQ4$Nmd8prqP? zfK(e}>wk`B_Ek}E#kZg|5}k<pmi#Z^O(Fb%dw?3=-Xt&n7@sOj1V@zu7J&8}85uL< z;6KK#qM>GrE%?i{NAJ%AYzLf?8={6k;n3`1-R+@!F3Dm|txt}xTL4=el#b2@RpiYY z8xq;j+n-4I-4_~L_>&{IOXq?sV**!(-U@y{Hfsh<O*Ks6or=*!#ecU2NZ78?Dwe$& z_Mp|!^#N_|&K0u<p48TmU5UFz978A!b_#sawF9ebrVdYjchcvf8M^`Ws|RArp^S^B zmK4rd!(fTAn$$^J2qUVbxsqes4*ttWaC~#$tea#pkVLgQ-V+biibP)xb#)^$U~LwS zhYMg0B7m!g72)p(`jkE*3#O^BVygWYfK)iVKVN1ZQn+^$yb)Swx1cLC_A>WZXX7DA z)O2W3wp_^v3|9~XjluqO+887<JZvq=Lydre6b+y;n4V1=gGBV+cQ)DsZc|a*nV>6) zzG-8S2+|jKeQs|8jAIkz2!A<k3=-uc9Oe!B$Z1#G1dQA_)5aiCD^jufMAixjop@l8 zMUThhfoWrqC=_{0OkPj|a(GUV!)$ok7$mAhBGQ&SF#v`tEF5S#Og~N=gG7l)WlK|3 zIbgU?kmJ<nX=9M64>=?>_Xb><rq~9yi*_Xy-=>Q}VtTA_Z%rr$2)7AhTpF1+1PNDC zdZ5z`*>ilFfN`FJdYbNPH3?VZ-1F=vw%0A1J6#MCvwZjA_P5Ynh(Zyp3OyP}sizA; zVv^7Mwd*<b5uk7eO`#!v&`uYE#2n9>(|w5p=6L4>oBGZ$T?i6Wd=0_>CL1P%!-DBT zkeJ~MiMpB4lY-*i1VuHlP8Wj21P>RDFG1cpD3}0I!7*J367&1^lUv*y;iR!oV0<IL zJ6zL*z>Xoe4qQq^5|iPOx<c2nxeHDcK*!yaWF<r@%`43=NG#aFprc{FE}IDq3178; zO-q+Ago)J3<4YbSg=YYRtuiNOzq?4zS`9*PbX~<)<*9*8A(<-Bl$eKuaft_4g5K4< zP@fuzkqg`dLm>%`{?nglT6}|yU>^<dmXX*>|4cQLum=XZ!MM?>ptD*tWdK{B^;}AO zUjYg)Y#!048-BV|LxPShaL3M>gd|UGgAVb6B>G_D=<zByCQBb_$N=|@$p+k#RLQlR zkb#vE9q*TynoS!7TTjg@IMah5$2{=M>VG#4NRDN<)3hO=FQ4b%2LGgX5mYiq&O3tE z*uc_NQHZgycOkQj7_T`LBDREEU#Idw+b{|^K^^BM^oeBg^mVkf%?x!6waAWDf{Awy zC5WWUJKiAPK+H)-tcft#vb@Y(Oot>I;q-w5ywTrJUWCf_se_n#c&d8@5Xp+Pre`n+ zsyiv{;8+yhhVv~{M@&_u@n{nzL-zd_hv~2SgFLbDmHI0eVuz=r$YPP-?z^hEy2rp) zynfmEnG_gJf;^;X{yx75jlR_W_t%LMAiMpWO-%=mz}V!%*r=c+FixEU#DD0U`UXMa zs6LcCj{XT7Zp29GR~-yaJ2*JB12Wn-12}9DSA&uFjUWp&)gK2!$eIGk-)AQOTq45; z5sT<&`~U#RL9rrAaeK070D>e=Nco;^IeQN51SlDRLZ7L%ADk)FAK4Jt$rnE*OADzc z*y5gssvh)({X2(e2#&2#3ybfP!eEI=TQ9UiKNigZ2r)!b3Vp+jYdZuoE#eq2Q((yq zp|E8t!R;Ry0plo!(L?(pkBS)p8R7%<2}BadNYoipCM|#i7v08r*&AnOfMtYp#;bbc zajw{su|M+qc?d@90{D#$DD(eOH3KwQ5q$~1r0z&Kr1ly90MX7(m}{TmaV<=j{$D>h zenOkUD@LSDBP!@Hr4z`d1#)Si<TAWCQz!&)5}!DDNN$-582u&K+@NRJ!kQUEVvkvY zcARf5s1dpLinhj+bu$4%28@&*W30|R6a}395TT)C=EmzY#ln`#SD{7d9Y{3~RH%*` zF|(U9K(atqOb48sDJ;(zGYwhwjUA|bLIEiazG&GBZ%+qt@&EuqvZ#D|9f>dE;42N^ zu}@w^*qtdLO%8x8{~M_K`y0UA_kp>i4=ywsrvL!WX8ydKYzey#=yK?d;VuwCN<;QM zMO3yIzG&y}bZ=TX<V7kmzY)9*EYBH4LhqrJ?vsN0{bsW!&c_#SY^i(r!4((-9Emrw zv?^b0Dh4rn;R`#Qzn{E_FApcD|GO)R6Un~U1No=-B6syFz!6CPrHe$9Z(62Ed6LE- zI(1a(NSQPp91e=0sQ-dAIkpr1t(P&rvF9I+&zqG)!1Z~U>EbB5GS4QbYv$+ahPU*_ zdBWZso3_!mRAzV*h~hPT(Hil5J~{2*WF1vKJlsgO4fK83<M|Uf-Zp{y;EO3yN3Qol z8{LV#BPOp%bIaBbsPBX?S}zK)#h*;y<ZXNt?&-}E(6<ka<+#<CB<-=+#8u!CELhu3 z@C4+H?(dBf6L_R|q9BNma4IQHd<llL5WvtAYThs@LB_z~Iyu2XRiEGrxdT!K6MS?v z3_pRsC(uV9K&<~tj=nl_Dh&-EptuI^=lkOvNF+YQ_6-liNdv_z*msDbj5LvghQwfu z$)FCU4nD}mF>Lj0w&9by2y0IxY@^Za-=!u?8tL2yFAM3bL(ejAACd?AdcP#7UITpx z-tpOhZU%A~a0*Lsa)PGGFxWV^NwvgxG`p1_%qjpz0-aG8;35k{bisj+9LWqeqmhUn zQdYGRJcc%EAolZ-hZwDQ`Jb{zuPH(W%-<HUq@s~+^&-0>0F`|LYAVQ!Jr~D@|4)_8 zWT+%kuoX{;2k(N6{(!89k~K*1AE4?O`4F&8($!`Qhdhj9HslU8QNX8!W)F;w8N;b( zUIv(KkoiN~jsesGO;g3#5R6A$2YkU%NJ2#qI#4!{Aq#<&D<gL?5hFDl?GZ!TBUO$B zU8V<983K=i4)7K!kOc~a-h1$L@}G&O_S=j|l}ROkj)|;*f(}{OiK?PTDolwS(pYmf zBwC%SWG9CFR>2Es6!0>|ILT7A$>AY~8dH}{Fy&-RJ?O9&q(gh$wYp@Hhz@Rkq-)D6 zIrZOP1&_-B89wyNs%J=!wBFyGKemlg_Ze>;00luqy>U^JwJBNJ5HphUKw(<tjrE}P zRxl}apq*?!CAw;OH#bvwQU0&5!{$GMP;o387P*rG;~#`OZZ#DgY^pe!?9Vm8y&G_k z)_9OJIjaA2oY=G%Z5oP>1S8Rfnlbd&U?&{qPwqJWyaq5;>pz>DVeJWdA5O?+twGH; zd+0kjIn@8&0EX&I<Z3s`c!p{hjs`iadJEoH28ALVN*3jJ5JSdmr1Z?6YXJyVfPi*{ zR8iy+JmBea-0vzrUD&<BrEPGF*kg0lcM*#ec-(Xd2-HxbRK(5@$k0Q@*B9qNBG%lW zF?84zM(7jgc*!)ArUeK|O&`32AIa`p$>$?He!_BP09J>VY&d;}K%^x@wgV>(q_rRq zN5KY*7uxoWkB|d20ZAY_^vA1_n@sl)S@CtiL|qA@p;0y$l0|_Xz-arYzt%^}nYjh- z$;|)<i6nQ>A#dWb>7!tq-o1lCg7F|p3fMVXlHhW37^XN_s-OojH2?f7%S04pROWu+ zsGAE*2`xAv^jx($IW-h=-r&>mVyc_4pnL#X(5~~u8FC1d#VH8)`%ddBAY05`@RdYO z&+Ap>Xp`S$`rHsI$^ouDK|knFeRnkp>W-cg8iAf3#NT@j1H1$52?##!Lci~F8v420 z<Gn}w`jc*p*Wb#WYzAZQ0!a|`ZZick446Ke1S<B3<5FE{;f*7T1i(O#xNI$H`hVX( zP{X-7{CNu=Yjz-`l3sMD1RJ6A>NF8BHWmni&H^#tXz-HrU>!s2XxH`WAz<t1lPRTv z7uZt-e9@^Qn<moqC$}hsZ|=qg8Sum+MIiVN4V1mI-z7m8ALxJ|egD=+0%@q|hqN8> zApY&E1UCT2{s(MP)?F>Ujx_mA0!Vb+rFfq_qzT@Q@Mo!<1P4iy%v;&483H)rU|Q&m zj8*d#a6H`H>|s$ffu=D8<XXezLj9^cI@-C|0fa&)@+DiygP9ZYfzV3??gfqBo*;vz z<Dax40LE%Je9^YH@ez5f$!!8rTvMsck8<$7Tb7Jph&dmVpznmdLlWTO?J5C}!8*7~ zn0e!fKFEcbQQJ`QM7AlU9n4A5fe-LT5XDwlj8#!axU6SJfNBU6q_1OgYbf4x06ar* zIJzi2x#zROGj)Jw@1$GYU)-lxLkYpgPgoh0QMi<^W`jF%o*~27`!HYu(pU_-q>a1w zV)tb>Xh@$S$i+Ljz_}-u!$q)*<T{D=F!w}YI#{A4ee>;~VUHTwB(EF)RH%Cz0}7x7 zv$8=kaplMV0TZOhU4bxs5(?=lL}7EFk7A@2UW>zAKqsG2f9(6eBY|xLPik_8k&`%8 zumQ9WWTU2?3G!rZR_LwusS^Qg(FDhiqn$1o-Vv#Tz!pm84s+!)7*_`n3LS@P(N7zP z<elxu+qOSK-j``wIzFPl!Z>{tY)L*VI=6U3#2yNUp^h?)<D65(aD<)WSTi#TWY<0R z7i`3VeVRv9VLrIP_Avs57`0@!h!cA&{Fk2z|8*+QNVz4(G;RWRy@1_t6uXYg{vWf8 zdK?H_x!b~O&5dE6pM@+~2#Q-9(f`NXj)r3-JH%Jeysz|w83YAh;~3R|j&M_QNF?oU z!Z-^}Y6S*(M*@yWY90A<)mQK)FpAt;MQhX)UXcI)gHeq5A6f7oUEk?p0mnhfizz6A zP(xq2=KmK)u`RSMuFQKeY*Xmi;7AF@X0H1GYc@%~oFMkaN~{g0kui8he-x9Z2LF$l zB>6O!2mRK2yI^$?;vWCLT`eTb{;^?B97|;DmY^4A0<YFmK!TmZ8KtK4TV~6`*kkUf zxjXiczpF8@nbN&twgM?=Lf#QTGj-hV|CA}HvWIr5*i0>F7+$yx3`z^K@aXvKqy4{P zY9cb?7V7zvsa;^corS0$z0-(>f1bXXfLmAb_QpXA)6v%O(DoA>xA0zSp&i%;wBs08 zpK9jyZ*Y+_SPXE)FjoiCu||VIX2Ta9)$sey1f2YiHN%aGk9iTauz=GFWz<xl@tY!| z^XU6TPC*jdI3JhE%WLcpi?AYDxMEm64N+)*NwY!w?>b3pLCj_u+@u3@>m#f+XzZxe ze}+wBjUxofc^?M3GeK?#l-z65{~7k!A*~0|adc}p+SZmtoreeeHI{F{hz~He1;vn6 z*8dqp*b^>tmBsu!FeV+4GeNtUJK6t=6>Q{ZVrw2C7jIU;RM$W$VP4T}kSCeor>c5t z2mn_Po=pV}SuzvI(FWJZ+rbkh9L-U(`OCsB0SaSq-)Mhw`^0qNkWRIe&u|2B5ygJd zGFZisCIvbu0dG{z7I6~22HqOH771|a@Kr{^cAT9qEP1ENbm;+i4G0O3LX0asxNvTY zXfCkd^za6s`THj+r&YqM8>j=StOA=s7jLv&m?07g7bUFQ(`gP!&|7TWdK+|khDfF$ zAr$9Lev9u#(Q`^&SmT$j7@yOYyD~G}iBkcICyt7DEpmhO%4P^X(M?qz-J2$kfr+^q zB=PV<@CUfU;EH!d?q`g}c-Zi6O@8)BHmw0fRX`c&>4!6f#|Ap7e98f78GQ@J5<L@` zI%Wt&V%{NIRY<A6fV|3qJY}Md66?v6*`Sebi;XxL`its$5rDQIzUbVXa@RCbC!g#H z*pOi2%3uIWgs(h&$3A%xOS-2BHaQ5Q$BH`)2GTHMX`n~+BG^Aw5V$M*```fY{kJ>L z-@^pb8U0Rqg-tI2IRf@V)?>24Pv}9~J4NxxA&<p`YVhPZ=8Nly2Fm6ZB;|wjTOky1 zLh<qH#s3W-{|y?k=aRoQ#Ra5;%q`H(HWW{+{r@*Snc^KFe1px&q`Hbfw~_S<=&A^G zh2G|;5B<O4i@fey3&l4c10HpNM_ZKc`ak|-9?3HkVc1nP0!$$LYcmux?BD+pGdg2d zj%`Y|RJTG>A-Q>M>G<U}>c_v~U?MK!z2vU#O{#=pxDLahj*>i-j&5q(=>Jad|2Py$ zjD9Q#VuFn@(@Q|~1ZmNT`{vIQ5hHX#5c}u8`GIutTr}Y<;3?ZI5iuIVAcz+HVg1lJ zlcH_{;uh{%B4YH1K@g4WwZ2CIB5^__&&W4RM2vPZ2x9S*^=YMm$ORD#dK}*g%@Pr# zdklh@zSm*S13<ho0r8i}ED<r9$smXW)vK(LsLTxxNzgLNNX-%vqqhu#s2g0;FAj*D zEaMT=<!6bw7TW`8y^e}O$^&X9Ag)lF9ikLQ*C#|qlcDAZ@qp+K)$?erzEGbfA_<=p zS+dZt77!OfavqJ?tT#(U5<ch966*zUVThuB0^(<rSt63~IWktCbCyFO;tNHdXc_h3 z^~EVAL`isZDH1*>SV!T41t9W6JwF=p0dAIvBz(@!mG-Q0-~sZ$uWF!Ht9Zif5LMhr zq)39*g5O30R2F>IP>_KhGk_!?>W^@6Q#=8!%E5dTM*-S+k_B{xqkRd|=lh&Kx9+`$ zbyrqs{D?&dPM!AHIpk<<+TT*7iQWYX+Zn!8u)Ky4`4o&AdUtep=hP7ma70KHkSr(& zwih;e1)*CQ?BCH@kOjME0Afb^>G6G6ZhO!|B2o*E9MBhkOyZ}FG*M%ScjOG&73~Ou z7(qma&IFVdPa6VS4fx<m)VnWHwfX;AJM*|GtM8Aa=0+r0xoa-D<pxSdN-3M@AX}nf zxeho6IxslHh~O5kXzr%Dlv@a{p`~f!R%nX3q^aeWsi|3--(Q+*>hE)gMbA9<ndf;1 zc)jM^_aD#up5<=mp66ayKXjyCb?u8ohpg8E!tu}uu`18q={o%6b1p712)T|dlpk%= zN^qn}m6d^0e2}-9fC+_{xwhP`lB00y6Ma1Gqjhk|{Ob03*{%8N2nChE#`rW_-Coh> zOfu&@<#u#>ozA+a<M$>rT;2(FIOTEX+KaufeTh!90kIa&7_&|)GK~6UTpVEVD8ByI z&vt6on0ijZ|HTbt^!=Gk>`i#9?vxXN)7@NQ44(5#DZq7!^o6O+*+!JliZT^2B-O%@ z#7B$&opzQjjei>a4z<jQ+yd>~peg^_C*quHQh3Y|)UOt059JcUnH_E~bb%*V;^3eM zGic1O&Z0QE=zvtLs+;$BSL0yRdoU_@=&=i>!w)fu4x!1+A@3R2{|I&#h5$ZdX8rCY zTdd@kQD2*TV=FMWrTutz<f>{=Bwj0^vTm6>H%OsJHFw{>f|!{IhjM3*z2_XK$a#&6 z{*S|(2!?EL5i|{fw7i~u)$oZ2CF4u&N>kp1Men?fGgg;Z>)4lge_PH~Y3W)t6fwKX z2g{nUuBpphU0l|I#e*fsO*N}H&0!nN#Y%aczWFJA1GN^DQUL3bt7<q2Dsr9_NKunC za@xuoh?qXGB+u0DeAP*y?OrLatnDbHuvS9cj_U50KmH2cF$T&<F~vh0m2Nxay*w}S z^O$*P14=UNP5_ddl%Ne#g(-Z4GFz^>1ZDlOV&}&VgPS`G6ApF+8*?V5%t%;u7JmFj z)hrLyFvqI{!pv?~Ga?0u4E);SSM-;eKEhKGqbz>f@D@2a7~#qwUOkFr<G-2do!ZKX zk2iS)L`Kox##W<ku_lP-LTdZ*_?P17qVjDRs#`PR9lcZw7rT04gNvcwnBv-C1Zl-Y zFZWRttxLowJ#vk{9=b4;N<c1QMa2Eu$WNu@5Pe!|P`nYho#+S2UDWdE&X(*rEXTt! zg|%P`)C^RI5gX=q%=%uzFv<lCs}NoMmzlmlLQ!tGH5rx>3!b}f(`EW9*MPJJkCZ-r z911R8V%y9v6;4Cu=r8|o{x}%DHC)=fxXl!Xr}PemhZlUNC6;<)L2mC?u!CQMTx>HY zb@?ENQX^9nll3xtsc!T8{=OK(_Tbl!34JhBUZ@WD7?{k-1<(2D@KD7F(E~};X=W~1 z6_*LQzv1V}<X<+blplsKl?2Z(k@N{2ul=|hQvbw{uepCtP$4~WFkXVeUB6g1MaL({ zb#oBlO=nh{>4%ZU4Q_4B^f;a9fD|q+lA$2+`rZAf1_+S1&~12V_e*jh(PntRLHZ=& zktP{sWzTy77Z4+PSZ1|hP5s`gQZPnDX$+F=ZLiHqy!<>ahOKGu&RY{y%BFASWRhYv zMs~Ysg7lZL9p;^~>1>tKg9H5oq)jJ#2W7vzVGX=@7QLPiQ5Qc|5?&DKQn_T3w$+#9 z(Yp-Ou}$F9!ttf*<jPt*KfLFT9Ehd|@whz>txzZ0fwA56tGn*PJXr?u$EV`_mFk2$ z?D&<^*BzM+@f+Yz9)&-zRwrKGmCaJ_T)K|Vco6b=(>JYGmi<hx7kl*Msb^=UVWOXe zB{t9d+|1D=Ue>YRahbP*;n;fU|6N!Y&E2YrVDoQd+yBxOO<%NG#7gD6xZj;Ys~4;$ zdG04JS36S7sQUI7{R7~a@lcJYF@yJLVvsIkM3wCk*XRSR{_ggO-}Y)kh@*=-^p@6H zD~7=->G<)1V&x%ugyY324M5Q==6p&Y6>i1)kw<O+!!mLc49J4Udgv01)8XPb^QhT# zyxJY5FRu>4GW;XEdB-)04>Szc;}v_c{hKs6k`WFU({mesEOG<SI76X%*B=Esf(f2% zw&0sz>%D_$!UJS>sosCiYQiaSj``{)(d`oSpc41_<nxX>C>eu`M9Xh#|GJ9b&J2gC z-a*LnNj~qQI=O<wP7?fF)icc^go68AQ72e%rb&YL9x@MfgJ8N%khl2uHFbgoN0%hn z*XP3@|AC2dAFrKY-3@hu1t*jw_}JF}O`$JX>6DWDZo>am36^Q_r$e^>T?ukiByz9Z zQYBZWy>;?`jH736`(ak+?S1pED!Fe+yC&ITbcF-&b;dR?2{SI28}d+<T*2um$!#<* z+xEOQJ~XtG`_mIuas_o4B=^kKPE}7JAgD@!w|82(=f&x{!tt=M&k3q4NbbIvq`sSA z-{BJbuCAa;uAqj3<ks5Rec@5GcaWsLD_vE|6;w))+^7zBbU|qEUdZLgSvy})CAa9{ z)DUFFJ($?ch~vX&$@siE`1-9&Z~I+o->PajQmHiU{8MqqX53ETV#bbur!L-TsYa^& zE1<L9Pi;?^5PKu;_-bOfmm10Ju^MsX|C~~3&!=eov>Nsy=;f^j5g00#dr*J-Ag-iy zIS09X@mr&_I=Q$hz$})%yT#;z*I<?{Shw>{!PX#EvgM|Cip4KGF8Q}F#*h$K`^&Qb z4pt}J5^IjPWiiC9-}@->>S+v6qZ-(^esQ=O02PG`-|MAV+%PKli+M16J;Wk6`*(fR z2=8Wy{~%6g;d9XUc>ZWa;sfkW`ReWSST(Z4LLD}J)~^2RMq<Dki$oW{rQBpxC*Hwh z!epBRee1Ou3fugGkmT+68LkGPAb!uW&COA`;^Kx=e{TAJN2?J}H)F)dien~vT={?2 zBPc5);&~2f!(=tGqit@L%OO?1pSs10mFz_Pc+1yLb1YqX$#3ocW5pD-+>Dm<6uxn~ zW7)&o6<5>iLPD_|j}MGbFeK82o_d+=Nva{U8C4bPtJvRI>pQ0eupWj%R@_M}c1^9e zbK28JHXKUwjqJ(=s+k4TxxJF2B{c=|zODLkuVDnafu0h;b|5_#mk=q|@y+nn$sG{d z9`)@XBR`o{LSTd`*<ia;+KA`VP=imGbBRn%Lv!~D6W*SWZO}CA?*f_LlaON(7UC5y zm!65G0#irg!)v2KF`xp|=Kio5j_nBLd2V>j*NVgdy?IbP-HVwVFG@*}MoUL_xws#i z)Wv?1_v`Vwit+<Z3HtcN$T+-UE_Pc=ou%1LP=~amq5Xb%;dU+3aXM-+3oquNXq;GO zk>w_a)f{Z<c?SBG16nAd^!5Yp7RT+hGF|P-IJX-u;SD!<gP-K|JEnz%{;YSfN*r_i zVGPZ4?NDLL(+KBJYQxaAZ*pmz?B~4umB%9HZ3HwQH@-ZtNRbYRN;lJxr>?j7xWX+A zRpo)?{ipUNCy)xa%MfvN_a00C0P!64A=YC}SDYY*%AMit-|+45vWSX>aG4L|<Xm%t zgNr5wH_MVjM}l_W?*gxlg9*7puD6^3MOqWYUH7A-KMkOZ0NcZGzD%L)J5F#EazA}% z=e1Z5jVhuG^Bv5G_f*hqSI9~ApaCv6c|m{L)g6Uo-j`AytC6fcOcSMN;=Sc#VXbBG zj32Ysobql;6N4^9G8s597X`u5rF*N=4RzmOi_nbWk}4|W!ljlXq2+e%ZRLs(-ib3S zzWlA?rYN*<{SfGA`M&RlPi1#3-P6!PI3T{7YCu@1eEN01ey#UUFbmZ|>zG;cTWSQT zT@{ewZl1{at2#Upg4Gbe#<E;ntyo7Z5Mp(1I`;g;syK|t{(*<H)kgzMVJA_?Gx#Ds zZ}<lc$qy<bLe*1xIoxfXo4RntBy^X02)PjEthgT98Cv{x*p%0?cQw2f>22Rxdq+7z zR&v?Mm)!U7gE0@mnA~OVMh!TH&Fl4HSF-4G*!D1I7&B+9Va`yr7__l*)f*V*T+*=2 z3t;SKR;SoXCOtgGnnq8}&BY$Xp8=<*YXXMRZL78gpTk&v{miB43>bGjjN6;>+Ds}L zPm7tiZ>U`s-`}}R#sD44TG48130t9L@U9#Fe7phkvkQhEJ|N`IRgG2{bW-XXlV`%A zBVMzg9(FBM&2W^%IJKo>=C2s1ziMEgep$LwB_J|ToGYcFc}%&>6(3@H8-_gyUjiQ7 zpjv!@QE#ynKV~JKsF(Rqd~+0{{}j3$2Q}EQ8iX&W@wQ}Q;jPeO(BgjQS1kf0s<HjZ z_3uH|ps-NCgc#KL2p@$ayY-*l?D!^7Oik=d=`TY=g-vzgW;!q|R7oLPrq%DZJ+cS( z?)6&Pe~NwkiUw5SSYf-EU)8yAPTaw<3$P0bKo{!9dhQo@G;q5YJe%+VJ>u2a@vC!W z*m6_v-bjPCk6YTu$-4VaQl)W1X3d+m$=!<ebq;djd;v7J@(b!c7=iZ+<1KLpbA&$C zBq|ytmbHCtb3K@{D>?u#42i%;;0n?5E*?)p93p7+^f{eW8!}tM;pqb-G1za%u7PK1 zchyxS*t~*70`7>BIXgHKHM_xlgu4YxVA!kpFEd@=KvDdF0R;gcB{(mowr97Sa47{M zGw|bW4{WU{+;J|I#8>}KpPdNNWANkq4%ap^;+2K&YFTk1-KPH#Hs^b%9dbg=Pw%rw zVMal2=xP3;;HpKr+-2^zw#(>cq&E>jeC=@2Q$`1MI6;*F*=%BVo8R|=_ZaGNo~Kud zc!J^q;*A}2^9b>pOZ1CwUm~7N$bEO<;qE=)H3iQPaZ&V_nLft5Bs>{h{U72?q~3p$ zXj$H&Bsf8#09n(sTr<O7p?3RxN`e#A4-oFZ#aW%iec`WnN`e!V6%fv1to`zSX!l5B z%olx2f)i955YFrTrG1-$J1fC?`<DbKC_*6ITOCfUAnl|BOZUK%-~_b^giFfZ@glvD z(@fG4%t0l=2}%|Sx7pIB8D;K+fb3P&>G0po^u^sug7e3TfjKoCZ`s<4vK`%9oApu5 zseB-*-moWY!bx38n$Syi3r<~~_(?pLICoyRKisR@y#y@~FATgJx7Brms(10@Bl*wo zI|ZYfQ3vPr*N4q-PA_~UBA|Hjg|)8+o+A$~?>%dOIuP5@Bl%V$b$|*)@pD4956SSk zp0MzRqIJg4&2G@bWGa3<_ZJzX0;If(XzMj&T_y~Lw@+NKktqJlOs^9w2f#yN9$obb z2{H@8CVp?Ms)P;c<eK)U(q-cuiR`9}@bfGBNLr>8_`=B6=T5=sf!L37-$q#!W$OxG zJT{mM>T9IoN-O0VV|JWOfJI(F${?`V9yCCVOVNO^EoK|rbb87a6K5~!X^22HJqDHU z1Ku5_nP}t6KMyjn>}#WHgF~Nd@qNIfF<MztZ75`iJ{kMZuW(Tm_5u8`FMhltp>PX; zHv(->OBm<_8ZqYV{Vp%~!e||#OfYlPlF6DeQF^1*P~fbh7JMMX9q?dO_7;p}6QE*u z#toWU3futM{r=Uj%f7G;%Dx28aq!@2r2xifgfbd`eaUM`J~Tc8jYF8mQ)ZU}xJ#&B z?lRtu3-fE|f=rJMMlkaBxurl3iZ@%5<s;19_tt|*L|6`t&JBKfUMbLZ{(fQN(5}1p zdiW1e`xmIKV`?v7P#WL}F}Ba&uA6oM`!TTL4ExifQox4j9T__-s)>&d@F_4j@6L}t zEd_XFq>QbrJ%4Nyb!SRb^WgqsNojED{VK7!KWaU>y(_eBh4A8O=_AWZg)IL@!Qa^d zuC({egVNmCfuENGx>t`7vC20GJPFzb?mP)MX=N#JqjWM(-g{uKJB=YXF(q+*Kgliy z?)y5K9`)y{$G`N4i}5O-UD3fOYn|hY?Rw+jE^7yHh9Q(%vz{CJg(FTV?xrzD#3v_+ zAH|{BJneR+@$F!?Z;*Q9qK|G+D_SNcajJ9QHb#g(A`u;imyLxfNi!`ypKY~F`eD(s z>Obs+F-)ii;;k;fQ>|#3RKux8k!L@H*u6LjZOLq1B~PVju@n8DxORROiSCV9ceY@H zZ~veuI5Ny%E^)WM+{q@M2>yOBHJAJTQB`tf+}ClWWke`sMqnS=oVEAZaW!(qRtSme z(t@sZt_dq}^BT@5N_}6Ss!vXn3Z`ZKt9@g=X1Ssa5(!wolk)q`k>DN(xxz=+4gS`6 zqwpR90zB6)WPlYduYwv@UuKU+e`y5Ra<gVD_PD|-G@|)M)SpiUT3PS_-w6GCQ;BBl zK*dXJizsjiku!Gdg=GlH+fb-0Q^@m{Rs>EEEb&kGL1a|1n`TeBJ3jAu+?ph*rfGj) zBUB978SOD*HM+ws7>;j%bmgl!7<H)0Hp2y&EXn?O{~gb$O9?b&8n?R+L)Eg&4AhhJ z!iB)fstC4CW2Antp_|?uV=_xA<eikt4VQk80LX-gxE`&lsiV+43LoGtRXP7JnK{<W z4|-g|;ViGA4Xmk+AlBgP&#qc4pu@C6SLZ$Q#A~YLN0^cd-(nU69*2cquLBcB1Hwz# zTQ%1Rc>2t@oV6CXo(=vSuuh1pXx3XYTR0DUs^ZfY*rHIZ@u@RuF&QxDV|*+14r|2r z)=oD<jN0gR>_%s-<@!Kpejc~fTNSF{m3Fc4BNaEF>5oNIISf7Ab1ge)5Keto`Kt4I z?N<#AMu^}8dApu;#aAOtkhP+|deQH}2+B7PfvvhQw~Xtefq<gZ1mu_-2b-*@K){)A zvivw&W=*%QtAX4GWB^09#LHyzi>u;cuE0G-$WXm`r~wCt^1xsJY8}!=sAGGb6Tk?w z$zq7L;yo03<kY_o`DH=B_b>$dGab(bJAounp}yj%9J%R2rFXE}IgRBGujaWFs+}ih zl&@?|dJa0dARu}6EV-Ws#?#S4jCt98`mcY&TRy<}G1DCxpaBz&^-wInmcS)?i3YR$ zC9z#^>POQnx@X`iUxuq0;|wmupdTzx+uJ9kN<S#K3Pk>4!{R~C(Bw&8v3IY=AR<;Z zvA@f=$6zN&QeV+}A~d7tBMXc+9=}%1c$J4cf+#2d?_SO8LpLIX!BBj<_}-ypv%!KP zDAr){uv$EL+P+t$Sc!&-^DJp-{4D%<Tqliiqy$4b(g?He;CF+bF%RASS@`#&%MCxj z)G9863?32P%anZuS17t*3upfiFxfow4`K@bfe|l{V;L5oY+?Vlw;c@2)o1?g49UMW z+3qa07>u~N)MR!j3LBJXMCB`qqKcekx;(|9(1t}aUC0<!(rm_fMmM|mnUWlfYsAEb zDv87Op2laISIK7j&#LY|R|(PA9?{3+db*N4onI`X&mvkIlT$a&x}I?aEu=Y@OFlnR zL2+Gx-e`=`#|pM5BzfDpW2foOodbxV*NbgHheP*9DM-elpG8#ML}K5Xn6mUH;&nX4 z@?G!m=?Y>a3qwlU?I=XIRa-QI-d#Hi;e3}+caloMg_~Z{x|awmDjR0p0U;Gu;TzF5 z8CnT~QXoRS;ZdQ<WgzINha*y^YDEaDe+c2Z;@;UmfM_B?<V@3w5ade;@nPRe$>a*! zd~z-RoS_vVNP7{YUU=`XegncwqQ#?GS`mWu7a`g;>gsz1h)xoO=UlA_L3Irwd`4Co zOTKtrf{34|6(OjoA;j6!I}XyzM(ZWM$oxbrLQq9Ri1eeeD=z}!Ezx4@Lahiv<qRQo zWxn~4d?D=@Hx_F}2&!cWk=o^OE+INV3qF1{#a)h=PvxhOqJgPs{E+Du7nat(K?G^f z>%UAZL1z4zyQ#t)anJi?g;s>j_z_vNbFK5xLfZ4zWNJmo7-~(M_6al!(vqEzA7`_) zB4oyo!Qa-ZM82S!M2>i6wN`}8_>o?>#Sl6IS|jOsp=-4wWX6x5_9WD&QSc3k76aC6 zMaYaFyF8+^=m71QM2k5&S`jkiM~#y+^T`*DB|UHTMvVv=Z(Djk3ZOx76_nt8`@&Zm z2{O(e_WFq5$P?`(I^5o@ks#yis_)gfOLN8B5<=6h8VNG4&T2lmED@+Yky~ltHjM-s zPd6EU`NKRA)=K=aXuC#&%tTRlenWSfi|C_du0zgFjRcu_;->eS_cO4i`n;iiuK3b! zZ3OW;cCcIOliSeXBy7aD^tt)k<QJ{)<<_&Ul1{e>!H(nsY!$#(_>~VjNh&A`6>U~% zirqE2<G{aQx+J98`IqTmA9fBXHsQLoSIre*zFg#Bco5wGStX~ZX{aF)*E|T0Bxo0t z)%>-WOmNZ*pz!JA-7^Y=r<>S7f0M~*&<idCBg6-LhWOTicWS{q{7mBIbLtV|S|^gf zAb9BI4rqRk<XA23yn6Xz)&$&$A-EEV5H*)=z8(oHZG{@#4;3z{N8pKQd}1&4G!Z83 z`s~C*5MF|(xgXa4uAYFjg1C&3f;RZj)mo|O9ny5p{VS>wj?y^=DS~UCM^7?9lQpmv zzB(@c%S_L|s(_&khF^>Y54go9JMMsM2b}6dFNOz7d_Mk~3dpmC-*SCCY<0e&77z^( zw!Dwmzpes-)B*0=i8EN@Esj;W<hjZ919<&yq_TKy&H7sf=a~X>IZbjiUa4S(AEx5R z5AAd~Cw90YAdfh9IYm~Z^R=_g5IzY%ey-&HuL3|>xaWXV;XTp4`(eE0J>#&Fa9aw? z&urGG%IBV_{hhjO9QYqX_9M8O&s5g?3hlQc3Wq-xq^292+%Aitng$Y2ss8X#4JiN@ z5UIdSj#*L81(*zAc&&Tm$7(PIb@VDg#}57S)e~55qNJ0Ic5!zcS~klC8BO|R6`X{d z6+Hg|CkY&WK7YKd8cxxhGmhh6hwkk1s}yQT>G7^ux4aq(y(;6F@LhDZs%dac9L6o) z6?;`yLkQI;IR-S(SoBRcjMNTA0Q|hGftwmY!E0HL8Q}{|4gZ4@-{7>D*S^=Os)i9^ zj!(cnUBeA39G|gteLr!4Szb}Yij2d<5h^&5hF|8;82J%?-1P^ltKo>ovGQ;-K3y++ Rs%*^`Wn5<0z@d}N{{upnlaBxZ literal 0 HcmV?d00001 diff --git a/backend/statistics/openApi.json b/backend/statistics/openApi.json index 43a99098d..b5621b430 100644 --- a/backend/statistics/openApi.json +++ b/backend/statistics/openApi.json @@ -1421,18 +1421,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -2342,12 +2331,15 @@ } }, "BaseDataSourceAttribute" : { - "required" : [ "code", "displayName", "name" ], + "required" : [ "code", "dataPrivacyCategory", "displayName", "name" ], "type" : "object", "properties" : { "code" : { "type" : "string" }, + "dataPrivacyCategory" : { + "$ref" : "#/components/schemas/DataPrivacyCategory" + }, "displayName" : { "type" : "string" }, @@ -2485,6 +2477,9 @@ "code" : { "type" : "string" }, + "dataPrivacyCategory" : { + "$ref" : "#/components/schemas/DataPrivacyCategory" + }, "name" : { "type" : "string" } @@ -2676,6 +2671,10 @@ } } }, + "DataPrivacyCategory" : { + "type" : "string", + "enum" : [ "QUASI_IDENTIFYING", "SENSITIVE", "INSENSITIVE" ] + }, "DataRow" : { "required" : [ "values" ], "type" : "object", @@ -3409,7 +3408,7 @@ } }, "GetEvaluationResponse" : { - "required" : [ "dataRows", "evaluationInfo", "tableColumnHeaders", "totalNumberOfElements" ], + "required" : [ "dataRows", "evaluationInfo", "sortDirection", "tableColumnHeaders", "totalNumberOfElements" ], "type" : "object", "properties" : { "dataRows" : { @@ -3421,6 +3420,12 @@ "evaluationInfo" : { "$ref" : "#/components/schemas/EvaluationInfo" }, + "sortAttribute" : { + "$ref" : "#/components/schemas/AttributeSelection" + }, + "sortDirection" : { + "$ref" : "#/components/schemas/SortDirection" + }, "tableColumnHeaders" : { "type" : "array", "items" : { @@ -3852,6 +3857,12 @@ "grouping" : { "$ref" : "#/components/schemas/Grouping" }, + "maxBin" : { + "type" : "number" + }, + "minBin" : { + "type" : "number" + }, "numberOfBins" : { "maximum" : 50, "minimum" : 2, @@ -4448,7 +4459,7 @@ }, "SortDirection" : { "type" : "string", - "default" : "DESC", + "default" : "ASC", "enum" : [ "ASC", "DESC" ] }, "StatisticsFeature" : { @@ -4493,6 +4504,9 @@ "businessModule" : { "type" : "string" }, + "dataPrivacyCategory" : { + "$ref" : "#/components/schemas/DataPrivacyCategory" + }, "dataSourceId" : { "type" : "string", "format" : "uuid" diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/AnalysisService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/AnalysisService.java index 2abbb097c..7dbbe6807 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/AnalysisService.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/AnalysisService.java @@ -360,19 +360,37 @@ public class AnalysisService { } } + validateBinning(histogramChartConfiguration, name); + } + + private static void validateBinning( + HistogramChartConfigurationDto histogramChartConfiguration, String name) { if (histogramChartConfiguration.binningMode().equals(BinningModeDto.MANUAL) - && histogramChartConfiguration.numberOfBins() == null) { + && (histogramChartConfiguration.numberOfBins() == null + || histogramChartConfiguration.minBin() == null + || histogramChartConfiguration.maxBin() == null)) { throw new BadRequestException( - "'%s': numberOfBins is required for binning mode '%s'" + "'%s': numberOfBins, minBin & maxBin is required for binning mode '%s'" .formatted(name, BinningModeDto.MANUAL.name())); } if (histogramChartConfiguration.binningMode().equals(BinningModeDto.AUTO) - && histogramChartConfiguration.numberOfBins() != null) { + && (histogramChartConfiguration.numberOfBins() != null + || histogramChartConfiguration.minBin() != null + || histogramChartConfiguration.maxBin() != null)) { throw new BadRequestException( - "'%s': numberOfBins must not be set for binning mode '%s'" + "'%s': numberOfBins, minBin & maxBin must not be set for binning mode '%s'" .formatted(name, BinningModeDto.AUTO.name())); } + + if (histogramChartConfiguration.binningMode().equals(BinningModeDto.MANUAL)) { + boolean maxIsNotGreaterThanMin = + histogramChartConfiguration.maxBin().compareTo(histogramChartConfiguration.minBin()) <= 0; + if (maxIsNotGreaterThanMin) { + throw new BadRequestException( + "'%s': value of maxBin must be greater than minBin".formatted(name)); + } + } } public List<HistogramBin> calculateHistogramBins( @@ -393,20 +411,22 @@ public class AnalysisService { } int numberOfBins; + BigDecimal minimum; + BigDecimal maximum; if (histogramChartConfiguration.binningMode().equals(BinningModeDto.MANUAL)) { numberOfBins = histogramChartConfiguration.numberOfBins(); + maximum = histogramChartConfiguration.maxBin(); + minimum = histogramChartConfiguration.minBin(); } else { numberOfBins = Math.clamp((int) Math.ceil(Math.sqrt(numberOfDataPoints)), 2, 50); - } - BigDecimal minimum; - BigDecimal maximum; - if (tableColumnPrimary.getValueType().equals(TableColumnValueType.DECIMAL)) { - minimum = tableColumnPrimary.getMinMaxNullUnknownValues().getMinDecimal(); - maximum = tableColumnPrimary.getMinMaxNullUnknownValues().getMaxDecimal(); - } else { - minimum = new BigDecimal(tableColumnPrimary.getMinMaxNullUnknownValues().getMinInteger()); - maximum = new BigDecimal(tableColumnPrimary.getMinMaxNullUnknownValues().getMaxInteger()); + if (tableColumnPrimary.getValueType().equals(TableColumnValueType.DECIMAL)) { + minimum = tableColumnPrimary.getMinMaxNullUnknownValues().getMinDecimal(); + maximum = tableColumnPrimary.getMinMaxNullUnknownValues().getMaxDecimal(); + } else { + minimum = new BigDecimal(tableColumnPrimary.getMinMaxNullUnknownValues().getMinInteger()); + maximum = new BigDecimal(tableColumnPrimary.getMinMaxNullUnknownValues().getMaxInteger()); + } } BigDecimal binWidth = calculateBinWidth(maximum, minimum, numberOfBins); 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 2c978d219..82bd31f36 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 @@ -17,6 +17,7 @@ import de.eshg.lib.aggregation.BusinessModuleAggregationHelper; import de.eshg.lib.aggregation.ClientResponse; import de.eshg.lib.auditlog.AuditLogger; import de.eshg.lib.statistics.api.Attribute; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.DataRow; import de.eshg.lib.statistics.api.DataSourceSensitivity; import de.eshg.lib.statistics.api.DataTableHeader; @@ -39,6 +40,7 @@ import de.eshg.statistics.persistence.entity.Evaluation; import de.eshg.statistics.persistence.entity.MinMaxNullUnknownValues; import de.eshg.statistics.persistence.entity.StatisticsDataSensitivity; import de.eshg.statistics.persistence.entity.TableColumn; +import de.eshg.statistics.persistence.entity.TableColumnDataPrivacyCategory; import de.eshg.statistics.persistence.entity.TableColumnValueType; import de.eshg.statistics.persistence.entity.TableRow; import de.eshg.statistics.persistence.entity.ValueToMeaning; @@ -312,6 +314,8 @@ public class DataAggregationService { if (baseModuleAttribute == null) { tableColumn.setValueType(mapToTableColumnValueType(businessModuleAttribute.valueType())); + tableColumn.setDataPrivacyCategory( + mapToTableColumnDataPrivacyCategory(businessModuleAttribute.dataPrivacyCategory())); tableColumn.setUnit(businessModuleAttribute.unit()); tableColumn.addValueToMeanings( EvaluationMapper.mapToValueToMeanings(businessModuleAttribute.valueOptions())); @@ -320,6 +324,8 @@ public class DataAggregationService { tableColumn.setBaseModuleAttributeCode(baseModuleAttribute.code()); tableColumn.setBaseModuleAttributeName(baseModuleAttribute.name()); tableColumn.setValueType(mapToTableColumnValueType(baseModuleAttribute.valueType())); + tableColumn.setDataPrivacyCategory( + mapToTableColumnDataPrivacyCategory(baseModuleAttribute.dataPrivacyCategory())); tableColumn.setUnit(baseModuleAttribute.unit()); tableColumn.addValueToMeanings( EvaluationMapper.mapToValueToMeanings(baseModuleAttribute.valueOptions())); @@ -340,6 +346,13 @@ public class DataAggregationService { return TableColumnValueType.valueOf(valueType.name()); } + private static TableColumnDataPrivacyCategory mapToTableColumnDataPrivacyCategory( + DataPrivacyCategory dataPrivacyCategory) { + return dataPrivacyCategory == null + ? null + : TableColumnDataPrivacyCategory.valueOf(dataPrivacyCategory.name()); + } + private static List<TableColumn> createTableColumnsForBaseAttributes( String dataSourceName, String businessModuleName, @@ -555,7 +568,10 @@ public class DataAggregationService { firstTableColumn.getBaseModuleAttributeCode(), secondTableColumn.getBaseModuleAttributeCode()) || !Objects.equals(firstTableColumn.getUnit(), secondTableColumn.getUnit()) - || firstTableColumn.isMandatory() != secondTableColumn.isMandatory()) { + || firstTableColumn.isMandatory() != secondTableColumn.isMandatory() + || !Objects.equals( + firstTableColumn.getDataPrivacyCategory(), + secondTableColumn.getDataPrivacyCategory())) { return true; } if (firstTableColumn.getValueToMeanings().size() 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 99e324277..12a8452e8 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 @@ -139,18 +139,19 @@ public class DataSourceAggregationService { return businessAttributes.stream() .map( attribute -> { - Optional<BaseAvailableDataSource> baseAvailableDataSource = - findBaseAvailableDataSource(baseAvailableDataSources, attribute); - return new BusinessDataSourceAttribute( - attribute.name(), - attribute.code(), - attribute.category(), - baseAvailableDataSource + List<BaseDataSourceAttribute> baseDataSourceAttributes = + findBaseAvailableDataSource(baseAvailableDataSources, attribute) .map( availableDataSource -> mapToBaseDataSourceAttributes( attribute.name(), availableDataSource.attributes())) - .orElse(null)); + .orElse(null); + return new BusinessDataSourceAttribute( + attribute.name(), + attribute.code(), + attribute.category(), + baseDataSourceAttributes == null ? attribute.dataPrivacyCategory() : null, + baseDataSourceAttributes); }) .toList(); } @@ -192,7 +193,8 @@ public class DataSourceAggregationService { EvaluationMapper.getAttributeDisplayName( businessAttributeName, attribute.name()), attribute.name(), - attribute.code())) + attribute.code(), + attribute.dataPrivacyCategory())) .toList(); } } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationCopyService.java b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationCopyService.java index 585b8adb7..1a3f05c24 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationCopyService.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/aggregation/EvaluationCopyService.java @@ -116,6 +116,7 @@ public class EvaluationCopyService { copy.setBaseModuleAttributeCode(original.getBaseModuleAttributeCode()); copy.setBaseModuleAttributeName(original.getBaseModuleAttributeName()); copy.setValueType(original.getValueType()); + copy.setDataPrivacyCategory(original.getDataPrivacyCategory()); copy.setUnit(original.getUnit()); copy.setDataSourceName(original.getDataSourceName()); copy.setDataSourceId(original.getDataSourceId()); @@ -484,6 +485,8 @@ public class EvaluationCopyService { copy.setGrouping(original.getGrouping()); copy.setBinningMode(original.getBinningMode()); copy.setNumberOfBins(original.getNumberOfBins()); + copy.setMinBin(original.getMinBin()); + copy.setMaxBin(original.getMaxBin()); if (copyHistogramBins) { copy.addBins(copyHistogramBins(original.getBins())); } 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 25f8ee96d..814f3ad46 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 @@ -464,6 +464,10 @@ public class EvaluationService extends AbstractAggregationResultService { return EvaluationMapper.mapToApi( evaluation, + sortTableColumn.getValueType().equals(TableColumnValueType.PROCEDURE_REFERENCE) + ? null + : sortTableColumn, + getEvaluationRequest.sortDirection(), tableRowPage.get().toList(), tableRowPage.getTotalElements(), isTooMuchDataForExportFunction().apply(evaluation)); @@ -480,7 +484,13 @@ public class EvaluationService extends AbstractAggregationResultService { AttributeSelectionDto sortAttribute, Evaluation evaluation) { TableColumn sortTableColumn = AggregationResultUtil.getTableColumn(sortAttribute, evaluation); if (sortTableColumn == null) { - sortTableColumn = evaluation.getTableColumns().getFirst(); + sortTableColumn = + evaluation.getTableColumns().stream() + .filter( + tableColumn -> + !tableColumn.getValueType().equals(TableColumnValueType.PROCEDURE_REFERENCE)) + .findFirst() + .orElse(evaluation.getTableColumns().getFirst()); } return sortTableColumn; } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/TableColumnHeader.java b/backend/statistics/src/main/java/de/eshg/statistics/api/TableColumnHeader.java index 3b9d07c82..b6d58e5d2 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/TableColumnHeader.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/TableColumnHeader.java @@ -5,6 +5,7 @@ package de.eshg.statistics.api; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.statistics.api.attributes.AbstractTableColumnHeaderAttribute; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; @@ -16,4 +17,5 @@ public record TableColumnHeader( @NotBlank String businessModule, @NotNull UUID dataSourceId, @NotBlank String dataSourceName, - @NotNull @Valid AbstractTableColumnHeaderAttribute attribute) {} + @NotNull @Valid AbstractTableColumnHeaderAttribute attribute, + DataPrivacyCategory dataPrivacyCategory) {} diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/chart/HistogramChartConfigurationDto.java b/backend/statistics/src/main/java/de/eshg/statistics/api/chart/HistogramChartConfigurationDto.java index e5e0cad2d..327c56bc8 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/chart/HistogramChartConfigurationDto.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/chart/HistogramChartConfigurationDto.java @@ -13,6 +13,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; @Schema(name = SCHEMA_NAME) public record HistogramChartConfigurationDto( @@ -21,7 +22,9 @@ public record HistogramChartConfigurationDto( ScalingDto scaling, GroupingDto grouping, @NotNull BinningModeDto binningMode, - @Min(2) @Max(50) Integer numberOfBins) + @Min(2) @Max(50) Integer numberOfBins, + BigDecimal minBin, + BigDecimal maxBin) implements AddChartConfigurationDto, ChartConfigurationDto { public static final String SCHEMA_NAME = "HistogramChartConfiguration"; diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java index d3b450677..63ad52674 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BaseDataSourceAttribute.java @@ -5,7 +5,12 @@ package de.eshg.statistics.api.datasource; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; public record BaseDataSourceAttribute( - @NotBlank String displayName, @NotBlank String name, @NotBlank String code) {} + @NotBlank String displayName, + @NotBlank String name, + @NotBlank String code, + @NotNull DataPrivacyCategory dataPrivacyCategory) {} diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java index 82d062773..512aee6b0 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/datasource/BusinessDataSourceAttribute.java @@ -5,6 +5,7 @@ package de.eshg.statistics.api.datasource; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import java.util.List; @@ -13,4 +14,5 @@ public record BusinessDataSourceAttribute( @NotBlank String name, @NotBlank String code, @NotBlank String category, + DataPrivacyCategory dataPrivacyCategory, @Valid List<BaseDataSourceAttribute> baseAttributes) {} diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationRequest.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationRequest.java index be3eb1f5c..c95f73fdf 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationRequest.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationRequest.java @@ -16,7 +16,7 @@ import java.util.Optional; public record GetEvaluationRequest( @Valid AttributeSelectionDto sortAttribute, - @Schema(defaultValue = "DESC") SortDirection sortDirection, + @Schema(defaultValue = "ASC") SortDirection sortDirection, @Min(0) @Schema(defaultValue = "0") Integer page, @Min(1) @Schema(defaultValue = "25") Integer pageSize, @Valid List<TableColumnFilterParameter> filters) { @@ -28,7 +28,7 @@ public record GetEvaluationRequest( Integer pageSize, List<TableColumnFilterParameter> filters) { this.sortAttribute = sortAttribute; - this.sortDirection = Optional.ofNullable(sortDirection).orElse(SortDirection.DESC); + this.sortDirection = Optional.ofNullable(sortDirection).orElse(SortDirection.ASC); this.page = Optional.ofNullable(page).orElse(0); this.pageSize = Optional.ofNullable(pageSize).orElse(25); this.filters = filters; diff --git a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationResponse.java b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationResponse.java index 9d7f9f048..3fe45f823 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationResponse.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/api/evaluation/GetEvaluationResponse.java @@ -5,7 +5,9 @@ package de.eshg.statistics.api.evaluation; +import de.eshg.base.SortDirection; import de.eshg.lib.statistics.api.DataRow; +import de.eshg.statistics.api.AttributeSelectionDto; import de.eshg.statistics.api.TableColumnHeader; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; @@ -14,6 +16,8 @@ import java.util.List; public record GetEvaluationResponse( @NotNull @Valid EvaluationInfo evaluationInfo, + @Valid AttributeSelectionDto sortAttribute, + @NotNull SortDirection sortDirection, @NotNull @Valid List<TableColumnHeader> tableColumnHeaders, @NotNull @Valid List<DataRow> dataRows, @NotNull @Min(0) long totalNumberOfElements) {} diff --git a/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/RepoMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/RepoMapper.java index 8a86461c1..90d8f2635 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/RepoMapper.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/RepoMapper.java @@ -219,7 +219,9 @@ public class RepoMapper { getEnumString(histogramChartConfiguration.getScaling()), getEnumString(histogramChartConfiguration.getGrouping()), getEnumString(histogramChartConfiguration.getBinningMode()), - histogramChartConfiguration.getNumberOfBins()); + histogramChartConfiguration.getNumberOfBins(), + histogramChartConfiguration.getMinBin(), + histogramChartConfiguration.getMaxBin()); } private static RepoLineChart mapToRepoLineChart(LineChartConfiguration lineChartConfiguration) { @@ -450,7 +452,9 @@ public class RepoMapper { mapToScaling(repoHistogramChart.scaling()), mapToGrouping(repoHistogramChart.grouping()), mapToBinning(repoHistogramChart.binningMode()), - repoHistogramChart.numberOfBins()); + repoHistogramChart.numberOfBins(), + repoHistogramChart.minBin(), + repoHistogramChart.maxBin()); } private static LineChartConfigurationDto mapToLineChartConfiguration( diff --git a/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/dto/chartconfiguration/RepoHistogramChart.java b/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/dto/chartconfiguration/RepoHistogramChart.java index 512706414..476490fb4 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/dto/chartconfiguration/RepoHistogramChart.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/centralrepository/dto/chartconfiguration/RepoHistogramChart.java @@ -11,6 +11,7 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; public record RepoHistogramChart( @NotNull @Valid RepoAttributeSelection primaryAttribute, @@ -18,7 +19,9 @@ public record RepoHistogramChart( String scaling, String grouping, @NotBlank String binningMode, - @Min(2) @Max(50) Integer numberOfBins) + @Min(2) @Max(50) Integer numberOfBins, + BigDecimal minBin, + BigDecimal maxBin) implements RepoChartConfiguration { public static final String SCHEMA_NAME = "RepoHistogramChart"; diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/AggregationResultExportService.java b/backend/statistics/src/main/java/de/eshg/statistics/export/AggregationResultExportService.java index 0b334af03..8a24514d4 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/export/AggregationResultExportService.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/export/AggregationResultExportService.java @@ -227,7 +227,7 @@ public class AggregationResultExportService { return tableRowPage.getSize(); } - @Transactional(readOnly = true) + @Transactional public void auditLogAggregationResultDataExport( UUID id, AbstractAggregationResultService service, String function) { AbstractAggregationResult aggregationResult = service.getAbstractAggregationResultInternal(id); diff --git a/backend/statistics/src/main/java/de/eshg/statistics/export/DiagramExportService.java b/backend/statistics/src/main/java/de/eshg/statistics/export/DiagramExportService.java index fed2d5c19..21659f8c1 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/export/DiagramExportService.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/export/DiagramExportService.java @@ -71,7 +71,7 @@ public class DiagramExportService { analysisService.checkPermissionForDiagram(diagramId); } - @Transactional(readOnly = true) + @Transactional public Resource exportData(UUID diagramId) { Diagram diagram = analysisService.getDiagramInternal(diagramId); AbstractAggregationResult aggregationResult = diagram.getAnalysis().getAggregationResult(); diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/AnalysisMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/AnalysisMapper.java index 1bb90fe3a..376de85c5 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/AnalysisMapper.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/AnalysisMapper.java @@ -197,6 +197,8 @@ public class AnalysisMapper { mapToBinningMode(histogramChartConfigurationDto.binningMode())); histogramChartConfiguration.setNumberOfBins(histogramChartConfigurationDto.numberOfBins()); histogramChartConfiguration.addBins(histogramBins); + histogramChartConfiguration.setMinBin(histogramChartConfigurationDto.minBin()); + histogramChartConfiguration.setMaxBin(histogramChartConfigurationDto.maxBin()); return histogramChartConfiguration; } @@ -395,7 +397,9 @@ public class AnalysisMapper { mapToScalingDto(histogramChartConfiguration.getScaling()), mapToGroupingDto(histogramChartConfiguration.getGrouping()), mapToBinningModeDto(histogramChartConfiguration.getBinningMode()), - histogramChartConfiguration.getNumberOfBins()); + histogramChartConfiguration.getNumberOfBins(), + histogramChartConfiguration.getMinBin(), + histogramChartConfiguration.getMaxBin()); } private static ChartConfigurationDto mapToChoroplethMapConfigurationDto( diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/AttributeSelectionMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/AttributeSelectionMapper.java index b62fa92fc..4831b1c3d 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/AttributeSelectionMapper.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/AttributeSelectionMapper.java @@ -7,6 +7,7 @@ package de.eshg.statistics.mapper; import de.eshg.statistics.api.AttributeSelectionDto; import de.eshg.statistics.persistence.entity.AttributeSelection; +import de.eshg.statistics.persistence.entity.TableColumn; import java.util.Objects; import java.util.UUID; @@ -70,4 +71,16 @@ public class AttributeSelectionMapper { attributeSelection.getBusinessModuleAttributeCode(), attributeSelection.getBaseModuleAttributeCode()); } + + public static AttributeSelectionDto mapToApi(TableColumn tableColumn) { + if (tableColumn == null) { + return null; + } else { + return new AttributeSelectionDto( + tableColumn.getBusinessModuleName(), + tableColumn.getDataSourceId(), + tableColumn.getBusinessModuleAttributeCode(), + tableColumn.getBaseModuleAttributeCode()); + } + } } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java index 856ac6552..dd8e28976 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/mapper/EvaluationMapper.java @@ -7,6 +7,7 @@ package de.eshg.statistics.mapper; import de.eshg.base.SortDirection; import de.eshg.base.user.api.UserDto; +import de.eshg.lib.statistics.api.DataPrivacyCategory; import de.eshg.lib.statistics.api.DataRow; import de.eshg.lib.statistics.api.ValueOptionInternal; import de.eshg.statistics.api.TableColumnHeader; @@ -33,6 +34,7 @@ import de.eshg.statistics.persistence.entity.Evaluation; import de.eshg.statistics.persistence.entity.MinMaxNullUnknownValues; import de.eshg.statistics.persistence.entity.StatisticsDataSensitivity; import de.eshg.statistics.persistence.entity.TableColumn; +import de.eshg.statistics.persistence.entity.TableColumnDataPrivacyCategory; import de.eshg.statistics.persistence.entity.TableColumnValueType; import de.eshg.statistics.persistence.entity.TableRow; import de.eshg.statistics.persistence.entity.ValueToMeaning; @@ -50,11 +52,15 @@ public class EvaluationMapper { public static GetEvaluationResponse mapToApi( Evaluation evaluation, + TableColumn sortTableColumn, + SortDirection sortDirection, List<TableRow> tableRows, long totalNumberOfElements, boolean isTooMuchDataForExport) { return new GetEvaluationResponse( mapToEvaluationInfo(evaluation, isTooMuchDataForExport), + AttributeSelectionMapper.mapToApi(sortTableColumn), + sortDirection, mapToApi(evaluation.getTableColumns()), tableRows.stream().map(EvaluationMapper::mapToApi).toList(), totalNumberOfElements); @@ -77,7 +83,8 @@ public class EvaluationMapper { tableColumn.getBusinessModuleAttributeCode(), tableColumn.getUnit(), tableColumn.getValueToMeanings(), - tableColumn.getMinMaxNullUnknownValues())); + tableColumn.getMinMaxNullUnknownValues()), + mapDataPrivacyCategory(tableColumn.getDataPrivacyCategory())); } else { return new TableColumnHeader( getAttributeDisplayName(tableColumn, false), @@ -93,7 +100,8 @@ public class EvaluationMapper { tableColumn.getBaseModuleAttributeCode(), tableColumn.getUnit(), tableColumn.getValueToMeanings(), - tableColumn.getMinMaxNullUnknownValues()))); + tableColumn.getMinMaxNullUnknownValues())), + mapDataPrivacyCategory(tableColumn.getDataPrivacyCategory())); } } @@ -171,6 +179,13 @@ public class EvaluationMapper { return new ValueOption(valueToMeaning.getValue(), valueToMeaning.getMeaning()); } + private static DataPrivacyCategory mapDataPrivacyCategory( + TableColumnDataPrivacyCategory dataPrivacyCategory) { + return dataPrivacyCategory == null + ? null + : DataPrivacyCategory.valueOf(dataPrivacyCategory.name()); + } + private static DataRow mapToApi(TableRow tableRow) { return new DataRow(tableRow.getCellEntries().stream().map(CellEntry::getValue).toList()); } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumn.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumn.java index 5f6b0f9e1..f42f28c32 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumn.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumn.java @@ -52,6 +52,10 @@ public class TableColumn extends BaseEntity { @JdbcType(PostgreSQLEnumJdbcType.class) private TableColumnValueType valueType; + @Column + @JdbcType(PostgreSQLEnumJdbcType.class) + private TableColumnDataPrivacyCategory dataPrivacyCategory; + @Column private String unit; @OneToMany( @@ -141,6 +145,14 @@ public class TableColumn extends BaseEntity { this.valueType = valueType; } + public TableColumnDataPrivacyCategory getDataPrivacyCategory() { + return dataPrivacyCategory; + } + + public void setDataPrivacyCategory(TableColumnDataPrivacyCategory dataPrivacyCategory) { + this.dataPrivacyCategory = dataPrivacyCategory; + } + public String getUnit() { return unit; } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumnDataPrivacyCategory.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumnDataPrivacyCategory.java new file mode 100644 index 000000000..fe1115a31 --- /dev/null +++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/TableColumnDataPrivacyCategory.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.statistics.persistence.entity; + +public enum TableColumnDataPrivacyCategory { + QUASI_IDENTIFYING, + SENSITIVE, + INSENSITIVE +} diff --git a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/chart/HistogramChartConfiguration.java b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/chart/HistogramChartConfiguration.java index afb249046..168ea620e 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/chart/HistogramChartConfiguration.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/persistence/entity/chart/HistogramChartConfiguration.java @@ -19,6 +19,7 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderColumn; import jakarta.validation.constraints.Min; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import org.hibernate.annotations.JdbcType; @@ -55,6 +56,12 @@ public class HistogramChartConfiguration extends ChartConfiguration @Column private Integer numberOfBins; + @Column(precision = 10, scale = 4) + private BigDecimal minBin; + + @Column(precision = 10, scale = 4) + private BigDecimal maxBin; + @OneToMany( cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, @@ -124,4 +131,20 @@ public class HistogramChartConfiguration extends ChartConfiguration this.bins.forEach(bin -> bin.setHistogramChartConfiguration(null)); this.bins.clear(); } + + public BigDecimal getMinBin() { + return minBin; + } + + public void setMinBin(BigDecimal minBin) { + this.minBin = minBin; + } + + public BigDecimal getMaxBin() { + return maxBin; + } + + public void setMaxBin(BigDecimal maxBin) { + this.maxBin = maxBin; + } } diff --git a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsPopulator.java b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsPopulator.java index 2afda5419..99d3ea083 100644 --- a/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsPopulator.java +++ b/backend/statistics/src/main/java/de/eshg/statistics/testhelper/StatisticsPopulator.java @@ -473,14 +473,18 @@ public class StatisticsPopulator { ScalingDto scaling = null; GroupingDto grouping = null; BinningModeDto binning = BinningModeDto.AUTO; - Integer numberBins = null; + Integer numberOfBins = null; + BigDecimal minBin = null; + BigDecimal maxBin = null; if (secondAttribute != null) { name = "histo with second"; secondDto = getAttributeSelectionDto(secondAttribute, businessModule, dataSourceId); scaling = ScalingDto.RELATIVE; grouping = GroupingDto.STACKED; binning = BinningModeDto.MANUAL; - numberBins = 4; + numberOfBins = 4; + minBin = BigDecimal.ONE; + maxBin = BigDecimal.TEN; } AnalysisDto analysisDto = analysisController.addAnalysis( @@ -493,7 +497,9 @@ public class StatisticsPopulator { scaling, grouping, binning, - numberBins))); + numberOfBins, + minBin, + maxBin))); addDiagramWithoutFilters(analysisDto); } 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 47f8deee6..33551dbbf 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 @@ -5,7 +5,7 @@ package de.eshg.statistics.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.rest.service.error.BadRequestException; import de.eshg.statistics.aggregation.ReportExecution; @@ -16,7 +16,6 @@ import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.DefaultTestHelperService; import de.eshg.testhelper.TestHelperController; import de.eshg.testhelper.environment.EnvironmentConfig; -import java.io.IOException; import java.util.UUID; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @@ -25,7 +24,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class StatisticsTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final StatisticsFeatureToggle statisticsFeatureToggle; private final AuditLogTestHelperService auditLogTestHelperService; @@ -59,11 +58,6 @@ public class StatisticsTestHelperController extends TestHelperController statisticsExecutorService.submit(reportExecution::handlePlannedReportsInternal); } - @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - @PostExchange("/populate-create-evaluation/{businessModuleName}/{anonymized}") public UUID createEvaluation( @PathVariable("businessModuleName") String businessModuleName, @@ -81,7 +75,7 @@ public class StatisticsTestHelperController extends TestHelperController } @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/statistics/src/main/resources/migrations/0044_add_auditlog_entry.xml b/backend/statistics/src/main/resources/migrations/0044_add_auditlog_entry.xml new file mode 100644 index 000000000..28652f29b --- /dev/null +++ b/backend/statistics/src/main/resources/migrations/0044_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/statistics/src/main/resources/migrations/0045_data_privacy_category.xml b/backend/statistics/src/main/resources/migrations/0045_data_privacy_category.xml new file mode 100644 index 000000000..a59aa1ebe --- /dev/null +++ b/backend/statistics/src/main/resources/migrations/0045_data_privacy_category.xml @@ -0,0 +1,16 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737990141304-1"> + <ext:createPostgresEnumType name="tablecolumndataprivacycategory" values="INSENSITIVE, QUASI_IDENTIFYING, SENSITIVE"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737990141304-2"> + <addColumn tableName="table_column"> + <column name="data_privacy_category" type="TABLECOLUMNDATAPRIVACYCATEGORY"/> + </addColumn> + </changeSet> +</databaseChangeLog> diff --git a/backend/statistics/src/main/resources/migrations/0046_add_minBin_and_maxBin.xml b/backend/statistics/src/main/resources/migrations/0046_add_minBin_and_maxBin.xml new file mode 100644 index 000000000..73434af86 --- /dev/null +++ b/backend/statistics/src/main/resources/migrations/0046_add_minBin_and_maxBin.xml @@ -0,0 +1,16 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737531968510-1"> + <addColumn tableName="histogram_chart_configuration"> + <column name="max_bin" type="numeric(10, 4)"/> + </addColumn> + <addColumn tableName="histogram_chart_configuration"> + <column name="min_bin" type="numeric(10, 4)"/> + </addColumn> + </changeSet> +</databaseChangeLog> diff --git a/backend/statistics/src/main/resources/migrations/0047_migrate_minBin_and_maxBin.xml b/backend/statistics/src/main/resources/migrations/0047_migrate_minBin_and_maxBin.xml new file mode 100644 index 000000000..0bc469ad7 --- /dev/null +++ b/backend/statistics/src/main/resources/migrations/0047_migrate_minBin_and_maxBin.xml @@ -0,0 +1,30 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="173753321568510-1"> + <sql> + UPDATE histogram_chart_configuration hc + SET min_bin = ( + SELECT (hb.lower_bound + hb.upper_bound) / 2 + FROM histogram_bin hb + WHERE hb.histogram_chart_configuration_id = hc.id + ORDER BY hb.lower_bound ASC + LIMIT 1 + ) + WHERE hc.binning_mode = 'MANUAL'; + UPDATE histogram_chart_configuration hc + SET max_bin = ( + SELECT (hb.lower_bound + hb.upper_bound) / 2 + FROM histogram_bin hb + WHERE hb.histogram_chart_configuration_id = hc.id + ORDER BY hb.upper_bound DESC + LIMIT 1 + ) + WHERE hc.binning_mode = 'MANUAL'; + </sql> + </changeSet> +</databaseChangeLog> diff --git a/backend/statistics/src/main/resources/migrations/changelog.xml b/backend/statistics/src/main/resources/migrations/changelog.xml index 648cc146f..f2e873a68 100644 --- a/backend/statistics/src/main/resources/migrations/changelog.xml +++ b/backend/statistics/src/main/resources/migrations/changelog.xml @@ -51,4 +51,8 @@ <include file="migrations/0041_introduce_table_column_value_type.xml"/> <include file="migrations/0042_store_procedure_references_instead_of_ids.xml"/> <include file="migrations/0043_remove_linechart_column.xml"/> + <include file="migrations/0044_add_auditlog_entry.xml"/> + <include file="migrations/0045_data_privacy_category.xml"/> + <include file="migrations/0046_add_minBin_and_maxBin.xml"/> + <include file="migrations/0047_migrate_minBin_and_maxBin.xml"/> </databaseChangeLog> diff --git a/backend/sti-protection/build.gradle b/backend/sti-protection/build.gradle index 99c14d304..e167fba5f 100644 --- a/backend/sti-protection/build.gradle +++ b/backend/sti-protection/build.gradle @@ -22,6 +22,7 @@ dependencies { testImplementation testFixtures(project(':business-module-persistence-commons')) testImplementation testFixtures(project(':lib-document-generator')) testImplementation testFixtures(project(':lib-procedures')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/sti-protection/gradle.lockfile b/backend/sti-protection/gradle.lockfile index 3dba2bc98..024a5d5b0 100644 --- a/backend/sti-protection/gradle.lockfile +++ b/backend/sti-protection/gradle.lockfile @@ -72,10 +72,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath @@ -164,10 +164,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -186,16 +186,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/sti-protection/openApi.json b/backend/sti-protection/openApi.json index 7709d6c8d..969c0858e 100644 --- a/backend/sti-protection/openApi.json +++ b/backend/sti-protection/openApi.json @@ -549,6 +549,95 @@ "tags" : [ "Archiving" ] } }, + "/citizen/public/department-info" : { + "get" : { + "operationId" : "getDepartmentInfo", + "parameters" : [ { + "in" : "query", + "name" : "request", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/GetDepartmentInfoRequest" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/GetDepartmentInfoResponse" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Get department info", + "tags" : [ "StiProtectionCitizen" ] + } + }, + "/citizen/public/free-appointments" : { + "get" : { + "operationId" : "getFreeAppointmentsForCitizen", + "parameters" : [ { + "in" : "query", + "name" : "appointmentType", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/StiAppointmentType" + } + }, { + "in" : "query", + "name" : "earliestDate", + "required" : false, + "schema" : { + "type" : "string", + "format" : "date-time" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/GetFreeAppointmentsResponse" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Get free appointments for an appointment type.", + "tags" : [ "StiProtectionCitizen" ] + } + }, + "/citizen/public/opening-hours" : { + "get" : { + "operationId" : "getOpeningHours", + "parameters" : [ { + "in" : "query", + "name" : "request", + "required" : true, + "schema" : { + "$ref" : "#/components/schemas/GetOpeningHoursRequest" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/GetOpeningHoursResponse" + } + } + }, + "description" : "OK" + } + }, + "summary" : "Get opening hours", + "tags" : [ "StiProtectionCitizen" ] + } + }, "/files/{fileId}" : { "delete" : { "operationId" : "deleteFile", @@ -2089,6 +2178,7 @@ "description" : "OK" } }, + "summary" : "Create a new STI procedure.", "tags" : [ "StiProtectionProcedure" ] } }, @@ -2570,6 +2660,7 @@ "description" : "OK" } }, + "summary" : "Create an STI follow-up procedure.", "tags" : [ "StiProtectionProcedure" ] } }, @@ -3234,18 +3325,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -3311,6 +3391,34 @@ "tags" : [ "TestHelper" ] } }, + "/test-helper/population/text-templates" : { + "post" : { + "operationId" : "populateTextTemplates", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TextTemplatePopulationRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/TextTemplatePopulationResponse" + } + } + }, + "description" : "OK" + } + }, + "tags" : [ "TestHelper" ] + } + }, "/test-helper/request-interceptor" : { "post" : { "operationId" : "interceptNextRequest", @@ -3655,9 +3763,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -4313,13 +4421,17 @@ "type" : "object", "properties" : { "content" : { - "type" : "string" + "type" : "string", + "description" : "Predefined text that will be inserted.", + "example" : "Complete hepatitis A serology (acute vs. immunity)" }, "context" : { "$ref" : "#/components/schemas/TextTemplateContext" }, "name" : { - "type" : "string" + "type" : "string", + "description" : "Display name of the text template.", + "example" : "Hepatitis Panel" } } }, @@ -4414,31 +4526,41 @@ "properties" : { "findings" : { "type" : "array", + "description" : "Records diagnostic findings using ICD-10 codes.", "items" : { "$ref" : "#/components/schemas/Icd10Code" } }, "generalRemarks" : { - "type" : "string" + "type" : "string", + "description" : "Additional remarks or observations documented during the diagnosis phase.", + "example" : "Positive for HIV antibodies and presence of P24 antigen detected." }, "medications" : { "type" : "array", + "description" : "Lists prescribed medications.", "items" : { "$ref" : "#/components/schemas/Medication" } }, "otherTestTypeName" : { - "type" : "string" + "type" : "string", + "description" : "Provides the name of a test type not included in the predefined list.", + "example" : "ELISA." }, "results" : { - "type" : "string" + "type" : "string", + "description" : "Details the results of the diagnosis.", + "example" : "Colicky pain, acute abdomen; medication prescribed for pain management." }, "resultsCommunicated" : { - "type" : "boolean" + "type" : "boolean", + "description" : "Indicates whether the patient has been informed of their diagnostic results and updates the laboratory status to 'CLOSE'." }, "testTypes" : { "uniqueItems" : true, "type" : "array", + "description" : "Specifies the type of laboratory tests conducted during examination.", "items" : { "$ref" : "#/components/schemas/TestType" } @@ -5013,6 +5135,71 @@ } } }, + "GetDepartmentInfoRequest" : { + "type" : "object", + "properties" : { + "concern" : { + "$ref" : "#/components/schemas/Concern" + } + } + }, + "GetDepartmentInfoResponse" : { + "required" : [ "city", "country", "email", "homepage", "houseNumber", "location", "name", "phoneNumber", "postalCode", "street" ], + "type" : "object", + "properties" : { + "abbreviation" : { + "type" : "string", + "description" : "The abbreviation of the name of the department", + "example" : "LTG" + }, + "city" : { + "type" : "string", + "description" : "The name of the city where the department is located", + "example" : "Berlin" + }, + "country" : { + "$ref" : "#/components/schemas/CountryCode" + }, + "email" : { + "type" : "string", + "description" : "The email address of the department", + "example" : "mail@address.de" + }, + "homepage" : { + "type" : "string", + "description" : "The domain of the department's official homepage, excluding protocols", + "example" : "department-homepage.de" + }, + "houseNumber" : { + "type" : "string", + "description" : "The house number at the department's address, including any extensions or suffixes", + "example" : "1b" + }, + "location" : { + "$ref" : "#/components/schemas/Location" + }, + "name" : { + "type" : "string", + "description" : "The name of the department", + "example" : "Gesundheitsamt Landkreis Testgebiet" + }, + "phoneNumber" : { + "type" : "string", + "description" : "The primary contact telephone number for the department", + "example" : "+491234567890" + }, + "postalCode" : { + "type" : "string", + "description" : "The postal code for the department’s address", + "example" : "12345" + }, + "street" : { + "type" : "string", + "description" : "The street name for the department’s address, not including the house number", + "example" : "Beispielweg" + } + } + }, "GetDetailedProcedureResponse" : { "required" : [ "facilities", "persons", "procedure", "tasks" ], "type" : "object", @@ -5252,6 +5439,33 @@ } } }, + "GetOpeningHoursRequest" : { + "required" : [ "concern" ], + "type" : "object", + "properties" : { + "concern" : { + "$ref" : "#/components/schemas/Concern" + } + } + }, + "GetOpeningHoursResponse" : { + "required" : [ "de", "en" ], + "type" : "object", + "properties" : { + "de" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "en" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } + }, "GetPersonFileStateResponse" : { "required" : [ "dataOrigin", "dateOfBirth", "emailAddresses", "firstName", "gender", "id", "lastName", "phoneNumbers", "referenceVersion", "salutation" ], "type" : "object", @@ -5582,7 +5796,7 @@ "GetStiProtectionProceduresSortBy" : { "type" : "string", "default" : "CREATED_AT", - "enum" : [ "CREATED_AT", "STATUS", "CONCERN", "YEAR_OF_BIRTH", "GENDER", "LAB_STATUS" ] + "enum" : [ "CREATED_AT", "SAMPLE_BARCODE", "APPOINTMENT" ] }, "GetStiProtectionProceduresSortOrder" : { "type" : "string", @@ -5731,7 +5945,8 @@ "description" : "ICD-10 code title", "example" : "Cholera" } - } + }, + "description" : "Records diagnostic findings using ICD-10 codes." }, "Image" : { "required" : [ "@type", "createdAt", "deletable", "deleted", "fileId", "fileName", "fileSizeBytes", "fileType", "locked", "modifiedAt" ], @@ -6082,6 +6297,25 @@ } } }, + "Location" : { + "required" : [ "latitude", "longitude" ], + "type" : "object", + "properties" : { + "latitude" : { + "type" : "number", + "description" : "Geographic coordinate that specifies the north–south angular location of a point on the surface of the Earth.", + "format" : "double", + "example" : 52.51627 + }, + "longitude" : { + "type" : "number", + "description" : "Geographic coordinate that specifies the east–west angular position of a point on the surface of the Earth.", + "format" : "double", + "example" : 13.377703 + } + }, + "description" : "Location defined by latitude and longitude." + }, "Mail" : { "required" : [ "@type", "createdAt", "deletable", "deleted", "fileId", "fileName", "fileSizeBytes", "fileType", "locked", "modifiedAt", "removedInvalidAttachments" ], "type" : "object", @@ -6216,16 +6450,23 @@ "type" : "object", "properties" : { "dose" : { - "type" : "string" + "type" : "string", + "description" : "Prescribed dosage of the medication.", + "example" : "300 mg once daily." }, "name" : { - "type" : "string" + "type" : "string", + "description" : "Name of the prescribed medication.", + "example" : "Tenofovir." }, "prescriptionDate" : { "type" : "string", - "format" : "date" + "description" : "Date the medication was prescribed.", + "format" : "date", + "example" : "2025-01-16" } - } + }, + "description" : "Lists prescribed medications." }, "MetaData" : { "required" : [ "@type" ], @@ -7021,6 +7262,10 @@ "type" : "string", "enum" : [ "ASC", "DESC" ] }, + "StiAppointmentType" : { + "type" : "string", + "enum" : [ "HIV_STI_CONSULTATION", "SEX_WORK" ] + }, "StiConsultationMedicalHistory" : { "type" : "object", "allOf" : [ { @@ -7101,6 +7346,9 @@ "person" : { "$ref" : "#/components/schemas/Person" }, + "sampleBarCode" : { + "type" : "string" + }, "status" : { "$ref" : "#/components/schemas/ProcedureStatus" }, @@ -7110,7 +7358,7 @@ } }, "StiProtectionProcedureOverview" : { - "required" : [ "accessCode", "concern", "createdAt", "gender", "id", "labStatus", "status", "yearOfBirth" ], + "required" : [ "concern", "createdAt", "gender", "id", "labStatus", "status", "yearOfBirth" ], "type" : "object", "properties" : { "accessCode" : { @@ -7119,6 +7367,10 @@ "appointment" : { "$ref" : "#/components/schemas/Appointment" }, + "appointmentStart" : { + "type" : "string", + "format" : "date-time" + }, "concern" : { "$ref" : "#/components/schemas/Concern" }, @@ -7139,6 +7391,9 @@ "labStatus" : { "$ref" : "#/components/schemas/LabStatus" }, + "sampleBarCode" : { + "type" : "string" + }, "status" : { "$ref" : "#/components/schemas/ProcedureStatus" }, @@ -7394,6 +7649,7 @@ }, "TestType" : { "type" : "string", + "description" : "Specifies the type of laboratory tests conducted during examination.", "enum" : [ "WESTERN_BLOT", "P24", "PCR", "OTHER" ] }, "TextTemplate" : { @@ -7401,24 +7657,58 @@ "type" : "object", "properties" : { "content" : { - "type" : "string" + "type" : "string", + "description" : "Predefined text that will be inserted.", + "example" : "Complete hepatitis A serology (acute vs. immunity)" }, "context" : { "$ref" : "#/components/schemas/TextTemplateContext" }, "externalId" : { "type" : "string", - "format" : "uuid" + "description" : "Unique identifier for referencing the text template.", + "format" : "uuid", + "example" : "UUID_4" }, "name" : { - "type" : "string" + "type" : "string", + "description" : "Display name of the text template.", + "example" : "Hepatitis Panel" } } }, "TextTemplateContext" : { "type" : "string", + "description" : "Category where the template is applicable.", + "example" : "DIAGNOSIS_RESULT", "enum" : [ "CONSULTATION_REASON", "CONSULTATION_REMARK", "RAPID_TESTS_REMARK", "LABORATORY_TESTS_REMARK", "DIAGNOSIS_RESULT", "DIAGNOSIS_REMARK" ] }, + "TextTemplatePopulationRequest" : { + "required" : [ "numberOfEntitiesToPopulate" ], + "type" : "object", + "properties" : { + "numberOfEntitiesToPopulate" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "TextTemplatePopulationResponse" : { + "required" : [ "count" ], + "type" : "object", + "properties" : { + "count" : { + "type" : "integer", + "format" : "int64" + }, + "textTemplates" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TextTemplate" + } + } + } + }, "Title" : { "type" : "string", "enum" : [ "DR", "PROF", "PROF_DR" ] @@ -7449,9 +7739,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -7513,7 +7803,7 @@ "type" : "string", "description" : "The Id of the user.", "format" : "uuid", - "example" : "UUID_4" + "example" : "UUID_5" }, "username" : { "type" : "string", diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/DepartmentInfoService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/DepartmentInfoService.java new file mode 100644 index 000000000..b53938467 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/DepartmentInfoService.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection; + +import de.eshg.base.department.GetDepartmentInfoResponse; +import de.eshg.base.department.LocationDto; +import de.eshg.lib.document.generator.department.DepartmentClient; +import de.eshg.stiprotection.api.citizen.GetOpeningHoursResponse; +import de.eshg.stiprotection.persistence.config.DepartmentInfoConfig; +import de.eshg.stiprotection.persistence.config.DepartmentInfoProperties; +import de.eshg.stiprotection.persistence.config.OpeningHoursProperties; +import de.eshg.stiprotection.persistence.db.Concern; +import java.util.Map; +import java.util.Optional; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +@Service +public class DepartmentInfoService { + + private final DepartmentClient departmentClient; + private final DepartmentInfoConfig departmentInfoConfig; + + public DepartmentInfoService( + DepartmentClient departmentClient, DepartmentInfoConfig departmentInfoConfig) { + this.departmentClient = departmentClient; + this.departmentInfoConfig = departmentInfoConfig; + } + + public GetDepartmentInfoResponse getDepartmentInfo(Concern concern) { + GetDepartmentInfoResponse baseDepartmentInfo = departmentClient.getDepartmentInfo(); + if (concern == null) { + return baseDepartmentInfo; + } + String key = + mapConcernToKey(concern, departmentInfoConfig.getDepartmentInfo(), "DepartmentInfo"); + Map<String, DepartmentInfoProperties> departments = departmentInfoConfig.getDepartmentInfo(); + DepartmentInfoProperties departmentInfo = departments.get(key); + return mergeWithDepartmentFallback(departmentInfo, baseDepartmentInfo); + } + + private String mapConcernToKey(Concern concern, Map<String, ?> map, String mapName) { + if (CollectionUtils.isEmpty(map)) { + throw new IllegalStateException("%s map is empty.".formatted(mapName)); + } + String key = concern.name().toLowerCase(); + if (!map.containsKey(key)) { + throw new IllegalStateException("%s map does not contain key %s.".formatted(mapName, key)); + } + return key; + } + + private GetDepartmentInfoResponse mergeWithDepartmentFallback( + DepartmentInfoProperties departmentInfo, GetDepartmentInfoResponse fallback) { + + Double latitude = + Optional.ofNullable(departmentInfo.latitude()) + .orElseGet(() -> fallback.location().latitude()); + Double longitude = + Optional.ofNullable(departmentInfo.longitude()) + .orElseGet(() -> fallback.location().longitude()); + + return new GetDepartmentInfoResponse( + Optional.ofNullable(departmentInfo.name()).orElseGet(fallback::name), + Optional.ofNullable(departmentInfo.abbreviation()).orElseGet(fallback::abbreviation), + Optional.ofNullable(departmentInfo.street()).orElseGet(fallback::street), + Optional.ofNullable(departmentInfo.houseNumber()).orElseGet(fallback::houseNumber), + Optional.ofNullable(departmentInfo.postalCode()).orElseGet(fallback::postalCode), + Optional.ofNullable(departmentInfo.city()).orElseGet(fallback::city), + Optional.ofNullable(departmentInfo.country()).orElseGet(fallback::country), + Optional.ofNullable(departmentInfo.phoneNumber()).orElseGet(fallback::phoneNumber), + Optional.ofNullable(departmentInfo.homepage()).orElseGet(fallback::homepage), + Optional.ofNullable(departmentInfo.email()).orElseGet(fallback::email), + new LocationDto(latitude, longitude)); + } + + public GetOpeningHoursResponse getOpeningHours(Concern concern) { + String key = mapConcernToKey(concern, departmentInfoConfig.getOpeningHours(), "OpeningHours"); + Map<String, OpeningHoursProperties> departments = departmentInfoConfig.getOpeningHours(); + OpeningHoursProperties openingHours = departments.get(key); + return new GetOpeningHoursResponse(openingHours.de(), openingHours.en()); + } +} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/FollowUpProcedureService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/FollowUpProcedureService.java index ad0dabcf9..8c2d79641 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/FollowUpProcedureService.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/FollowUpProcedureService.java @@ -16,6 +16,7 @@ import de.eshg.stiprotection.persistence.db.examination.LaboratoryTestSamplesDat import de.eshg.stiprotection.persistence.db.medicalhistory.Examination; import de.eshg.stiprotection.persistence.db.medicalhistory.MedicalHistory; import de.eshg.stiprotection.persistence.db.medicalhistory.SexWorkMedicalHistory; +import java.util.List; import org.springframework.stereotype.Service; @Service @@ -44,12 +45,23 @@ public class FollowUpProcedureService { private void transferDiagnosisData( StiProtectionProcedure procedure, StiProtectionProcedure followUpProcedure) { + Diagnosis previousDiagnosis = procedure.getDiagnosis(); + Diagnosis followUpDiagnosis = - DiagnosisMapper.toDatabaseType(DiagnosisMapper.toInterfaceType(procedure.getDiagnosis())); + DiagnosisMapper.toDatabaseType( + DiagnosisMapper.toInterfaceType(previousDiagnosis, List.of())); + followUpDiagnosis.setIcd10Codes(copyICD10Codes(previousDiagnosis)); followUpDiagnosis.setResultsCommunicated(false); followUpProcedure.setDiagnosis(followUpDiagnosis); } + private static List<String> copyICD10Codes(Diagnosis previousDiagnosis) { + if (previousDiagnosis == null) { + return List.of(); + } + return List.copyOf(previousDiagnosis.getIcd10Codes()); + } + private void transferMedicalHistoryData( StiProtectionProcedure procedure, StiProtectionProcedure followUpProcedure) { MedicalHistory medicalHistory = procedure.getMedicalHistory(); 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 8abb000d4..341cedaeb 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 @@ -7,15 +7,18 @@ package de.eshg.stiprotection; import de.eshg.lib.common.BusinessModule; import de.eshg.rest.service.security.config.StiProtectionPublicSecurityConfig; +import de.eshg.stiprotection.persistence.config.DepartmentInfoConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @SpringBootApplication @Import(StiProtectionPublicSecurityConfig.class) @EntityScan("de.eshg") +@EnableConfigurationProperties(DepartmentInfoConfig.class) public class StiProtectionApplication { @Bean diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionCitizenController.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionCitizenController.java new file mode 100644 index 000000000..884548a77 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionCitizenController.java @@ -0,0 +1,96 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection; + +import de.eshg.base.department.GetDepartmentInfoResponse; +import de.eshg.lib.appointmentblock.AppointmentBlockService; +import de.eshg.lib.appointmentblock.MappingUtil; +import de.eshg.lib.appointmentblock.api.AppointmentDto; +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.stiprotection.api.citizen.GetDepartmentInfoRequest; +import de.eshg.stiprotection.api.citizen.GetOpeningHoursRequest; +import de.eshg.stiprotection.api.citizen.GetOpeningHoursResponse; +import de.eshg.stiprotection.api.citizen.StiAppointmentTypeDto; +import de.eshg.stiprotection.mapper.ConcernMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.time.Clock; +import java.time.Instant; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path = StiProtectionCitizenController.BASE_URL) +@Tag(name = "StiProtectionCitizen") +public class StiProtectionCitizenController { + + private static final Logger log = LoggerFactory.getLogger(StiProtectionCitizenController.class); + + public static final String BASE_URL = BaseUrls.StiProtection.CITIZEN_PUBLIC_CONTROLLER; + + private final DepartmentInfoService departmentInfoService; + private final AppointmentBlockService appointmentBlockService; + private final Clock clock; + + public StiProtectionCitizenController( + DepartmentInfoService departmentInfoService, + AppointmentBlockService appointmentBlockService, + Clock clock) { + this.departmentInfoService = departmentInfoService; + this.appointmentBlockService = appointmentBlockService; + this.clock = clock; + } + + @GetMapping("/department-info") + @Operation(summary = "Get department info") + @Transactional(readOnly = true) + public GetDepartmentInfoResponse getDepartmentInfo( + @Valid @RequestBody GetDepartmentInfoRequest request) { + return departmentInfoService.getDepartmentInfo(ConcernMapper.toDatabaseType(request.concern())); + } + + @GetMapping("/opening-hours") + @Operation(summary = "Get opening hours") + @Transactional(readOnly = true) + public GetOpeningHoursResponse getOpeningHours( + @Valid @RequestBody GetOpeningHoursRequest request) { + return departmentInfoService.getOpeningHours(ConcernMapper.toDatabaseType(request.concern())); + } + + @Operation(summary = "Get free appointments for an appointment type.") + @GetMapping("/free-appointments") + @Transactional(readOnly = true) + public GetFreeAppointmentsResponse getFreeAppointmentsForCitizen( + @RequestParam(name = "appointmentType") @NotNull StiAppointmentTypeDto appointmentType, + @RequestParam(name = "earliestDate", required = false) Instant earliestDate) { + + if (earliestDate != null && earliestDate.isBefore(Instant.now(clock))) { + log.warn("Received earliestDate {} is in the past. Adjusting to current time.", earliestDate); + earliestDate = Instant.now(clock); + } + + List<AppointmentDto> appointments = + appointmentBlockService.getFreeAppointments( + earliestDate, + null, + MappingUtil.mapEnum(AppointmentType.class, appointmentType), + null, + null); + + return new GetFreeAppointmentsResponse(appointments); + } +} 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 72d1016e6..dacd07406 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 @@ -94,6 +94,7 @@ public class StiProtectionProcedureController { } @PostMapping + @Operation(summary = "Create a new STI procedure.") @Transactional public CreateProcedureResponse createProcedure( @Valid @RequestBody CreateProcedureRequest request) { @@ -108,7 +109,7 @@ public class StiProtectionProcedureController { @GetMapping("/{id}") @Operation(summary = "Get STI protection procedure by id.") - @Transactional(readOnly = true) + @Transactional public GetProcedureResponse getStiProcedure(@PathVariable("id") UUID procedureId) { auditLogger.log( "Vorgangsbearbeitung", @@ -250,13 +251,13 @@ public class StiProtectionProcedureController { } @PostMapping("/{id}/follow-up") + @Operation(summary = "Create an STI follow-up procedure.") @Transactional @ProcedureStatusTransition public CreateFollowUpProcedureResponse createFollowUpProcedure( @PathVariable("id") UUID procedureId, @Valid @RequestBody CreateFollowUpProcedureRequest request) { StiProtectionProcedure procedure = procedureFinder.findByExternalId(procedureId); - // Todo remove old access code user if (procedure.getProcedureStatus().isOpen()) { appointmentService.cancelAppointment(procedure); stiProtectionService.closeProcedure(procedure); diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureDeletionService.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureDeletionService.java index a8743bcf1..838c23f89 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureDeletionService.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/StiProtectionProcedureDeletionService.java @@ -9,10 +9,8 @@ import de.eshg.base.centralfile.FacilityApi; import de.eshg.base.centralfile.PersonApi; import de.eshg.lib.procedure.cemetery.CemeteryService; import de.eshg.lib.procedure.procedures.ProcedureDeletionService; -import de.eshg.rest.service.error.NotFoundException; import de.eshg.stiprotection.persistence.db.StiProtectionProcedure; import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository; -import java.util.UUID; import org.springframework.stereotype.Service; @Service @@ -27,12 +25,6 @@ public class StiProtectionProcedureDeletionService super(stiProtectionProcedureRepository, cemeteryService, personApi, facilityApi); } - public StiProtectionProcedure find(UUID externalId) { - return procedureRepository - .findByExternalId(externalId) - .orElseThrow(() -> new NotFoundException("Procedure " + externalId + " not found.")); - } - @Override protected void markRelatedFileStatesForDeletion(StiProtectionProcedure procedure) { // do nothing 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 67db54541..85c5b6ee1 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 @@ -46,7 +46,6 @@ import de.eshg.stiprotection.persistence.data.ResultPage; import de.eshg.stiprotection.persistence.data.StiProtectionProcedureData; import de.eshg.stiprotection.persistence.db.Concern; import de.eshg.stiprotection.persistence.db.Person; -import de.eshg.stiprotection.persistence.db.Person_; import de.eshg.stiprotection.persistence.db.StiProtectionProcedure; import de.eshg.stiprotection.persistence.db.StiProtectionProcedureRepository; import de.eshg.stiprotection.persistence.db.StiProtectionProcedure_; @@ -170,7 +169,7 @@ public class StiProtectionProcedureService { Join<StiProtectionProcedure, Person> psJoin = root.join(Procedure_.RELATED_PERSONS, JoinType.INNER); - Path<?> sortProperty = getSortProperty(sortBy, root, psJoin); + Path<?> sortProperty = getSortProperty(sortBy, root); if (sortOrder == ASC) { query.orderBy(criteriaBuilder.asc(sortProperty)); @@ -182,16 +181,11 @@ public class StiProtectionProcedureService { } private static Path<?> getSortProperty( - GetStiProtectionProceduresSortByDto sortBy, - Root<StiProtectionProcedure> root, - Join<StiProtectionProcedure, Person> psJoin) { + GetStiProtectionProceduresSortByDto sortBy, Root<StiProtectionProcedure> root) { return switch (sortBy) { case CREATED_AT -> root.get(Procedure_.createdAt); - case STATUS -> root.get(Procedure_.procedureStatus); - case CONCERN -> root.get(StiProtectionProcedure_.concern); - case YEAR_OF_BIRTH -> psJoin.get(Person_.yearOfBirth); - case GENDER -> psJoin.get(Person_.gender); - case LAB_STATUS -> root.get(StiProtectionProcedure_.labStatus); + case SAMPLE_BARCODE -> root.get(StiProtectionProcedure_.sampleBarCode); + case APPOINTMENT -> root.get(StiProtectionProcedure_.appointmentStart); }; } @@ -286,12 +280,12 @@ public class StiProtectionProcedureService { } public void deleteAnonymousUser(StiProtectionProcedure procedure) { - UUID anonymousUserId = procedure.getPerson().getAnonymousUserId(); - if (anonymousUserId == null) { - throw new BadRequestException("User already deleted."); + Person person = procedure.getPerson(); + UUID anonymousUserId = person.getAnonymousUserId(); + if (anonymousUserId != null) { + citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(anonymousUserId); + person.setAnonymousUserId(null); } - citizenAccessCodeUserApi.deleteCitizenAccessCodeUser(anonymousUserId); - procedure.getPerson().setAnonymousUserId(null); } public void verifyAnonymousUserPin(UUID procedureId, String pin) { diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetProcedureResponse.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetProcedureResponse.java index 71ba4c961..d22cdb352 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetProcedureResponse.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetProcedureResponse.java @@ -26,4 +26,5 @@ public record GetProcedureResponse( @Valid AppointmentDto appointment, @NotNull @Valid List<AppointmentHistoryEntryDto> appointmentHistory, @Valid WaitingRoomDto waitingRoom, - @NotNull LabStatusDto labStatus) {} + @NotNull LabStatusDto labStatus, + String sampleBarCode) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetStiProtectionProceduresSortByDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetStiProtectionProceduresSortByDto.java index 0f259de5f..c7d39cbf1 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetStiProtectionProceduresSortByDto.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/GetStiProtectionProceduresSortByDto.java @@ -10,9 +10,6 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "GetStiProtectionProceduresSortBy") public enum GetStiProtectionProceduresSortByDto { CREATED_AT, - STATUS, - CONCERN, - YEAR_OF_BIRTH, - GENDER, - LAB_STATUS + SAMPLE_BARCODE, + APPOINTMENT } diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java index 4387e67cd..b4aad2993 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/StiProtectionProcedureOverviewDto.java @@ -26,5 +26,7 @@ public record StiProtectionProcedureOverviewDto( CountryCode countryOfBirth, @NotNull GenderDto gender, @Valid AppointmentDto appointment, - @NotNull String accessCode, - @NotNull LabStatusDto labStatus) {} + String accessCode, + @NotNull LabStatusDto labStatus, + String sampleBarCode, + Instant appointmentStart) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationRequest.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationRequest.java new file mode 100644 index 000000000..9ed821038 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationRequest.java @@ -0,0 +1,10 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.api; + +import jakarta.validation.constraints.NotNull; + +public record TextTemplatePopulationRequest(@NotNull Integer numberOfEntitiesToPopulate) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationResponse.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationResponse.java new file mode 100644 index 000000000..62c3e70b9 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/TextTemplatePopulationResponse.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.api; + +import de.eshg.stiprotection.api.texttemplate.TextTemplateDto; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +public record TextTemplatePopulationResponse( + @Valid List<TextTemplateDto> textTemplates, @NotNull long count) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetDepartmentInfoRequest.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetDepartmentInfoRequest.java new file mode 100644 index 000000000..955279dc5 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetDepartmentInfoRequest.java @@ -0,0 +1,10 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.api.citizen; + +import de.eshg.stiprotection.api.ConcernDto; + +public record GetDepartmentInfoRequest(ConcernDto concern) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursRequest.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursRequest.java new file mode 100644 index 000000000..3ad4a5197 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursRequest.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.api.citizen; + +import de.eshg.stiprotection.api.ConcernDto; +import jakarta.validation.constraints.NotNull; + +public record GetOpeningHoursRequest(@NotNull ConcernDto concern) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursResponse.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursResponse.java new file mode 100644 index 000000000..a0b8d9ce0 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/GetOpeningHoursResponse.java @@ -0,0 +1,11 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.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/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/StiAppointmentTypeDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/StiAppointmentTypeDto.java new file mode 100644 index 000000000..e7cd6c3da --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/citizen/StiAppointmentTypeDto.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.api.citizen; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "StiAppointmentType") +public enum StiAppointmentTypeDto { + HIV_STI_CONSULTATION, + SEX_WORK +} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/DiagnosisDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/DiagnosisDto.java index 600223b73..cc73b85ee 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/DiagnosisDto.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/DiagnosisDto.java @@ -17,13 +17,28 @@ import org.springframework.util.CollectionUtils; @Schema(name = "Diagnosis") public record DiagnosisDto( - String results, - @Valid List<MedicationDto> medications, - @Valid List<Icd10CodeDto> findings, - Set<TestTypeDto> testTypes, - String otherTestTypeName, - String generalRemarks, - Boolean resultsCommunicated) { + @Schema( + description = "Details the results of the diagnosis.", + example = "Colicky pain, acute abdomen; medication prescribed for pain management.") + String results, + @Schema(description = "Lists prescribed medications.") @Valid List<MedicationDto> medications, + @Schema(description = "Records diagnostic findings using ICD-10 codes.") @Valid + List<Icd10CodeDto> findings, + @Schema(description = "Specifies the type of laboratory tests conducted during examination.") + Set<TestTypeDto> testTypes, + @Schema( + description = "Provides the name of a test type not included in the predefined list.", + example = "ELISA.") + String otherTestTypeName, + @Schema( + description = + "Additional remarks or observations documented during the diagnosis phase.", + example = "Positive for HIV antibodies and presence of P24 antigen detected.") + String generalRemarks, + @Schema( + description = + "Indicates whether the patient has been informed of their diagnostic results and updates the laboratory status to 'CLOSE'.") + Boolean resultsCommunicated) { @AssertTrue( message = diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/MedicationDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/MedicationDto.java index d30b01e9d..ed0b7b2b6 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/MedicationDto.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/diagnosis/MedicationDto.java @@ -12,4 +12,10 @@ import java.time.LocalDate; @Schema(name = "Medication") public record MedicationDto( - @NotBlank String name, @NotBlank String dose, @NotNull LocalDate prescriptionDate) {} + @Schema(description = "Name of the prescribed medication.", example = "Tenofovir.") @NotBlank + String name, + @Schema(description = "Prescribed dosage of the medication.", example = "300 mg once daily.") + @NotBlank + String dose, + @Schema(description = "Date the medication was prescribed.", example = "2025-01-16") @NotNull + LocalDate prescriptionDate) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/CreateTextTemplateRequest.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/CreateTextTemplateRequest.java index f3fd681ad..6f172cdc4 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/CreateTextTemplateRequest.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/CreateTextTemplateRequest.java @@ -5,8 +5,21 @@ package de.eshg.stiprotection.api.texttemplate; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; public record CreateTextTemplateRequest( - @NotBlank String name, @NotNull TextTemplateContextDto context, @NotBlank String content) {} + @NotBlank + @Schema(description = "Display name of the text template.", example = "Hepatitis Panel") + String name, + @NotNull + @Schema( + description = "Category where the template is applicable.", + example = "DIAGNOSIS_RESULT") + TextTemplateContextDto context, + @NotBlank + @Schema( + description = "Predefined text that will be inserted.", + example = "Complete hepatitis A serology (acute vs. immunity)") + String content) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/TextTemplateDto.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/TextTemplateDto.java index ce51225ca..b8a3dde48 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/TextTemplateDto.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/api/texttemplate/TextTemplateDto.java @@ -12,7 +12,21 @@ import java.util.UUID; @Schema(name = "TextTemplate") public record TextTemplateDto( - @NotNull UUID externalId, - @NotBlank String name, - @NotNull TextTemplateContextDto context, - @NotBlank String content) {} + @NotNull + @Schema( + description = "Unique identifier for referencing the text template.", + example = "696a337f-8cbe-47b7-ac13-2e13e574c2c5") + UUID externalId, + @NotBlank + @Schema(description = "Display name of the text template.", example = "Hepatitis Panel") + String name, + @NotNull + @Schema( + description = "Category where the template is applicable.", + example = "DIAGNOSIS_RESULT") + TextTemplateContextDto context, + @NotBlank + @Schema( + description = "Predefined text that will be inserted.", + example = "Complete hepatitis A serology (acute vs. immunity)") + String content) {} 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 11196485b..2759c4031 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 @@ -34,7 +34,8 @@ public class StiProtectionProcedureMapper { procedureData.appointment(), procedureData.userDefinedAppointment()), AppointmentHistoryMapper.toInterfaceType(procedureData.appointmentHistory()), WaitingRoomMapper.toInterfaceType(procedureData.waitingRoom()), - LabStatusMapper.toInterfaceData(procedureData.procedure().getLabStatus())); + LabStatusMapper.toInterfaceData(procedureData.procedure().getLabStatus()), + procedureData.sampleBarCode()); } public static StiProtectionProcedureOverviewDto toOverviewType( @@ -50,6 +51,8 @@ public class StiProtectionProcedureMapper { AppointmentMapper.toInterfaceType( procedureData.appointment(), procedureData.userDefinedAppointment()), procedureData.accessCode(), - LabStatusMapper.toInterfaceData(procedureData.procedure().getLabStatus())); + LabStatusMapper.toInterfaceData(procedureData.procedure().getLabStatus()), + procedureData.sampleBarCode(), + procedureData.appointmentStart()); } } diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/diagnosis/DiagnosisMapper.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/diagnosis/DiagnosisMapper.java index ed760365d..d0a5f48bb 100644 --- a/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/diagnosis/DiagnosisMapper.java +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/mapper/diagnosis/DiagnosisMapper.java @@ -30,21 +30,6 @@ public class DiagnosisMapper { entity.getResultsCommunicated()); } - public static DiagnosisDto toInterfaceType(Diagnosis entity) { - if (entity == null) { - return new DiagnosisDto(null, null, null, null, null, null, false); - } - - return new DiagnosisDto( - entity.getResults(), - MedicationMapper.toInterfaceType(entity.getMedications()), - null, - TestTypeMapper.toInterfaceType(entity.getTestTypes()), - entity.getOtherTestTypeName(), - entity.getGeneralRemarks(), - entity.getResultsCommunicated()); - } - public static Diagnosis toDatabaseType(DiagnosisDto dto) { if (dto == null) { return null; diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoConfig.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoConfig.java new file mode 100644 index 000000000..a0da16ff5 --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoConfig.java @@ -0,0 +1,25 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.persistence.config; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "de.eshg.sti-protection") +public class DepartmentInfoConfig { + + private final Map<String, DepartmentInfoProperties> departmentInfo = new HashMap<>(); + private final Map<String, OpeningHoursProperties> openingHours = new HashMap<>(); + + public Map<String, DepartmentInfoProperties> getDepartmentInfo() { + return departmentInfo; + } + + public Map<String, OpeningHoursProperties> getOpeningHours() { + return openingHours; + } +} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoProperties.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoProperties.java new file mode 100644 index 000000000..59c2ba9cc --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/DepartmentInfoProperties.java @@ -0,0 +1,22 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.persistence.config; + +import de.eshg.lib.common.CountryCode; + +public record DepartmentInfoProperties( + String name, + String abbreviation, + String street, + String houseNumber, + String postalCode, + String city, + CountryCode country, + String phoneNumber, + String homepage, + String email, + Double latitude, + Double longitude) {} diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/OpeningHoursProperties.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/OpeningHoursProperties.java new file mode 100644 index 000000000..5dd2bf1bb --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/persistence/config/OpeningHoursProperties.java @@ -0,0 +1,10 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.persistence.config; + +import java.util.List; + +public record OpeningHoursProperties(List<String> de, List<String> en) {} 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 c0af713ab..241e07493 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 @@ -57,4 +57,12 @@ public record StiProtectionProcedureData(StiProtectionProcedure procedure, Strin public WaitingRoom waitingRoom() { return procedure.getWaitingRoom(); } + + public String sampleBarCode() { + return procedure.getSampleBarCode(); + } + + public Instant appointmentStart() { + return procedure.getAppointmentStart(); + } } 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 7f15f64f2..df22cc434 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 @@ -29,12 +29,15 @@ import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderColumn; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; +import jakarta.persistence.Table; import jakarta.persistence.Transient; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -44,6 +47,13 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType; import org.springframework.util.Assert; @Entity +@Table( + indexes = { + @Index(name = "idx_sti_protection_procedure_sample_bar_code", columnList = "sample_bar_code"), + @Index( + name = "idx_sti_protection_procedure_appointment_start", + columnList = "appointment_start"), + }) public class StiProtectionProcedure extends Procedure<StiProtectionProcedure, StiProtectionTask, Person, Facility> implements EntityWithAppointment { @@ -134,6 +144,12 @@ public class StiProtectionProcedure @Column(nullable = false) private LabStatus labStatus; + @DataSensitivity(SensitivityLevel.SENSITIVE) + private String sampleBarCode; + + @DataSensitivity(SensitivityLevel.SENSITIVE) + private Instant appointmentStart; + @Transient public Person getPerson() { Assert.isTrue(getRelatedPersons().size() == 1, "There should be exactly one related person"); @@ -286,7 +302,13 @@ public class StiProtectionProcedure @PrePersist @PreUpdate - public void computeLabStatus() { + public void computeFieldValues() { + computeLabStatus(); + computeSampleBarcode(); + computeAppointmentStart(); + } + + private void computeLabStatus() { labStatus = LabStatus.OPEN; if (laboratoryTestExamination != null && Objects.nonNull(laboratoryTestExamination.getTestsConductedDate())) { @@ -300,4 +322,28 @@ public class StiProtectionProcedure public LabStatus getLabStatus() { return labStatus; } + + private void computeSampleBarcode() { + if (laboratoryTestExamination == null) { + sampleBarCode = null; + } else { + sampleBarCode = laboratoryTestExamination.getSampleBarCode(); + } + } + + public String getSampleBarCode() { + return sampleBarCode; + } + + private void computeAppointmentStart() { + if (userDefinedAppointment != null) { + appointmentStart = userDefinedAppointment.getAppointmentStart(); + } else if (appointment != null) { + appointmentStart = appointment.getAppointmentStart(); + } + } + + public Instant getAppointmentStart() { + return appointmentStart; + } } 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 204191f1d..324abfae0 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 @@ -5,18 +5,20 @@ package de.eshg.stiprotection.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; 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; +import de.eshg.stiprotection.api.TextTemplatePopulationRequest; +import de.eshg.stiprotection.api.TextTemplatePopulationResponse; +import de.eshg.stiprotection.api.texttemplate.TextTemplateDto; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.TestHelperController; 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; @@ -25,21 +27,24 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class StiProtectionTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { private final AuditLogTestHelperService auditLogTestHelperService; private final StiProtectionPopulator populator; + private final TextTemplatePopulator textTemplatePopulator; private final OverdueProceduresNotifier overdueProceduresNotifier; public StiProtectionTestHelperController( StiProtectionTestHelperService testHelperService, AuditLogTestHelperService auditLogTestHelperService, StiProtectionPopulator populator, + TextTemplatePopulator textTemplatePopulator, EnvironmentConfig environmentConfig, OverdueProceduresNotifier overdueProceduresNotifier) { super(testHelperService, environmentConfig); this.auditLogTestHelperService = auditLogTestHelperService; this.populator = populator; + this.textTemplatePopulator = textTemplatePopulator; this.overdueProceduresNotifier = overdueProceduresNotifier; } @@ -52,6 +57,14 @@ public class StiProtectionTestHelperController extends TestHelperController result.entities(), result.totalNumberOfElements()); } + @PostExchange("/population/text-templates") + public TextTemplatePopulationResponse populateTextTemplates( + @Valid @RequestBody TextTemplatePopulationRequest request) { + ListWithTotalNumber<TextTemplateDto> result = + this.textTemplatePopulator.populate(request.numberOfEntitiesToPopulate()); + return new TextTemplatePopulationResponse(result.entities(), result.totalNumberOfElements()); + } + @PostExchange("/notify/overdue-procedures") public ResponseEntity<Void> notifyOfOverdueProcedures() { overdueProceduresNotifier.runNow(); @@ -59,12 +72,7 @@ public class StiProtectionTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } diff --git a/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/TextTemplatePopulator.java b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/TextTemplatePopulator.java new file mode 100644 index 000000000..290c7a1aa --- /dev/null +++ b/backend/sti-protection/src/main/java/de/eshg/stiprotection/testhelper/TextTemplatePopulator.java @@ -0,0 +1,94 @@ +/* + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package de.eshg.stiprotection.testhelper; + +import static de.eshg.base.util.ClassNameUtil.getClassNameAsPropertyKey; + +import de.eshg.stiprotection.TextTemplateController; +import de.eshg.stiprotection.api.texttemplate.CreateTextTemplateRequest; +import de.eshg.stiprotection.api.texttemplate.CreateTextTemplateResponse; +import de.eshg.stiprotection.api.texttemplate.TextTemplateContextDto; +import de.eshg.stiprotection.api.texttemplate.TextTemplateDto; +import de.eshg.stiprotection.mapper.texttemplate.TextTemplateMapper; +import de.eshg.stiprotection.persistence.db.texttemplate.TextTemplate; +import de.eshg.stiprotection.persistence.db.texttemplate.TextTemplateRepository; +import de.eshg.testhelper.environment.EnvironmentConfig; +import de.eshg.testhelper.population.BasePopulator; +import de.eshg.testhelper.population.ListWithTotalNumber; +import de.eshg.testhelper.population.PopulateWithAccessTokenHelper; +import de.eshg.testhelper.population.PopulationProperties; +import de.eshg.testhelper.population.PopulatorComponent; +import java.time.Clock; +import java.util.Optional; +import net.datafaker.Faker; + +@PopulatorComponent +public class TextTemplatePopulator extends BasePopulator<TextTemplateDto> { + + private final PopulateWithAccessTokenHelper populateWithAccessTokenHelper; + private final TextTemplateController textTemplateController; + private final TextTemplateRepository textTemplateRepository; + + public TextTemplatePopulator( + PopulationProperties properties, + Clock clock, + EnvironmentConfig environmentConfig, + PopulateWithAccessTokenHelper populateWithAccessTokenHelper, + TextTemplateRepository textTemplateRepository, + TextTemplateController textTemplateController) { + super(properties, clock, getClassNameAsPropertyKey(TextTemplateDto.class), environmentConfig); + this.populateWithAccessTokenHelper = populateWithAccessTokenHelper; + this.textTemplateController = textTemplateController; + this.textTemplateRepository = textTemplateRepository; + } + + @Override + public ListWithTotalNumber<TextTemplateDto> populate(int numberOfEntitiesToPopulate) { + return populateWithAccessTokenHelper.doWithAccessToken( + () -> populateWithAuthentication(numberOfEntitiesToPopulate)); + } + + @Override + protected TextTemplateDto populate( + int index, + Faker faker, + BasePopulator<TextTemplateDto>.UniqueValueProvider uniqueValueProvider) { + + CreateTextTemplateResponse resp = + textTemplateController.createTextTemplate(textTemplate(faker)); + Optional<TextTemplate> texTemplateDto = + this.textTemplateRepository.findByExternalId(resp.textTemplateId()); + + return TextTemplateMapper.toInterfaceType(texTemplateDto.get()); + } + + @Override + protected long countExistingEntities() { + return textTemplateRepository.count(); + } + + private static CreateTextTemplateRequest textTemplate(Faker faker) { + return new CreateTextTemplateRequest( + faker.theItCrowd().actors() + " - Vorlage " + faker.random().nextInt(1, 10), + BasePopulator.randomElement(faker, TextTemplateContextDto.values()), + BasePopulator.randomElement(faker, textTemplateContents())); + } + + private static String[] textTemplateContents() { + return new String[] { + "Patient ist verunsichert und uneinsichtig.", + "Syphilis-Wert: $Wert", + "Der Grund für den Besuch des Patienten ist $Grund.", + "Der Patient ist allergisch gegen $Allergie und kann deshalb nicht mit $Arznei behandelt werden.", + "Was ist ihre Aufgabe im Betrieb/Welche Position haben Sie inne? Antwort: $Aufgabe", + "Scherzfrage: Wie hoch ist die durchschnittliche Fluggeschwindigkeit einer unbeladenen Schwalbe? Antwort: $Antwort", + "HIV-Testergebnis: $Ergebnis.", + "Anmerkung für MFA: $Notiz.", + "Anmerkung für Arzt:Ärztin: $Notiz.", + "Gruppe: $Wert\nFlora: $Wert\nEndocervix: $Wert\nProliferationsgrad: $Wert\n\nBemerkung: $Text.\n\nHPV\nhigh risk HPV-DNA: $Befund\nlow risk HPV-DNA: $Befund\n\nC-Nr.: $LabornummerFürBürger" + }; + } +} diff --git a/backend/sti-protection/src/main/resources/application-health-department-frankfurt.properties b/backend/sti-protection/src/main/resources/application-health-department-frankfurt.properties new file mode 100644 index 000000000..1561ea528 --- /dev/null +++ b/backend/sti-protection/src/main/resources/application-health-department-frankfurt.properties @@ -0,0 +1,39 @@ +de.eshg.sti-protection.department-info.hiv_sti_consultation.name=HIV/STI - Beratung +de.eshg.sti-protection.department-info.hiv_sti_consultation.abbreviation=HIV-STI-Beratung +de.eshg.sti-protection.department-info.hiv_sti_consultation.street=Breite Gasse +de.eshg.sti-protection.department-info.hiv_sti_consultation.houseNumber=28 +de.eshg.sti-protection.department-info.hiv_sti_consultation.postalCode=60313 +de.eshg.sti-protection.department-info.hiv_sti_consultation.city=Frankfurt am Main +de.eshg.sti-protection.department-info.hiv_sti_consultation.country=DE +de.eshg.sti-protection.department-info.hiv_sti_consultation.phoneNumber=+49 69 212 43270 +de.eshg.sti-protection.department-info.hiv_sti_consultation.homepage=https://frankfurt.de/service-und-rathaus/verwaltung/aemter-und-institutionen/gesundheitsamt +de.eshg.sti-protection.department-info.hiv_sti_consultation.email=sexuelle.gesundheit@stadt-frankfurt.de +# de.eshg.sti-protection.department-info.hiv_sti_consultation.latitude= +# de.eshg.sti-protection.department-info.hiv_sti_consultation.longitude= + +# can be set individually to overwrite base department infos +de.eshg.sti-protection.department-info.sex_work.name=HIV/STI - Sexarbeit +de.eshg.sti-protection.department-info.sex_work.abbreviation=Sexarbeit +de.eshg.sti-protection.department-info.sex_work.street=Breite Gasse +de.eshg.sti-protection.department-info.sex_work.houseNumber=28 +de.eshg.sti-protection.department-info.sex_work.postalCode=60313 +de.eshg.sti-protection.department-info.sex_work.city=Frankfurt am Main +de.eshg.sti-protection.department-info.sex_work.country=DE +de.eshg.sti-protection.department-info.sex_work.phoneNumber=+49 69 212 43270 +de.eshg.sti-protection.department-info.sex_work.homepage=https://frankfurt.de/service-und-rathaus/verwaltung/aemter-und-institutionen/gesundheitsamt +de.eshg.sti-protection.department-info.sex_work.email=sexuelle.gesundheit@stadt-frankfurt.de +# de.eshg.sti-protection.department-info.sex_work.latitude= +# de.eshg.sti-protection.department-info.sex_work.longitude= + + +de.eshg.sti-protection.opening-hours.sex_work.de[0]=Di, Mi +de.eshg.sti-protection.opening-hours.sex_work.de[1]=09:00 - 11:00 Uhr\nOffene Sprechstunde nur für Sexarbeiterinnen und Sexarbeiter + +de.eshg.sti-protection.opening-hours.sex_work.en[0]=Tu, We +de.eshg.sti-protection.opening-hours.sex_work.en[1]=09:00 - 11:00 am\nOpen consultation hours only for sex workers + +de.eshg.sti-protection.opening-hours.hiv_sti_consultation.de[0]=Di, Mi 09:00 - 11:00 Uhr +de.eshg.sti-protection.opening-hours.hiv_sti_consultation.de[1]=Offene Sprechstunde für alle + +de.eshg.sti-protection.opening-hours.hiv_sti_consultation.en[0]=Tu, We 09:00 - 11:00 am +de.eshg.sti-protection.opening-hours.hiv_sti_consultation.en[1]=Open consultation hours for everyone diff --git a/backend/sti-protection/src/main/resources/application.properties b/backend/sti-protection/src/main/resources/application.properties index c5cb3b8f9..a4046083b 100644 --- a/backend/sti-protection/src/main/resources/application.properties +++ b/backend/sti-protection/src/main/resources/application.properties @@ -19,6 +19,7 @@ de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[SEX_WORK]=30m de.eshg.lib.appointmentblock.defaultAppointmentTypeConfiguration[RESULTS_REVIEW]=30m eshg.population.default-number-of-entities-to-populate.sti-protection-procedure=30 +eshg.population.default-number-of-entities-to-populate.text-template-dto=30 eshg.population.default-number-of-entities-to-populate.appointment-block-group=0 eshg.sti-protection.overdue-procedures.cron=0 0 1 * * * diff --git a/backend/sti-protection/src/main/resources/migrations/0045_add_sample_bar_code.xml b/backend/sti-protection/src/main/resources/migrations/0045_add_sample_bar_code.xml new file mode 100644 index 000000000..50fca9405 --- /dev/null +++ b/backend/sti-protection/src/main/resources/migrations/0045_add_sample_bar_code.xml @@ -0,0 +1,20 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737541616148-1"> + <addColumn tableName="sti_protection_procedure"> + <column name="sample_bar_code" type="TEXT"/> + </addColumn> + </changeSet> + <changeSet author="GA-Lotse" id="1737541616148-2"> + <createIndex indexName="idx_sti_protection_procedure_sample_bar_code" tableName="sti_protection_procedure"> + <column name="sample_bar_code"/> + </createIndex> + </changeSet> +</databaseChangeLog> diff --git a/backend/sti-protection/src/main/resources/migrations/0046_add_appointment_start.xml b/backend/sti-protection/src/main/resources/migrations/0046_add_appointment_start.xml new file mode 100644 index 000000000..1b6c2dc9f --- /dev/null +++ b/backend/sti-protection/src/main/resources/migrations/0046_add_appointment_start.xml @@ -0,0 +1,21 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737557805956-1"> + <addColumn tableName="sti_protection_procedure"> + <column name="appointment_start" type="TIMESTAMP WITH TIME ZONE"/> + </addColumn> + </changeSet> + <changeSet author="GA-Lotse" id="1737557805956-2"> + <createIndex indexName="idx_sti_protection_procedure_appointment_start" + tableName="sti_protection_procedure"> + <column name="appointment_start"/> + </createIndex> + </changeSet> +</databaseChangeLog> diff --git a/backend/sti-protection/src/main/resources/migrations/0047_add_auditlog_entry.xml b/backend/sti-protection/src/main/resources/migrations/0047_add_auditlog_entry.xml new file mode 100644 index 000000000..28652f29b --- /dev/null +++ b/backend/sti-protection/src/main/resources/migrations/0047_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/sti-protection/src/main/resources/migrations/0048_convert_duration_columns_to_interval.xml b/backend/sti-protection/src/main/resources/migrations/0048_convert_duration_columns_to_interval.xml new file mode 100644 index 000000000..0f9b823d4 --- /dev/null +++ b/backend/sti-protection/src/main/resources/migrations/0048_convert_duration_columns_to_interval.xml @@ -0,0 +1,30 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="use_interval_for_durations"> + <sql> + ALTER TABLE appointment_type_config + ALTER COLUMN standard_duration_in_minutes TYPE interval second(6) + USING (standard_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_type_config" + oldColumnName="standard_duration_in_minutes" + newColumnName="standard_duration"/> + + <sql> + ALTER TABLE appointment_block_group + ALTER COLUMN slot_duration_in_minutes TYPE interval second(6) + USING (slot_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_block_group" + oldColumnName="slot_duration_in_minutes" + newColumnName="slot_duration"/> + </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 442c59cbc..aee80fc88 100644 --- a/backend/sti-protection/src/main/resources/migrations/changelog.xml +++ b/backend/sti-protection/src/main/resources/migrations/changelog.xml @@ -52,5 +52,9 @@ <include file="migrations/0042_add_previous_file_state_id_to_system_progress_entry.xml"/> <include file="migrations/0043_add_text_templates.xml"/> <include file="migrations/0044_add_follow_up_flag.xml"/> + <include file="migrations/0045_add_sample_bar_code.xml"/> + <include file="migrations/0046_add_appointment_start.xml"/> + <include file="migrations/0047_add_auditlog_entry.xml"/> + <include file="migrations/0048_convert_duration_columns_to_interval.xml"/> </databaseChangeLog> diff --git a/backend/synapse/gradle.lockfile b/backend/synapse/gradle.lockfile index e1de784a1..205a9bf9b 100644 --- a/backend/synapse/gradle.lockfile +++ b/backend/synapse/gradle.lockfile @@ -20,10 +20,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -36,9 +36,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/test-commons/gradle.lockfile b/backend/test-commons/gradle.lockfile index 3cfa3ddc8..20a9f4359 100644 --- a/backend/test-commons/gradle.lockfile +++ b/backend/test-commons/gradle.lockfile @@ -33,7 +33,7 @@ de.cronn:test-utils:1.1.1=compileClasspath,productionRuntimeClasspath,runtimeCla de.cronn:validation-file-assertions:0.8.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.persistence:jakarta.persistence-api:3.1.0=compileClasspath @@ -61,11 +61,11 @@ org.checkerframework:checker-qual:3.43.0=productionRuntimeClasspath,runtimeClass org.hamcrest:hamcrest-core:2.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-core:6.6.4.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.jetbrains:annotations:26.0.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt +org.jetbrains:annotations:26.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -78,9 +78,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/backend/test-commons/src/main/java/de/eshg/base/AuditLogTraits.java b/backend/test-commons/src/main/java/de/eshg/base/AuditLogTraits.java deleted file mode 100644 index 64b0e2037..000000000 --- a/backend/test-commons/src/main/java/de/eshg/base/AuditLogTraits.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: Apache-2.0 - */ - -package de.eshg.base; - -import de.cronn.assertions.validationfile.junit5.JUnit5ValidationFileAssertions; -import de.cronn.assertions.validationfile.normalization.ValidationNormalizer; -import de.eshg.normalization.UuidNormalizer; - -public interface AuditLogTraits extends JUnit5ValidationFileAssertions { - - String SUFFIX = "auditlog"; - - StaticLogDirExtension auditLogDirExtension(); - - default ValidationNormalizer defaultValidationNormalizer() { - return new UuidNormalizer(); - } - - default void assertAuditLogContentWithFile() { - StaticLogDirExtension staticLogDirExtension = auditLogDirExtension(); - assertWithFileWithSuffix( - dumpContent(staticLogDirExtension), defaultValidationNormalizer(), SUFFIX); - } - - default void assertAuditLogContentWithFileWithSuffix(String suffix) { - StaticLogDirExtension staticLogDirExtension = auditLogDirExtension(); - assertWithFileWithSuffix( - dumpContent(staticLogDirExtension), defaultValidationNormalizer(), SUFFIX + "_" + suffix); - } - - private String dumpContent(StaticLogDirExtension staticLogDirExtension) { - return PathUtil.listDirectoryAndFileContent(staticLogDirExtension.toPath()); - } -} diff --git a/backend/test-commons/src/main/java/de/eshg/base/StaticLogDirExtension.java b/backend/test-commons/src/main/java/de/eshg/base/StaticLogDirExtension.java deleted file mode 100644 index d74d6d79e..000000000 --- a/backend/test-commons/src/main/java/de/eshg/base/StaticLogDirExtension.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: Apache-2.0 - */ - -package de.eshg.base; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.nio.file.Path; -import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -public class StaticLogDirExtension implements BeforeEachCallback { - - private final Path logOutputDir = TempDirUtil.getTempDirOrThrow(); - - @Override - public void beforeEach(ExtensionContext context) throws IOException { - FileUtils.cleanDirectory(logOutputDir.toFile()); - assertThat(logOutputDir).isEmptyDirectory(); - } - - public Path toPath() { - return logOutputDir; - } - - public Path toAbsolutePath() { - return logOutputDir.toAbsolutePath(); - } -} diff --git a/backend/test-commons/src/main/java/de/eshg/base/ZipUtil.java b/backend/test-commons/src/main/java/de/eshg/base/ZipUtil.java index 3233cb380..24a92d8fd 100644 --- a/backend/test-commons/src/main/java/de/eshg/base/ZipUtil.java +++ b/backend/test-commons/src/main/java/de/eshg/base/ZipUtil.java @@ -23,34 +23,44 @@ import org.apache.commons.lang3.exception.UncheckedException; public class ZipUtil { + private static final int MAX_ENTRIES = 1_000; + private static final int MAX_BYTES = 1_000_000; + + public static final String TOO_MANY_ZIP_ENTRIES = "Too many ZIP entries"; + public static final String EXPANDED_CONTENT_TOO_LARGE = "Expanded content too large"; + private ZipUtil() {} public static byte[] extractZipEntry(String internalPath, byte[] content) { try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content); ZipInputStream zipInputStream = new ZipInputStream(byteArrayInputStream)) { - for (ZipEntry zipEntry = zipInputStream.getNextEntry(); - zipEntry != null; - zipEntry = zipInputStream.getNextEntry()) { + for (int numberOfEntries = 0; numberOfEntries < MAX_ENTRIES; numberOfEntries++) { + ZipEntry zipEntry = zipInputStream.getNextEntry(); + if (zipEntry == null) { + return new byte[0]; + } if (internalPath.equals(zipEntry.getName())) { - return zipInputStream.readAllBytes(); + return readBytes(zipInputStream); } } + throw new IllegalStateException(TOO_MANY_ZIP_ENTRIES); } catch (IOException e) { throw new UncheckedIOException(e); } - return new byte[0]; } public static List<String> listZipEntries(byte[] content) { try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content); ZipInputStream zipInputStream = new ZipInputStream(byteArrayInputStream)) { List<String> zipEntries = new ArrayList<>(); - for (ZipEntry zipEntry = zipInputStream.getNextEntry(); - zipEntry != null; - zipEntry = zipInputStream.getNextEntry()) { + for (int numberOfEntries = 0; numberOfEntries < MAX_ENTRIES; numberOfEntries++) { + ZipEntry zipEntry = zipInputStream.getNextEntry(); + if (zipEntry == null) { + return zipEntries; + } zipEntries.add(zipEntry.getName()); } - return zipEntries; + throw new IllegalStateException(TOO_MANY_ZIP_ENTRIES); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -65,20 +75,28 @@ public class ZipUtil { } public static String listZipContent(byte[] content, List<ValidationNormalizer> normalizers) { + long totalSize = 0; List<String> results = new ArrayList<>(); try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content); ZipInputStream zipInputStream = new ZipInputStream(byteArrayInputStream)) { - for (ZipEntry zipEntry = zipInputStream.getNextEntry(); - zipEntry != null; - zipEntry = zipInputStream.getNextEntry()) { - results.add(stringifyZipEntry(zipEntry, zipInputStream.readAllBytes(), normalizers)); + for (int numberOfEntries = 0; numberOfEntries < MAX_ENTRIES; numberOfEntries++) { + ZipEntry zipEntry = zipInputStream.getNextEntry(); + if (zipEntry == null) { + return results.stream() + .map(String::stripTrailing) + .collect(Collectors.joining(System.lineSeparator())); + } + byte[] entryContent = readBytes(zipInputStream); + totalSize += entryContent.length; + if (totalSize > MAX_BYTES) { + throw new IllegalStateException(EXPANDED_CONTENT_TOO_LARGE); + } + results.add(stringifyZipEntry(zipEntry, entryContent, normalizers)); } + throw new IllegalStateException(TOO_MANY_ZIP_ENTRIES); } catch (IOException e) { throw new UncheckedIOException(e); } - return results.stream() - .map(String::stripTrailing) - .collect(Collectors.joining(System.lineSeparator())); } private static String stringifyZipEntry( @@ -120,4 +138,12 @@ public class ZipUtil { } return source; } + + private static byte[] readBytes(ZipInputStream zipInputStream) throws IOException { + byte[] result = zipInputStream.readNBytes(MAX_BYTES); + if (zipInputStream.readNBytes(1).length > 0) { + throw new IllegalArgumentException(EXPANDED_CONTENT_TOO_LARGE); + } + return result; + } } diff --git a/backend/test-helper-commons-api/gradle.lockfile b/backend/test-helper-commons-api/gradle.lockfile index f7af9e94f..da1f0d31c 100644 --- a/backend/test-helper-commons-api/gradle.lockfile +++ b/backend/test-helper-commons-api/gradle.lockfile @@ -9,7 +9,7 @@ com.jayway.jsonpath:json-path:2.9.0=testCompileClasspath,testRuntimeClasspath com.vaadin.external.google:android-json:0.0.20131108.vaadin1=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -24,10 +24,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -40,9 +40,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/test-helper-commons-spring/gradle.lockfile b/backend/test-helper-commons-spring/gradle.lockfile index 2424c4def..23de51727 100644 --- a/backend/test-helper-commons-spring/gradle.lockfile +++ b/backend/test-helper-commons-spring/gradle.lockfile @@ -20,10 +20,10 @@ org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath org.assertj:assertj-core:3.26.3=testCompileClasspath,testRuntimeClasspath org.awaitility:awaitility:4.2.2=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath org.junit.jupiter:junit-jupiter-params:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -36,9 +36,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt 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 diff --git a/backend/test-helper-commons/gradle.lockfile b/backend/test-helper-commons/gradle.lockfile index 9c8bed433..bd3ebd849 100644 --- a/backend/test-helper-commons/gradle.lockfile +++ b/backend/test-helper-commons/gradle.lockfile @@ -55,10 +55,10 @@ io.prometheus:prometheus-metrics-exposition-formats:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-model:1.3.5=testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.persistence:jakarta.persistence-api:3.1.0=compileClasspath @@ -99,10 +99,10 @@ org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath org.hdrhistogram:HdrHistogram:2.2.2=testRuntimeClasspath org.hibernate.orm:hibernate-core:6.6.4.Final=compileClasspath org.hibernate.validator:hibernate-validator:8.0.2.Final=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -120,16 +120,17 @@ org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DefaultTestHelperService.java b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DefaultTestHelperService.java index 0069e193e..7fe639a35 100644 --- a/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DefaultTestHelperService.java +++ b/backend/test-helper-commons/src/main/java/de/eshg/testhelper/DefaultTestHelperService.java @@ -111,12 +111,16 @@ public class DefaultTestHelperService implements TestHelperWithDatabaseService { public void resetResettableProperties() { environmentConfig.assertIsNotProduction(); for (ResettableProperties resettableProperties : resettableProperties) { - String resettablePropertiesSnapshot = - initialResettablePropertiesSnapshots.get(resettableProperties); - SnapshotUtil.restoreSnapshot(resettablePropertiesSnapshot, resettableProperties); + resetProperties(resettableProperties); } } + public void resetProperties(ResettableProperties resettableProperties) { + String resettablePropertiesSnapshot = + initialResettablePropertiesSnapshots.get(resettableProperties); + SnapshotUtil.restoreSnapshot(resettablePropertiesSnapshot, resettableProperties); + } + protected String[] getTablesToExclude() { return new String[] {}; } diff --git a/backend/travel-medicine/build.gradle b/backend/travel-medicine/build.gradle index d6e4afe3c..506254636 100644 --- a/backend/travel-medicine/build.gradle +++ b/backend/travel-medicine/build.gradle @@ -23,6 +23,7 @@ dependencies { testImplementation testFixtures(project(':lib-procedures')) testImplementation testFixtures(project(':base-api')) testImplementation testFixtures(project(':lib-procedures')) + testImplementation testFixtures(project(':lib-auditlog')) } dockerCompose { diff --git a/backend/travel-medicine/gradle.lockfile b/backend/travel-medicine/gradle.lockfile index 30a32d8e7..2537c3d60 100644 --- a/backend/travel-medicine/gradle.lockfile +++ b/backend/travel-medicine/gradle.lockfile @@ -72,10 +72,10 @@ io.prometheus:prometheus-metrics-exposition-textformats:1.3.5=productionRuntimeC io.prometheus:prometheus-metrics-model:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.prometheus:prometheus-metrics-tracer-common:1.3.5=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.smallrye:jandex:3.2.0=annotationProcessor,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations:2.2.27=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.27=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations:2.2.28=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.28=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=annotationProcessor,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 @@ -161,10 +161,10 @@ org.hibernate.orm:hibernate-core:6.6.4.Final=annotationProcessor,compileClasspat org.hibernate.orm:hibernate-envers:6.6.4.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hibernate.orm:hibernate-jpamodelgen:6.6.4.Final=annotationProcessor org.hibernate.validator:hibernate-validator:8.0.2.Final=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jboss.logging:jboss-logging:3.6.1.Final=annotationProcessor,compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jetbrains:annotations:17.0.0=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath @@ -183,16 +183,17 @@ org.mozilla:rhino:1.7.13=productionRuntimeClasspath,runtimeClasspath,testRuntime org.objenesis:objenesis:3.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.openapitools:jackson-databind-nullable:0.2.6=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testCompileClasspath,testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath org.slf4j:jul-to-slf4j:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.16=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-common:2.8.3=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.3=testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:2.8.4=productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.4=testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-actuator:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-autoconfigure:3.4.1=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath diff --git a/backend/travel-medicine/openApi.json b/backend/travel-medicine/openApi.json index 1bed10406..3f3ad546a 100644 --- a/backend/travel-medicine/openApi.json +++ b/backend/travel-medicine/openApi.json @@ -3436,18 +3436,7 @@ }, "/test-helper/archiving-job" : { "post" : { - "operationId" : "runArchivingJob", - "responses" : { - "200" : { - "description" : "OK" - } - }, - "tags" : [ "TestHelper" ] - } - }, - "/test-helper/audit-log-storage" : { - "delete" : { - "operationId" : "clearAuditLogStorageDirectory", + "operationId" : "runAuditLogArchivingJob", "responses" : { "200" : { "description" : "OK" @@ -5039,9 +5028,9 @@ "format" : "uuid" }, "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, @@ -9474,9 +9463,9 @@ "type" : "object", "properties" : { "standardDurationInMinutes" : { - "minimum" : 0, + "minimum" : 1, "type" : "integer", - "format" : "int32" + "format" : "int64" } } }, diff --git a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TravelMedicineTestHelperController.java b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TravelMedicineTestHelperController.java index 344fcddfd..58d8c3418 100644 --- a/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TravelMedicineTestHelperController.java +++ b/backend/travel-medicine/src/main/java/de/eshg/travelmedicine/testhelper/TravelMedicineTestHelperController.java @@ -5,7 +5,7 @@ package de.eshg.travelmedicine.testhelper; -import de.eshg.auditlog.SharedAuditLogTestHelperApi; +import de.eshg.auditlog.AuditLogClientTestHelperApi; import de.eshg.lib.auditlog.AuditLogTestHelperService; import de.eshg.testhelper.ConditionalOnTestHelperEnabled; import de.eshg.testhelper.TestHelperApi; @@ -19,7 +19,6 @@ import de.eshg.travelmedicine.testhelper.api.PostPopulateProcedureRequest; import de.eshg.travelmedicine.testhelper.api.PostPopulateProcedureResponse; import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; -import java.io.IOException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -28,7 +27,7 @@ import org.springframework.web.service.annotation.PostExchange; @RestController @ConditionalOnTestHelperEnabled public class TravelMedicineTestHelperController extends TestHelperController - implements SharedAuditLogTestHelperApi { + implements AuditLogClientTestHelperApi { public static final String TEST_POPULATION_PATH = "/population"; public static final String TEST_POPULATION_URL = TestHelperApi.BASE_URL + TEST_POPULATION_PATH; @@ -76,12 +75,7 @@ public class TravelMedicineTestHelperController extends TestHelperController } @Override - public void clearAuditLogStorageDirectory() throws IOException { - auditLogTestHelperService.clearAuditLogStorageDirectory(); - } - - @Override - public void runArchivingJob() { - auditLogTestHelperService.runArchivingJob(); + public void runAuditLogArchivingJob() { + auditLogTestHelperService.runAuditLogArchivingJob(); } } 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 6c0ca06e4..97669ebb4 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 @@ -146,22 +146,22 @@ public class PersonClient { .orElseThrow(() -> new BadRequestException("Reference person not found.")); boolean dataAdded = addEmailAndPhoneNumberToReferencePerson(referencePerson, personFromCentralFile); - PersonDetailsDto personDetailsDto = mapToPersonDetailsDto(referencePerson); + UpdatePersonRequest updatePersonRequest = mapToUpdatePersonRequest(referencePerson); AddPersonFileStateResponse addPersonFileStateResponse; if (dataAdded) { UpdateReferencePersonRequest updateReferencePersonRequest = - new UpdateReferencePersonRequest(personDetailsDto, referencePerson.version()); + new UpdateReferencePersonRequest(updatePersonRequest, referencePerson.version()); addPersonFileStateResponse = personApi.updateReferencePerson(referencePersonId, updateReferencePersonRequest); } else { - AddPersonFileStateRequest addPersonRequest = mapToAddPeronRequest(referencePerson); + AddPersonFileStateRequest addPersonRequest = mapToAddPersonRequest(referencePerson); addPersonFileStateResponse = personApi.addPersonFileState(addPersonRequest); } return addPersonFileStateResponse.id(); } - private AddPersonFileStateRequest mapToAddPeronRequest( + private AddPersonFileStateRequest mapToAddPersonRequest( GetReferencePersonResponse referencePerson) { return new AddPersonFileStateRequest( referencePerson.id(), @@ -339,8 +339,8 @@ public class PersonClient { personFromCentralFile.differentBillingAddress()); } - private PersonDetailsDto mapToPersonDetailsDto(GetReferencePersonResponse referencePerson) { - return new PersonDetailsDto( + private UpdatePersonRequest mapToUpdatePersonRequest(GetReferencePersonResponse referencePerson) { + return new UpdatePersonRequest( referencePerson.title(), referencePerson.salutation(), referencePerson.gender(), 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 b71ca6c77..5a018325b 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 @@ -153,7 +153,7 @@ public class VaccinationConsultationController { @GetMapping(path = "/{procedureId}" + DETAILS_URL) @Operation(summary = "Get vaccination consultation details") - @Transactional(readOnly = true) + @Transactional public GetVaccinationConsultationDetailsResponse getVaccinationConsultationDetails( @PathVariable("procedureId") UUID procedureId) { GetVaccinationConsultationDetailsResponse vaccinationConsultationDetails = diff --git a/backend/travel-medicine/src/main/resources/migrations/0057_add_auditlog_entry.xml b/backend/travel-medicine/src/main/resources/migrations/0057_add_auditlog_entry.xml new file mode 100644 index 000000000..123233c56 --- /dev/null +++ b/backend/travel-medicine/src/main/resources/migrations/0057_add_auditlog_entry.xml @@ -0,0 +1,55 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="1737664353337-1"> + <createSequence cacheSize="1" cycle="false" dataType="bigint" incrementBy="50" + maxValue="9223372036854775807" minValue="1" sequenceName="audit_log_entry_seq" + startValue="1"/> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-2"> + <createTable tableName="audit_log_entry"> + <column name="id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" primaryKeyName="pk_audit_log_entry"/> + </column> + <column name="version" type="BIGINT"> + <constraints nullable="false"/> + </column> + <column name="category" type="TEXT"> + <constraints nullable="false"/> + </column> + <column name="created_at" type="TIMESTAMP WITH TIME ZONE"> + <constraints nullable="false"/> + </column> + <column name="function" type="TEXT"> + <constraints nullable="false"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-3"> + <createTable tableName="audit_log_entry_additional_data"> + <column name="audit_log_entry_id" type="BIGINT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + <column name="additional_data" type="TEXT"/> + <column name="additional_data_key" type="TEXT"> + <constraints nullable="false" primaryKey="true" + primaryKeyName="pk_audit_log_entry_additional_data"/> + </column> + </createTable> + </changeSet> + <changeSet author="GA-Lotse" id="1737664353337-4"> + <addForeignKeyConstraint constraintName="fk_audit_log_entry_additional_data_audit_log_entry" + baseTableName="audit_log_entry_additional_data" baseColumnNames="audit_log_entry_id" + referencedTableName="audit_log_entry" referencedColumnNames="id" + deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" + onUpdate="NO ACTION" validate="true"/> + </changeSet> +</databaseChangeLog> + diff --git a/backend/travel-medicine/src/main/resources/migrations/0058_convert_duration_columns_to_interval.xml b/backend/travel-medicine/src/main/resources/migrations/0058_convert_duration_columns_to_interval.xml new file mode 100644 index 000000000..ce7f10863 --- /dev/null +++ b/backend/travel-medicine/src/main/resources/migrations/0058_convert_duration_columns_to_interval.xml @@ -0,0 +1,30 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<!-- + Copyright 2025 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="use_interval_for_durations"> + <sql> + ALTER TABLE appointment_type_config + ALTER COLUMN standard_duration_in_minutes TYPE interval second(6) + USING (standard_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_type_config" + oldColumnName="standard_duration_in_minutes" + newColumnName="standard_duration"/> + + <sql> + ALTER TABLE appointment_block_group + ALTER COLUMN slot_duration_in_minutes TYPE interval second(6) + USING (slot_duration_in_minutes * INTERVAL '1 minute'); + </sql> + <renameColumn tableName="appointment_block_group" + oldColumnName="slot_duration_in_minutes" + newColumnName="slot_duration"/> + </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 3a0fd7858..2788bc867 100644 --- a/backend/travel-medicine/src/main/resources/migrations/changelog.xml +++ b/backend/travel-medicine/src/main/resources/migrations/changelog.xml @@ -64,5 +64,7 @@ <include file="migrations/0054_make_appointment_block_users_non_nullable.xml"/> <include file="migrations/0055_add_cemetery_delete_at.xml"/> <include file="migrations/0056_add_previous_file_state_id_to_system_progress_entry.xml"/> + <include file="migrations/0057_add_auditlog_entry.xml"/> + <include file="migrations/0058_convert_duration_columns_to_interval.xml"/> </databaseChangeLog> diff --git a/backend/util-commons/gradle.lockfile b/backend/util-commons/gradle.lockfile index c1378d07d..7721f3d48 100644 --- a/backend/util-commons/gradle.lockfile +++ b/backend/util-commons/gradle.lockfile @@ -33,7 +33,7 @@ de.cronn:test-utils:1.1.1=testCompileClasspath,testRuntimeClasspath de.cronn:validation-file-assertions:0.8.0=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-commons:1.14.2=testCompileClasspath,testRuntimeClasspath io.micrometer:micrometer-observation:1.14.2=testCompileClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.27=testCompileClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.28=testCompileClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.1.3=testCompileClasspath,testRuntimeClasspath jakarta.annotation:jakarta.annotation-api:2.1.1=testCompileClasspath,testRuntimeClasspath jakarta.validation:jakarta.validation-api:3.0.2=testCompileClasspath,testRuntimeClasspath @@ -58,10 +58,10 @@ org.bouncycastle:bcutil-jdk18on:1.80=testRuntimeClasspath org.checkerframework:checker-qual:3.43.0=compileClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:2.2=testRuntimeClasspath org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.jacoco:org.jacoco.agent:0.8.11=jacocoAgent,jacocoAnt -org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt -org.jacoco:org.jacoco.core:0.8.11=jacocoAnt -org.jacoco:org.jacoco.report:0.8.11=jacocoAnt +org.jacoco:org.jacoco.agent:0.8.12=jacocoAgent,jacocoAnt +org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt +org.jacoco:org.jacoco.core:0.8.12=jacocoAnt +org.jacoco:org.jacoco.report:0.8.12=jacocoAnt org.jetbrains:annotations:17.0.0=testRuntimeClasspath org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath org.junit.jupiter:junit-jupiter-engine:5.11.4=testRuntimeClasspath @@ -75,9 +75,10 @@ org.mockito:mockito-core:5.14.2=testCompileClasspath,testRuntimeClasspath org.mockito:mockito-junit-jupiter:5.14.2=testCompileClasspath,testRuntimeClasspath org.objenesis:objenesis:3.3=testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.6=jacocoAnt -org.ow2.asm:asm-tree:9.6=jacocoAnt -org.ow2.asm:asm:9.6=jacocoAnt,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.7=jacocoAnt +org.ow2.asm:asm-tree:9.7=jacocoAnt +org.ow2.asm:asm:9.6=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7=jacocoAnt org.postgresql:postgresql:42.7.4=testRuntimeClasspath org.rnorth.duct-tape:duct-tape:1.0.8=testRuntimeClasspath org.skyscreamer:jsonassert:1.5.3=testCompileClasspath,testRuntimeClasspath diff --git a/build.gradle b/build.gradle index 4a9958f60..12e749843 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ dockerCompose { //CAUTION: run ./gradlew wrapper (twice) after changes to this task! //See https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper wrapper { - gradleVersion = '8.9' + gradleVersion = '8.12.1' distributionType = Wrapper.DistributionType.ALL } diff --git a/buildSrc/src/main/groovy/node.gradle b/buildSrc/src/main/groovy/node.gradle index 776d575db..de48161a8 100644 --- a/buildSrc/src/main/groovy/node.gradle +++ b/buildSrc/src/main/groovy/node.gradle @@ -4,7 +4,7 @@ plugins { node { version = '22.13.1' - pnpmVersion = '9.15.4' + pnpmVersion = '10.1.0' download = true workDir = file("${rootProject.projectDir}/.gradle/nodejs") pnpmWorkDir = file("${rootProject.projectDir}/.gradle/pnpm") diff --git a/buildSrc/src/main/groovy/openapi-generator.gradle b/buildSrc/src/main/groovy/openapi-generator.gradle deleted file mode 100644 index 11be10e5a..000000000 --- a/buildSrc/src/main/groovy/openapi-generator.gradle +++ /dev/null @@ -1,116 +0,0 @@ -import org.apache.commons.io.FilenameUtils -import org.openapitools.generator.gradle.plugin.tasks.GenerateTask -import de.eshg.frontend.OpenapiGeneratorPluginConfiguration - -import java.nio.file.Files - -plugins { - id 'workspace-package' -} - -def groupName = 'generated api'; -def generateTaskName = 'generateApi' -def srcDir = layout.projectDirectory.dir('src') - -def openapiGeneratorExtension = project.extensions.create('openapiGenerator', OpenapiGeneratorPluginConfiguration) - -def generateMetaTaskProvider = tasks.register(generateTaskName) { - group = groupName -} - -tasks.named('compile') { - dependsOn generateMetaTaskProvider - inputs.files("${rootDir}/config/tsconfig.lib-legacy.json") -} - -static String emulateSymlinkIfNecessary(java.nio.file.Path path) { - if (Files.isSymbolicLink(path)) { - return path.toString() - } else if (Files.isRegularFile(path)) { - // Try to emulate git symlinks on Windows - def lines = Files.readAllLines(path) - if (lines.size() == 1) { - String symlink = lines.get(0) - return path.getParent().resolve(symlink) - } else { - // Required for the opencode pipeline - return path.toString(); - } - } else { - throw new IllegalArgumentException("${path} is neither a symlink nor a regular file") - } -} - -project.afterEvaluate { - // We have multiple spec files in the project and create dynamic generate tasks for each. - // They are added as dependency to the meta generate task. - openapiGeneratorExtension.inputSpecs.get().each { apiName, specFile -> - { - def generatedApiDir = srcDir.file(apiName).asFile.path - def generateTaskProvider = tasks.register("${generateTaskName}_${apiName}", GenerateTask) { - group = groupName - dependsOn rootProject.tasks.installDependencies - generatorName = 'typescript-fetch' - inputSpec = emulateSymlinkIfNecessary(specFile.asFile.toPath()) - outputDir = generatedApiDir - modelNamePrefix = 'Api' - additionalProperties = [ - 'supportsES6' : 'true', - 'withInterfaces' : 'true', - 'useSingleRequestParameter' : 'false', - // We need the "modern" discriminator behavior for the discriminator columns. - // See https://blog.cronn.de/en/java/rest/2024/01/02/grand-cronn-pet-hotel.html - // However, it seems to be not as battle tested and thus it generates duplicated imports that we fix via fixDuplicateImports() - 'legacyDiscriminatorBehavior': 'false', - // Override default enum suffix 'Enum' - 'enumNameSuffix' : '' - ] - - doFirst { - delete generatedApiDir - } - - doLast { - // See the comment at the "legacyDiscriminatorBehavior" property - fixDuplicateImports(generatedApiDir) - } - } - - generateMetaTaskProvider.configure { - dependsOn generateTaskProvider - } - } - } -} - -// Fix/workaround for https://github.com/OpenAPITools/openapi-generator/issues/15637 -private void fixDuplicateImports(openApiOutputDir) { - fileTree("${openApiOutputDir}/models").matching { - include "**/*.ts" - }.visit { FileVisitDetails details -> - details.file.text = details.file.text.replaceAll(/(?m)import \{(\s*.*?FromJSONTyped,?)+\s*\} from '\.\/index';/, "// removed duplicate import here") - - // Self imports, needed when discriminator for self exists - def importName = FilenameUtils.removeExtension(details.file.name) - details.file.text = details.file.text.replaceAll(/(?m)import type \{\s+$importName\s+} from '\.\/$importName';/, "// removed self import here") - details.file.text = details.file.text.replaceAll(/(?m)import \{\s+(?:\w+(?:FromJSON|FromJSONTyped|ToJSON),\s+){3}} from '\.\/$importName';/, "// removed self import here") - } -} - -tasks.register('assemble') { - dependsOn 'compile' -} - -tasks.register('build') { - dependsOn 'check' - dependsOn 'assemble' -} - -def cleanGeneratedApiProvider = tasks.register('cleanGeneratedApi', Delete) { - group = groupName - delete srcDir -} - -tasks.named('clean').configure { - dependsOn cleanGeneratedApiProvider -} diff --git a/citizen-portal-api/.gitignore b/citizen-portal-api/.gitignore deleted file mode 100644 index 39f8bdd3b..000000000 --- a/citizen-portal-api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/** diff --git a/citizen-portal-api/build.gradle b/citizen-portal-api/build.gradle deleted file mode 100644 index d9315d2f6..000000000 --- a/citizen-portal-api/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id 'openapi-generator' -} - -def backendDir = rootProject.layout.projectDirectory.dir('backend') - -openapiGenerator { - inputSpecs = [ - 'businessProcedures' : backendDir.file('lib-procedures/openApi.json'), // TODO: use separate OpenAPI specification for citizen-portal - 'officialMedicalService' : backendDir.file('official-medical-service/openApi.json'), // TODO: use separate OpenAPI specification for citizen-portal - 'medicalRegistry' : backendDir.file('medical-registry/openApi.json'), // TODO: use separate OpenAPI specification for citizen-portal - 'openData' : backendDir.file('opendata/openApi.json'), // TODO: use separate OpenAPI specification for citizen-portal - ] -} diff --git a/citizen-portal-api/package.json b/citizen-portal-api/package.json deleted file mode 100644 index 2a2bee28f..000000000 --- a/citizen-portal-api/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@eshg/citizen-portal-api", - "version": "0.0.1", - "type": "module", - "private": true, - "exports": { - "./*": "./build/dist/src/*/index.js" - } -} diff --git a/citizen-portal-api/tsconfig.json b/citizen-portal-api/tsconfig.json deleted file mode 100644 index f3bf745a5..000000000 --- a/citizen-portal-api/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../config/tsconfig.lib-legacy.json" -} diff --git a/citizen-portal/gradleDependencies.json b/citizen-portal/gradleDependencies.json index 24f5ae485..d06419508 100644 --- a/citizen-portal/gradleDependencies.json +++ b/citizen-portal/gradleDependencies.json @@ -1,9 +1,12 @@ { "dependencies": [ ":base-api", - ":citizen-portal-api", ":lib-portal", + ":lib-procedures-api", ":measles-protection-api", + ":medical-registry-api", + ":official-medical-service-api", + ":opendata-api", ":school-entry-api", ":travel-medicine-api" ] diff --git a/citizen-portal/package.json b/citizen-portal/package.json index a977c658e..5c8b7d22d 100644 --- a/citizen-portal/package.json +++ b/citizen-portal/package.json @@ -7,9 +7,12 @@ "@emotion/react": "catalog:joy", "@emotion/styled": "catalog:joy", "@eshg/base-api": "workspace:*", - "@eshg/citizen-portal-api": "workspace:*", "@eshg/lib-portal": "workspace:*", + "@eshg/lib-procedures-api": "workspace:*", "@eshg/measles-protection-api": "workspace:*", + "@eshg/medical-registry-api": "workspace:*", + "@eshg/official-medical-service-api": "workspace:*", + "@eshg/opendata-api": "workspace:*", "@eshg/school-entry-api": "workspace:*", "@eshg/travel-medicine-api": "workspace:*", "@fontsource/poppins": "catalog:joy", diff --git a/citizen-portal/src/lib/baseModule/api/queries/gdpr.ts b/citizen-portal/src/lib/baseModule/api/queries/gdpr.ts index 431ed00cc..2e9031f54 100644 --- a/citizen-portal/src/lib/baseModule/api/queries/gdpr.ts +++ b/citizen-portal/src/lib/baseModule/api/queries/gdpr.ts @@ -3,15 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { STATIC_QUERY_OPTIONS } from "@eshg/lib-portal/api/queryOptions"; +import { PortalErrorCode } from "@eshg/lib-portal/errorHandling/PortalErrorCode"; +import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers"; import { ApiBusinessModule, ApiGdprDownloadPackageInfo, ApiGetGdprDownloadPackagesInfoResponse, GdprValidationTaskApiInterface, -} from "@eshg/citizen-portal-api/businessProcedures"; -import { STATIC_QUERY_OPTIONS } from "@eshg/lib-portal/api/queryOptions"; -import { PortalErrorCode } from "@eshg/lib-portal/errorHandling/PortalErrorCode"; -import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers"; +} from "@eshg/lib-procedures-api"; import { queryOptions, useSuspenseQueries } from "@tanstack/react-query"; import assert from "assert"; import { isDefined } from "remeda"; diff --git a/citizen-portal/src/lib/baseModule/components/gdpr/GdprProcedureStatusChip.tsx b/citizen-portal/src/lib/baseModule/components/gdpr/GdprProcedureStatusChip.tsx index c3eac31e4..7d93d9209 100644 --- a/citizen-portal/src/lib/baseModule/components/gdpr/GdprProcedureStatusChip.tsx +++ b/citizen-portal/src/lib/baseModule/components/gdpr/GdprProcedureStatusChip.tsx @@ -23,7 +23,6 @@ export function GdprProcedureStatusChip({ const gdprProcedureStatusColor = { [ApiGdprProcedureStatus.Draft]: "warning", - [ApiGdprProcedureStatus.Open]: "neutral", [ApiGdprProcedureStatus.InProgress]: "primary", [ApiGdprProcedureStatus.Closed]: "success", [ApiGdprProcedureStatus.Aborted]: "danger", diff --git a/citizen-portal/src/lib/businessModules/measlesProtection/components/reportCase/subforms/MinimalPersonForm.tsx b/citizen-portal/src/lib/businessModules/measlesProtection/components/reportCase/subforms/MinimalPersonForm.tsx index 726f51b3b..20854aff7 100644 --- a/citizen-portal/src/lib/businessModules/measlesProtection/components/reportCase/subforms/MinimalPersonForm.tsx +++ b/citizen-portal/src/lib/businessModules/measlesProtection/components/reportCase/subforms/MinimalPersonForm.tsx @@ -8,7 +8,10 @@ import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; -import { validateLength } from "@eshg/lib-portal/helpers/validators"; +import { + validateDateOfBirth, + validateLength, +} from "@eshg/lib-portal/helpers/validators"; import { Grid, Stack } from "@mui/joy"; import { @@ -69,6 +72,7 @@ export function MinimalPersonForm(props: Readonly<NestedFormProps>) { name={fieldName("dateOfBirth")} label={minimalPersonFormConfig.dateOfBirth.label} required={minimalPersonFormConfig.dateOfBirth.required} + validate={validateDateOfBirth} /> </Grid> </Grid> diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/api/clients.ts b/citizen-portal/src/lib/businessModules/medicalRegistry/api/clients.ts index 7ec4b4715..8147327de 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/api/clients.ts +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/api/clients.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; import { Configuration, MedicalRegistryApi, MedicalRegistryFeatureTogglesPublicApi, MedicalRegistryPublicCitizenApi, -} from "@eshg/citizen-portal-api/medicalRegistry"; -import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/medical-registry-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts b/citizen-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts index 81abf68f3..f05b51b01 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { CreateProcedureRequest } from "@eshg/citizen-portal-api/medicalRegistry/apis"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { CreateProcedureRequest } from "@eshg/medical-registry-api"; import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients"; import { useTranslation } from "@/lib/i18n/client"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/api/queries/featureTogglesApi.ts b/citizen-portal/src/lib/businessModules/medicalRegistry/api/queries/featureTogglesApi.ts index 0725066fb..03e6e0af8 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/api/queries/featureTogglesApi.ts +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/api/queries/featureTogglesApi.ts @@ -3,15 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiGetMedicalRegistryFeatureTogglesResponse, - ApiMedicalRegistryFeature, -} from "@eshg/citizen-portal-api/medicalRegistry"; import { selectDisabledOldFeature, selectEnabledNewFeature, useGetFeatureToggle, } from "@eshg/lib-portal/api/featureToggles"; +import { + ApiGetMedicalRegistryFeatureTogglesResponse, + ApiMedicalRegistryFeature, +} from "@eshg/medical-registry-api"; import { useFeatureTogglesApi } from "@/lib/businessModules/medicalRegistry/api/clients"; import { medicalRegistryFeatureTogglesPublicApiQueryKey } from "@/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/ProfessionalRegistrationStepper.tsx b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/ProfessionalRegistrationStepper.tsx index 6c39ef930..70dd6cdb1 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/ProfessionalRegistrationStepper.tsx +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/ProfessionalRegistrationStepper.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiTypeOfChange } from "@eshg/citizen-portal-api/medicalRegistry"; import { MedicalRegistryCreateProcedureFormValues } from "@eshg/lib-portal/businessModules/medicalRegistry/medicalRegistryCreateProcedureFormValues"; import { shouldEnable } from "@eshg/lib-portal/businessModules/medicalRegistry/sections"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; @@ -12,6 +11,7 @@ import { StepFactory, } from "@eshg/lib-portal/components/form/MultiStepForm"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Typography } from "@mui/joy"; import { FormikProps, useFormikContext } from "formik"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepFour.tsx b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepFour.tsx index 51316bd2c..e93b1d900 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepFour.tsx +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepFour.tsx @@ -3,10 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiCountryCode, - ApiTypeOfChange, -} from "@eshg/citizen-portal-api/medicalRegistry"; import { EmployeeInformationFormValues, MedicalRegistryCreateProcedureFormValues, @@ -15,6 +11,7 @@ import { import { shouldEnable } from "@eshg/lib-portal/businessModules/medicalRegistry/sections"; import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; import { validateFile } from "@eshg/lib-portal/helpers/validators"; +import { ApiCountryCode, ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Add, DeleteOutlined } from "@mui/icons-material"; import { Button, Grid, IconButton, Sheet, Stack, Typography } from "@mui/joy"; import { FieldArray, useFormikContext } from "formik"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepOne.tsx b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepOne.tsx index e55fca689..f2f4dcc9f 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepOne.tsx +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepOne.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiTypeOfChange } from "@eshg/citizen-portal-api/medicalRegistry"; import { changeTypeNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; import { GeneralInformationFormValues, @@ -21,6 +20,7 @@ import { validateLength, validatePastOrTodayDate, } from "@eshg/lib-portal/helpers/validators"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Grid, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; import { useMemo } from "react"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepTwo.tsx b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepTwo.tsx index 573c8f258..218b9e2fb 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepTwo.tsx +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/pages/professionalRegistrationForm/steps/ProfessionalRegistrationFormStepTwo.tsx @@ -3,11 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiEmploymentStatus, - ApiEmploymentType, - ApiTypeOfChange, -} from "@eshg/citizen-portal-api/medicalRegistry"; import { professionalTitleNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; import { MedicalRegistryCreateProcedureFormValues, @@ -24,6 +19,11 @@ import { validateLength, validatePastOrTodayDate, } from "@eshg/lib-portal/helpers/validators"; +import { + ApiEmploymentStatus, + ApiEmploymentType, + ApiTypeOfChange, +} from "@eshg/medical-registry-api"; import { Grid, Radio, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; import { useMemo } from "react"; diff --git a/citizen-portal/src/lib/businessModules/medicalRegistry/shared/navigationItems.tsx b/citizen-portal/src/lib/businessModules/medicalRegistry/shared/navigationItems.tsx index 6c0c7d54e..14438f1f3 100644 --- a/citizen-portal/src/lib/businessModules/medicalRegistry/shared/navigationItems.tsx +++ b/citizen-portal/src/lib/businessModules/medicalRegistry/shared/navigationItems.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiMedicalRegistryFeature } from "@eshg/citizen-portal-api/medicalRegistry"; +import { ApiMedicalRegistryFeature } from "@eshg/medical-registry-api"; import { StickyNote2Outlined } from "@mui/icons-material"; import { NavigationItem } from "@/lib/baseModule/components/layout/types"; diff --git a/citizen-portal/src/lib/businessModules/officialMedicalService/api/clients.ts b/citizen-portal/src/lib/businessModules/officialMedicalService/api/clients.ts index eaf67b8aa..256dec1fa 100644 --- a/citizen-portal/src/lib/businessModules/officialMedicalService/api/clients.ts +++ b/citizen-portal/src/lib/businessModules/officialMedicalService/api/clients.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; import { CitizenPublicApi, Configuration, -} from "@eshg/citizen-portal-api/officialMedicalService"; -import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/official-medical-service-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( diff --git a/citizen-portal/src/lib/businessModules/opendata/api/clients.ts b/citizen-portal/src/lib/businessModules/opendata/api/clients.ts index 4a7bb309a..e81b19d19 100644 --- a/citizen-portal/src/lib/businessModules/opendata/api/clients.ts +++ b/citizen-portal/src/lib/businessModules/opendata/api/clients.ts @@ -3,11 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - Configuration, - OpenDataPublicCitizenApi, -} from "@eshg/citizen-portal-api/openData"; import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +import { Configuration, OpenDataPublicCitizenApi } from "@eshg/opendata-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( diff --git a/citizen-portal/src/lib/businessModules/opendata/api/queries/citizenPublicApi.ts b/citizen-portal/src/lib/businessModules/opendata/api/queries/citizenPublicApi.ts index 594b01fe2..3ffd06619 100644 --- a/citizen-portal/src/lib/businessModules/opendata/api/queries/citizenPublicApi.ts +++ b/citizen-portal/src/lib/businessModules/opendata/api/queries/citizenPublicApi.ts @@ -3,20 +3,43 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { GetOpenDocuments1Request } from "@eshg/citizen-portal-api/openData"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; -import { useSuspenseQuery } from "@tanstack/react-query"; +import { + ApiGetOpenDocumentsResponse, + ApiVersion, + GetOpenDocuments1Request, +} from "@eshg/opendata-api"; +import { + InfiniteData, + useInfiniteQuery, + useSuspenseQuery, +} from "@tanstack/react-query"; import { useOpenDataPublicCitizenApi } from "@/lib/businessModules/opendata/api/clients"; import { publicCitizenApiQueryKey } from "@/lib/businessModules/opendata/api/queries/apiQueryKeys"; -export function useGetOpenDocuments(request: GetOpenDocuments1Request) { +interface GetOpenDataOverviewResponse { + versions: ApiVersion[]; + totalElements: number; + totalPages: number; +} + +export function useGetOpenDocuments( + request: Omit<GetOpenDocuments1Request, "pageNumber">, +) { const openDataApi = useOpenDataPublicCitizenApi(); - return useSuspenseQuery({ + return useInfiniteQuery({ queryKey: publicCitizenApiQueryKey(["getOpenDocuments1Raw", request]), - queryFn: () => - openDataApi.getOpenDocuments1Raw(request).then(unwrapRawResponse), + queryFn: ({ pageParam }) => + openDataApi + .getOpenDocuments1Raw({ ...request, pageNumber: pageParam }) + .then(unwrapRawResponse), + initialPageParam: 0, + getNextPageParam: (lastPage, pages) => { + return pages.length < lastPage.totalPages ? pages.length : null; + }, + select: mapToOpenDataOverview, }); } @@ -28,3 +51,16 @@ export function useGetVersion(versionId: string) { queryFn: () => openDataApi.getVersion1(versionId), }); } + +export function mapToOpenDataOverview( + data: InfiniteData<ApiGetOpenDocumentsResponse, number>, +) { + return { + versions: + data.pages + .flatMap((page) => page.elements) + .flatMap(({ versions }) => versions) ?? [], + totalElements: data.pages[0]?.totalElements ?? 0, + totalPages: data.pages[0]?.totalPages ?? 0, + } satisfies GetOpenDataOverviewResponse; +} diff --git a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataDetailsContent.tsx b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataDetailsContent.tsx index cb36a0002..23cf3026a 100644 --- a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataDetailsContent.tsx +++ b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataDetailsContent.tsx @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiVersion } from "@eshg/citizen-portal-api/openData"; import { useFileDownload } from "@eshg/lib-portal/api/files/download"; import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink"; import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { formatFileSize } from "@eshg/lib-portal/helpers/file"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { ApiVersion } from "@eshg/opendata-api"; import { DownloadOutlined, FileCopyOutlined } from "@mui/icons-material"; import { Button, Card, Chip, Stack, Typography } from "@mui/joy"; import { visuallyHidden } from "@mui/utils"; diff --git a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataFilters.tsx b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataFilters.tsx index 768552474..dad4a546c 100644 --- a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataFilters.tsx +++ b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataFilters.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiBusinessModule } from "@eshg/citizen-portal-api/openData"; import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; import { SelectOptions, @@ -11,6 +10,7 @@ import { } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { YearInput } from "@eshg/lib-portal/components/inputs/YearInput"; import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; +import { ApiBusinessModule } from "@eshg/opendata-api"; import { CloseOutlined, DateRangeOutlined, diff --git a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataOverviewContent.tsx b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataOverviewContent.tsx index c36eef005..598c6fb22 100644 --- a/citizen-portal/src/lib/businessModules/opendata/components/OpenDataOverviewContent.tsx +++ b/citizen-portal/src/lib/businessModules/opendata/components/OpenDataOverviewContent.tsx @@ -3,24 +3,30 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiVersion } from "@eshg/citizen-portal-api/openData"; import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink"; -import { parseOptionalNonNegativeInt } from "@eshg/lib-portal/helpers/searchParams"; -import { ChevronRightOutlined } from "@mui/icons-material"; -import { Card, CardContent, List, ListItem, Stack, Typography } from "@mui/joy"; -import { useSearchParams } from "next/navigation"; -import { isEmpty } from "remeda"; +import { ApiVersion } from "@eshg/opendata-api"; +import { ChevronRightOutlined, RefreshOutlined } from "@mui/icons-material"; +import { + Button, + Card, + CardContent, + List, + ListItem, + Stack, + Typography, +} from "@mui/joy"; +import { startTransition } from "react"; +import { Trans } from "react-i18next"; +import { isDefined, isEmpty } from "remeda"; import { theme } from "@/lib/baseModule/theme/theme"; import { useGetOpenDocuments } from "@/lib/businessModules/opendata/api/queries/citizenPublicApi"; import { OpenDataFilters } from "@/lib/businessModules/opendata/components/OpenDataFilters"; -import { SEARCH_PARAMS } from "@/lib/businessModules/opendata/components/helpers"; import { useOpenDataFilterValues } from "@/lib/businessModules/opendata/components/useOpenDataFilterValues"; import { fileTypeNames } from "@/lib/businessModules/opendata/shared/constants"; import { useCitizenRoutes } from "@/lib/businessModules/opendata/shared/routes"; import { useTranslation } from "@/lib/i18n/client"; import { MobileBreakpoint, byBreakpoint } from "@/lib/shared/breakpoints"; -import { Pagination } from "@/lib/shared/components/Pagination"; import { PageContent } from "@/lib/shared/components/layout/PageContent"; import { ContentSheet, @@ -28,7 +34,6 @@ import { } from "@/lib/shared/components/layout/contentSheet"; import { GridColumnStack } from "@/lib/shared/components/layout/grid"; import { PageTitle } from "@/lib/shared/components/layout/page"; -import { useReplaceSearchParams } from "@/lib/shared/hooks/searchParams/useReplaceSearchParams"; import { useIsMobile } from "@/lib/shared/hooks/useIsMobile"; const PAGE_SIZE = 10; @@ -37,20 +42,14 @@ export function OpenDataOverviewContent() { const { t } = useTranslation(["opendata/overview"]); const isMobile = useIsMobile(); - const { pageNumber, setPageNumber } = usePagination(); - const filterValues = useOpenDataFilterValues(); - const { - data: { totalElements, elements }, - } = useGetOpenDocuments({ - pageNumber, + const { data, fetchNextPage, hasNextPage, isFetching } = useGetOpenDocuments({ pageSize: PAGE_SIZE, searchString: filterValues.search, sourcesFilter: filterValues.topic, fileTypeFilter: filterValues.fileType, statisticsYearFilter: filterValues.year, }); - const versions = elements.flatMap(({ versions }) => versions); return ( <PageContent> @@ -70,30 +69,55 @@ export function OpenDataOverviewContent() { </Stack> )} <Typography level="h3" sx={{ marginTop: 1 }}> - {totalElements === 1 - ? t("resultSection.foundSingular", { totalElements }) - : t("resultSection.foundPlural", { totalElements })} + {t("resultSection.found", { + count: data?.totalElements ?? 0, + })} </Typography> - <List - sx={{ padding: 0, gap: "inherit" }} - aria-label={t("resultSection.results")} - > - {versions.map((version) => ( - <ListItem key={version.externalId} sx={{ padding: 0 }}> - <OpenDataCard version={version} /> - </ListItem> - ))} - </List> - - {/* TODO: ISSUE-7233: replace pagination with "Show more" button */} - {totalElements > PAGE_SIZE && ( - <Pagination - totalCount={totalElements} - pageSize={PAGE_SIZE} - pageNumber={pageNumber} - onPageChange={setPageNumber} - /> + {isDefined(data) && ( + <> + <List + sx={{ padding: 0, gap: "inherit" }} + aria-label={t("resultSection.results")} + > + {data.versions.map((version) => ( + <ListItem key={version.externalId} sx={{ padding: 0 }}> + <OpenDataCard version={version} /> + </ListItem> + ))} + </List> + + <Stack gap={2} alignItems="center"> + {hasNextPage && ( + <Button + loading={isFetching} + variant="soft" + endDecorator={<RefreshOutlined />} + sx={{ margin: "auto" }} + onClick={() => { + startTransition(async () => { + await fetchNextPage(); + }); + }} + > + {t("pagination.loadMore")} + </Button> + )} + <Typography + sx={{ + color: (theme) => theme.palette.text.primary, + }} + > + <Trans + i18nKey="opendata/overview:pagination.shown" + count={data.totalElements} + values={{ + shownElements: data.versions.length, + }} + ></Trans> + </Typography> + </Stack> + </> )} </ContentSheet> </GridColumnStack> @@ -154,19 +178,3 @@ function OpenDataCard({ version }: { version: ApiVersion }) { </Card> ); } - -function usePagination() { - const searchParams = useSearchParams(); - const replaceSearchParams = useReplaceSearchParams(); - const pageNumber = - parseOptionalNonNegativeInt(searchParams.get(SEARCH_PARAMS.pageNumber)) ?? - 0; - - function setPageNumber(newPageNumber: number) { - replaceSearchParams([ - { name: SEARCH_PARAMS.pageNumber, value: newPageNumber }, - ]); - } - - return { pageNumber, setPageNumber }; -} diff --git a/citizen-portal/src/lib/businessModules/opendata/components/useOpenDataFilterValues.ts b/citizen-portal/src/lib/businessModules/opendata/components/useOpenDataFilterValues.ts index 8fbb279d1..d9a2ec972 100644 --- a/citizen-portal/src/lib/businessModules/opendata/components/useOpenDataFilterValues.ts +++ b/citizen-portal/src/lib/businessModules/opendata/components/useOpenDataFilterValues.ts @@ -4,8 +4,8 @@ */ import { ApiBusinessModule } from "@eshg/base-api"; -import { ApiOpenDataFileType } from "@eshg/citizen-portal-api/openData"; import { parseOptionalEnum } from "@eshg/lib-portal/helpers/searchParams"; +import { ApiOpenDataFileType } from "@eshg/opendata-api"; import { useSearchParams } from "next/navigation"; import { isNonNullish } from "remeda"; diff --git a/citizen-portal/src/lib/businessModules/opendata/locales/de/overview.json b/citizen-portal/src/lib/businessModules/opendata/locales/de/overview.json index f11a5501b..65985438d 100644 --- a/citizen-portal/src/lib/businessModules/opendata/locales/de/overview.json +++ b/citizen-portal/src/lib/businessModules/opendata/locales/de/overview.json @@ -16,8 +16,13 @@ }, "resultSection": { "results": "Ergebnisse", - "foundSingular": "{{totalElements}} Ergebnis gefunden", - "foundPlural": "{{totalElements}} Ergebnisse gefunden", + "found_one": "{{count}} Ergebnis gefunden", + "found_other": "{{count}} Ergebnisse gefunden", "version": "Version" + }, + "pagination": { + "loadMore": "Mehr anzeigen", + "shown_one": "<strong>{{shownElements}}</strong> von <strong>{{count}}</strong> Ergebnis werden angezeigt", + "shown_other": "<strong>{{shownElements}}</strong> von <strong>{{count}}</strong> Ergebnissen werden angezeigt" } } diff --git a/citizen-portal/src/lib/businessModules/opendata/locales/en/overview.json b/citizen-portal/src/lib/businessModules/opendata/locales/en/overview.json index 399ff6eb9..bf06e5247 100644 --- a/citizen-portal/src/lib/businessModules/opendata/locales/en/overview.json +++ b/citizen-portal/src/lib/businessModules/opendata/locales/en/overview.json @@ -16,8 +16,13 @@ }, "resultSection": { "results": "Results", - "foundSingular": "{{totalElements}} result found", - "foundPlural": "{{totalElements}} results found", + "found_one": "{{count}} result found", + "found_other": "{{count}} results found", "version": "Version" + }, + "pagination": { + "loadMore": "Show more", + "shown_one": "<strong>{{shownElements}}</strong> of <strong>{{count}}</strong> result is shown", + "shown_other": "<strong>{{shownElements}}</strong> of <strong>{{count}}</strong> results are shown" } } diff --git a/citizen-portal/src/lib/businessModules/opendata/shared/constants.ts b/citizen-portal/src/lib/businessModules/opendata/shared/constants.ts index 52f189956..e257dac50 100644 --- a/citizen-portal/src/lib/businessModules/opendata/shared/constants.ts +++ b/citizen-portal/src/lib/businessModules/opendata/shared/constants.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiOpenDataFileType } from "@eshg/citizen-portal-api/openData"; +import { ApiOpenDataFileType } from "@eshg/opendata-api"; export const fileTypeNames = { [ApiOpenDataFileType.Csv]: "CSV", diff --git a/citizen-portal/src/lib/shared/api/clients.ts b/citizen-portal/src/lib/shared/api/clients.ts index 3ba677568..8e413fa98 100644 --- a/citizen-portal/src/lib/shared/api/clients.ts +++ b/citizen-portal/src/lib/shared/api/clients.ts @@ -4,16 +4,16 @@ */ import { Configuration, DepartmentApi, PublicConfigApi } from "@eshg/base-api"; +import { + ApiConfiguration, + useApiConfiguration, +} from "@eshg/lib-portal/api/ApiProvider"; import { ApiBusinessModule, Configuration as BusinessModuleConfiguration, ConfigurationParameters, GdprValidationTaskApi, -} from "@eshg/citizen-portal-api/businessProcedures"; -import { - ApiConfiguration, - useApiConfiguration, -} from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/lib-procedures-api"; export const BUSINESS_MODULE_URLS = { [ApiBusinessModule.SchoolEntry]: "PUBLIC_SCHOOL_ENTRY_BACKEND_URL", diff --git a/config/eslint.next.js b/config/eslint.next.js index 33d2aa2b5..6c9543b88 100644 --- a/config/eslint.next.js +++ b/config/eslint.next.js @@ -95,6 +95,11 @@ function noRestrictedImportsRuleOptions(packageType) { importNames: ["Form"], message: "Use 'FormPlus' from '@eshg/lib-portal' instead.", }, + { + name: "zustand/middleware", + importNames: ["devtools"], + message: "Remove before committing.", + }, ], }; } diff --git a/config/vitest.base.ts b/config/vitest.base.ts index 87257ace1..f445badb9 100644 --- a/config/vitest.base.ts +++ b/config/vitest.base.ts @@ -8,7 +8,7 @@ import tsconfigPaths from "vite-tsconfig-paths"; import { UserConfigExport, configDefaults } from "vitest/config"; export const VITEST_OUT_DIR = "./build/vitest"; -export const VITEST_COVERAGE_EXCLUDES = ["**/*.tsx", "**/*.d.ts"]; +export const VITEST_COVERAGE_EXCLUDES = ["**/*.d.ts"]; // https://vitejs.dev/config/ export const VITEST_BASE_CONFIG: UserConfigExport = { @@ -16,14 +16,15 @@ export const VITEST_BASE_CONFIG: UserConfigExport = { test: { exclude: configDefaults.exclude, environment: "node", - reporters: ["basic"], + reporters: ["default"], globals: true, coverage: { provider: "istanbul", all: true, reportsDirectory: `${VITEST_OUT_DIR}/coverage`, - reporter: ["html","lcov"], + reporter: ["html", "lcov"], include: ["src/**/*"], exclude: VITEST_COVERAGE_EXCLUDES, - },} + }, + }, }; diff --git a/docs/frontend.adoc b/docs/frontend.adoc index 50ca1ba4c..f6a7f80d5 100644 --- a/docs/frontend.adoc +++ b/docs/frontend.adoc @@ -58,8 +58,9 @@ The Lib Portal serves as shared library for components that can be used in all s === Api Clients -The portal applications depend on generated api clients for fetching data from the backend services. -For example, the `employee-portal-api` folder contains a module that generates all needed api clients for the `employee-portal` from OpenApi specifications. The api clients are automatically generated when starting the application. +The portal applications and business module library packages depend on generated api clients for fetching data from the backend services. +For example, the `packages/dental-api` folder contains a module that generates the api client for the `packages/dental` package from a OpenAPI specification. +The api clients are automatically generated when starting the application (or running any other gradle task depending on them). === Naming Convention diff --git a/employee-portal-api/.gitignore b/employee-portal-api/.gitignore deleted file mode 100644 index 39f8bdd3b..000000000 --- a/employee-portal-api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/** diff --git a/employee-portal-api/build.gradle b/employee-portal-api/build.gradle deleted file mode 100644 index 7d45fc5d0..000000000 --- a/employee-portal-api/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id 'openapi-generator' -} - -def backendDir = rootProject.layout.projectDirectory.dir('backend') - -openapiGenerator { - inputSpecs = [ - 'auditlog' : backendDir.file('auditlog/openApi.json'), - 'businessProcedures' : backendDir.file('lib-procedures/openApi.json'), - 'libEditor' : backendDir.file('lib-editor/openApi.json'), - 'libStatistics' : backendDir.file('lib-statistics/openApi.json'), - 'inspection' : backendDir.file('inspection/openApi.json'), - 'opendata' : backendDir.file('opendata/openApi.json'), - 'chatManagement' : backendDir.file('chat-management/openApi.json'), - 'stiProtection' : backendDir.file('sti-protection/openApi.json'), - 'medicalRegistry' : backendDir.file('medical-registry/openApi.json'), - 'officialMedicalService': backendDir.file('official-medical-service/openApi.json') - ] -} diff --git a/employee-portal-api/package.json b/employee-portal-api/package.json deleted file mode 100644 index 78f2b564d..000000000 --- a/employee-portal-api/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@eshg/employee-portal-api", - "version": "0.0.1", - "type": "module", - "private": true, - "exports": { - "./*": "./build/dist/src/*/index.js" - } -} diff --git a/employee-portal-api/tsconfig.json b/employee-portal-api/tsconfig.json deleted file mode 100644 index f3bf745a5..000000000 --- a/employee-portal-api/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../config/tsconfig.lib-legacy.json" -} diff --git a/employee-portal/gradleDependencies.json b/employee-portal/gradleDependencies.json index 0d59661c6..7bc579885 100644 --- a/employee-portal/gradleDependencies.json +++ b/employee-portal/gradleDependencies.json @@ -1,14 +1,23 @@ { "dependencies": [ + ":auditlog-api", ":base-api", + ":chat-management-api", ":dental", ":dental-api", - ":employee-portal-api", + ":inspection-api", + ":lib-editor-api", ":lib-employee-portal", ":lib-portal", + ":lib-procedures-api", + ":lib-statistics-api", ":measles-protection-api", + ":medical-registry-api", + ":official-medical-service-api", + ":opendata-api", ":school-entry-api", ":statistics-api", + ":sti-protection-api", ":travel-medicine-api" ] } diff --git a/employee-portal/package.json b/employee-portal/package.json index 0a36af8f4..780032375 100644 --- a/employee-portal/package.json +++ b/employee-portal/package.json @@ -7,15 +7,24 @@ "@ducanh2912/next-pwa": "10.2.9", "@emotion/react": "catalog:joy", "@emotion/styled": "catalog:joy", + "@eshg/auditlog-api": "workspace:*", "@eshg/base-api": "workspace:*", + "@eshg/chat-management-api": "workspace:*", "@eshg/dental": "workspace:*", "@eshg/dental-api": "workspace:*", - "@eshg/employee-portal-api": "workspace:*", + "@eshg/inspection-api": "workspace:*", + "@eshg/lib-editor-api": "workspace:*", "@eshg/lib-employee-portal": "workspace:*", "@eshg/lib-portal": "workspace:*", + "@eshg/lib-procedures-api": "workspace:*", + "@eshg/lib-statistics-api": "workspace:*", "@eshg/measles-protection-api": "workspace:*", + "@eshg/medical-registry-api": "workspace:*", + "@eshg/official-medical-service-api": "workspace:*", + "@eshg/opendata-api": "workspace:*", "@eshg/school-entry-api": "workspace:*", "@eshg/statistics-api": "workspace:*", + "@eshg/sti-protection-api": "workspace:*", "@eshg/travel-medicine-api": "workspace:*", "@fontsource/poppins": "catalog:joy", "@fontsource/source-code-pro": "catalog:joy", diff --git a/employee-portal/src/app/(baseModule)/gdpr/validation-tasks/[businessModule]/overview/page.tsx b/employee-portal/src/app/(baseModule)/gdpr/validation-tasks/[businessModule]/overview/page.tsx index d2fd223dd..f96fbb7c2 100644 --- a/employee-portal/src/app/(baseModule)/gdpr/validation-tasks/[businessModule]/overview/page.tsx +++ b/employee-portal/src/app/(baseModule)/gdpr/validation-tasks/[businessModule]/overview/page.tsx @@ -5,15 +5,15 @@ "use client"; -import { - ApiGdprValidationTaskSortKey, - ApiSortDirection, - GetAllGdprValidationTasksRequest, -} from "@eshg/employee-portal-api/businessProcedures"; import { parseOptionalEnum, parseReadonlyPageParams, } from "@eshg/lib-portal/helpers/searchParams"; +import { + ApiGdprValidationTaskSortKey, + ApiSortDirection, + GetAllGdprValidationTasksRequest, +} from "@eshg/lib-procedures-api"; import { ReadonlyURLSearchParams, useSearchParams } from "next/navigation"; import { ValidationTasksTable } from "@/lib/baseModule/components/gdpr/validationTasks/ValidationTasksTable"; diff --git a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx index f9d8d6751..1c67c4c5c 100644 --- a/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx +++ b/employee-portal/src/app/(baseModule)/inbox-procedures/page.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiInboxProcedure } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiInboxProcedure } from "@eshg/lib-procedures-api"; import { useState } from "react"; import { useCreateInboxProcedure } from "@/lib/baseModule/api/mutations/inboxProcedures"; diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/inspection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/inspection/page.tsx index 8994c177d..a0f805067 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/inspection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/inspection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/measles-protection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/measles-protection/page.tsx index 2175e03cb..b2b9f101e 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/measles-protection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/measles-protection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/medical-registry/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/medical-registry/page.tsx index 76c64936a..49520a800 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/medical-registry/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/medical-registry/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/school-entry/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/school-entry/page.tsx index 19f27a633..0a26e7c6a 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/school-entry/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/school-entry/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/sti-protection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/sti-protection/page.tsx index c425118a2..edba4a613 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/sti-protection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/sti-protection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/travel-medicine/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/travel-medicine/page.tsx index 776c1273b..690273526 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/travel-medicine/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving-admin/travel-medicine/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance, diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/inspection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/inspection/page.tsx index 8b2de4d07..03832b2fc 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/inspection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/inspection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/inspection/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/measles-protection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/measles-protection/page.tsx index 92a3ec5a9..dce64cd80 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/measles-protection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/measles-protection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/measlesProtection/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/medical-registry/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/medical-registry/page.tsx index 2b107cb11..a70f5c6ef 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/medical-registry/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/medical-registry/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/medicalRegistry/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/school-entry/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/school-entry/page.tsx index e88d46b0f..b8e01743f 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/school-entry/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/school-entry/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/schoolEntry/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/sti-protection/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/sti-protection/page.tsx index 152f5097c..ffdeb8268 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/sti-protection/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/sti-protection/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/stiProtection/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/(archiving)/archiving/travel-medicine/page.tsx b/employee-portal/src/app/(businessModules)/(archiving)/archiving/travel-medicine/page.tsx index f0a2f3570..32b7510f6 100644 --- a/employee-portal/src/app/(businessModules)/(archiving)/archiving/travel-medicine/page.tsx +++ b/employee-portal/src/app/(businessModules)/(archiving)/archiving/travel-medicine/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useBulkUpdateProceduresArchivingRelevance } from "@/lib/businessModules/travelMedicine/api/mutations/archiving"; import { diff --git a/employee-portal/src/app/(businessModules)/dental/children/[childId]/examinations/[examinationId]/page.tsx b/employee-portal/src/app/(businessModules)/dental/children/[childId]/examinations/[examinationId]/page.tsx index f776e4acc..b791d0bab 100644 --- a/employee-portal/src/app/(businessModules)/dental/children/[childId]/examinations/[examinationId]/page.tsx +++ b/employee-portal/src/app/(businessModules)/dental/children/[childId]/examinations/[examinationId]/page.tsx @@ -5,29 +5,17 @@ "use client"; -import { - ApiExaminationResult, - UpdateExaminationRequest, -} from "@eshg/dental-api"; -import { Examination } from "@eshg/dental/api/models/Examination"; -import { useUpdateExamination } from "@eshg/dental/api/mutations/childApi"; import { getExaminationQuery } from "@eshg/dental/api/queries/childApi"; import { useDentalApi } from "@eshg/dental/shared/DentalProvider"; -import { - mapOptionalValue, - mapRequiredValue, -} from "@eshg/lib-portal/helpers/form"; import { useSuspenseQuery } from "@tanstack/react-query"; import { DentalChildPageProps } from "@/app/(businessModules)/dental/children/[childId]/layout"; import { ChildExaminationForm } from "@/lib/businessModules/dental/features/children/details/ChildExaminationForm"; import { AdditionalInformationFormSection } from "@/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection"; -import { - ExaminationFormLayout, - ExaminationFormValues, - mapToExaminationFormValues, -} from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; +import { ExaminationFormLayout } from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; import { NoteFormSection } from "@/lib/businessModules/dental/features/examinations/NoteFormSection"; +import { DentalExaminationFormSection } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection"; +import { DentalExaminationStoreProvider } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; export default function ExaminationDetailsPage(props: DentalChildPageProps) { const { childApi } = useDentalApi(); @@ -35,73 +23,25 @@ export default function ExaminationDetailsPage(props: DentalChildPageProps) { const { data: examination } = useSuspenseQuery( getExaminationQuery(childApi, examinationId), ); - const updateExamination = useUpdateExamination(examinationId); - - async function handleSubmit(values: ExaminationFormValues) { - await updateExamination.mutateAsync( - mapToRequest(examination, values, examination.version), - ); - } return ( - <ChildExaminationForm - initialValues={mapToExaminationFormValues( - examination.result, - examination.note, - )} - onSubmit={handleSubmit} - > - <ExaminationFormLayout - additionalInformation={ - <AdditionalInformationFormSection - screening={examination.screening} - fluoridation={examination.fluoridation} - fluoridationConsentGiven={examination.fluoridationConsentGiven} - /> - } - note={<NoteFormSection />} - /> - </ChildExaminationForm> + <DentalExaminationStoreProvider examinationResult={examination.result}> + <ChildExaminationForm examination={examination}> + <ExaminationFormLayout + additionalInformation={ + <AdditionalInformationFormSection + screening={examination.screening} + fluoridation={examination.fluoridation} + fluoridationConsentGiven={examination.fluoridationConsentGiven} + status={examination.status} + /> + } + dentalExamination={ + examination.screening ? <DentalExaminationFormSection /> : undefined + } + note={<NoteFormSection />} + /> + </ChildExaminationForm> + </DentalExaminationStoreProvider> ); } - -function mapToRequest( - examination: Examination, - formValues: ExaminationFormValues, - version: number, -): UpdateExaminationRequest { - return { - examinationId: examination.id, - apiUpdateExaminationRequest: { - version, - note: mapOptionalValue(formValues.note), - result: mapExaminationResultRequest(examination, formValues), - }, - }; -} - -function mapExaminationResultRequest( - examination: Examination, - formValues: ExaminationFormValues, -): ApiExaminationResult | undefined { - if (examination.screening) { - return { - type: "ScreeningExaminationResult", - oralHygieneStatus: mapOptionalValue(formValues.oralHygieneStatus), - fluorideVarnishApplied: - mapOptionalValue(formValues.fluorideVarnishApplied) ?? false, - toothDiagnoses: [], - }; - } - - if (examination.fluoridation) { - return { - type: "FluoridationExaminationResult", - fluorideVarnishApplied: mapRequiredValue( - formValues.fluorideVarnishApplied, - ), - }; - } - - return undefined; -} diff --git a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/details/page.tsx b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/details/page.tsx index bbd6bd6cd..de8cd6c22 100644 --- a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/details/page.tsx +++ b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/details/page.tsx @@ -8,7 +8,8 @@ import { routes } from "@eshg/dental/shared/routes"; import { ProphylaxisSessionDetails } from "@/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionDetails"; -import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; +import { useSyncOutgoingProphylaxisSessionChanges } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncOutgoingProphylaxisSessionChanges"; import { MainContentLayout } from "@/lib/shared/components/layout/MainContentLayout"; import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout"; import { Toolbar } from "@/lib/shared/components/layout/Toolbar"; @@ -19,6 +20,8 @@ export default function ProphylaxisSessionDetailsPage() { ); const groupName = useProphylaxisSessionStore((state) => state.groupName); + useSyncOutgoingProphylaxisSessionChanges(); + return ( <StickyToolbarLayout toolbar={ diff --git a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/examinations/[participantIndex]/page.tsx b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/examinations/[participantIndex]/page.tsx index 3b8ba9679..a57c61e46 100644 --- a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/examinations/[participantIndex]/page.tsx +++ b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/examinations/[participantIndex]/page.tsx @@ -5,25 +5,11 @@ "use client"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { isDefined } from "remeda"; import * as v from "valibot"; -import { AdditionalInformationFormSection } from "@/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection"; -import { ExaminationFormLayout } from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; -import { NoteFormSection } from "@/lib/businessModules/dental/features/examinations/NoteFormSection"; -import { ProphylaxisSessionExaminationBottomBar } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationBottomBar"; -import { ProphylaxisSessionExaminationForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationForm"; -import { ProphylaxisSessionExaminationToolbar } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationToolbar"; -import { ADDITIONAL_INFO_FORM_COMPONENTS } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/formComponents"; -import { useProphylaxisSessionExaminationForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationForm"; -import { useProphylaxisSessionExaminationNavigation } from "@/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationNavigation"; -import { - useFilteredParticipants, - useProphylaxisSessionStore, -} from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; -import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout"; +import { DentalExaminationStoreProvider } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { ParticipantExaminationPage } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationPage"; +import { useFilteredParticipants } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; const PageParamsSchema = v.object({ participantIndex: v.pipe( @@ -52,72 +38,13 @@ export default function ProphylaxisSessionExaminationPage( ); } - const router = useRouter(); - const prophylaxisSessionId = useProphylaxisSessionStore((state) => state.id); - const isScreening = useProphylaxisSessionStore((state) => state.isScreening); - const fluoridationVarnish = useProphylaxisSessionStore( - (state) => state.fluoridationVarnish, - ); - const setExaminationResult = useProphylaxisSessionStore( - (state) => state.setExaminationResult, - ); - - const [nextRoute, setNextRoute] = useState<string>(); - const examinationForm = useProphylaxisSessionExaminationForm({ - examinationResult: participant.result, - note: participant.note, - onSubmit: (examinationResult) => { - setExaminationResult(participant.childId, examinationResult); - if (isDefined(nextRoute)) { - setNextRoute(undefined); - router.push(nextRoute); - } - }, - }); - const examinationNavigation = useProphylaxisSessionExaminationNavigation({ - participantIndex, - participantsLength: filteredParticipants.length, - onNavigate: (nextRoute) => { - setNextRoute(nextRoute); - void examinationForm.submitForm(); - }, - }); - return ( - <StickyToolbarLayout - toolbar={ - <ProphylaxisSessionExaminationToolbar - prophylaxisSessionId={prophylaxisSessionId} - participant={participant} - participantIndex={participantIndex} - onBackClicked={examinationNavigation.gotoOverview} - /> - } - > - <ProphylaxisSessionExaminationForm - form={examinationForm} - bottomBar={ - <ProphylaxisSessionExaminationBottomBar - onPreviousParticipantClicked={ - examinationNavigation.gotoPreviousParticipant - } - onNextParticipantClicked={examinationNavigation.gotoNextParticipant} - onOverviewClicked={examinationNavigation.gotoOverview} - /> - } - > - <ExaminationFormLayout - additionalInformation={ - <AdditionalInformationFormSection - screening={isScreening} - fluoridation={isDefined(fluoridationVarnish)} - fluoridationConsentGiven={participant.fluoridationConsentGiven} - components={ADDITIONAL_INFO_FORM_COMPONENTS} - /> - } - note={<NoteFormSection />} - /> - </ProphylaxisSessionExaminationForm> - </StickyToolbarLayout> + <DentalExaminationStoreProvider examinationResult={participant.result}> + <ParticipantExaminationPage + participant={participant} + participantIndex={participantIndex} + participantsLength={filteredParticipants.length} + /> + </DentalExaminationStoreProvider> ); } diff --git a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/layout.tsx b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/layout.tsx index 806458a00..ead951ef2 100644 --- a/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/layout.tsx +++ b/employee-portal/src/app/(businessModules)/dental/prophylaxis-sessions/[prophylaxisSessionId]/layout.tsx @@ -10,7 +10,7 @@ import { useDentalApi } from "@eshg/dental/shared/DentalProvider"; import { useSuspenseQuery } from "@tanstack/react-query"; import { PropsWithChildren } from "react"; -import { ProphylaxisSessionStoreProvider } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; +import { ProphylaxisSessionStoreProvider } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; export type ProphylaxisSessionPageProps = Readonly<{ params: ProphylaxisSessionPageParams; diff --git a/employee-portal/src/app/(businessModules)/inspection/procedures/(subpages)/[id]/reportresult/edit/[reportId]/layout.tsx b/employee-portal/src/app/(businessModules)/inspection/procedures/(subpages)/[id]/reportresult/edit/[reportId]/layout.tsx index 7a4c48489..6a8da6e70 100644 --- a/employee-portal/src/app/(businessModules)/inspection/procedures/(subpages)/[id]/reportresult/edit/[reportId]/layout.tsx +++ b/employee-portal/src/app/(businessModules)/inspection/procedures/(subpages)/[id]/reportresult/edit/[reportId]/layout.tsx @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +"use client"; + import { PropsWithChildren } from "react"; import { EditInspectionPageParams } from "@/app/(businessModules)/inspection/procedures/[id]/layout"; diff --git a/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/execution/page.tsx b/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/execution/page.tsx index 49c5e6c55..cc414ea4d 100644 --- a/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/execution/page.tsx +++ b/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/execution/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPhase } from "@eshg/inspection-api"; import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection"; import { InspectionTabDisabled } from "@/lib/businessModules/inspection/components/inspection/common/InspectionTabDisabled"; diff --git a/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/reportresult/page.tsx b/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/reportresult/page.tsx index 5fd6a3433..af1334749 100644 --- a/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/reportresult/page.tsx +++ b/employee-portal/src/app/(businessModules)/inspection/procedures/[id]/reportresult/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPhase } from "@eshg/inspection-api"; import { useGetInspection } from "@/lib/businessModules/inspection/api/queries/inspection"; import { InspectionTabDisabled } from "@/lib/businessModules/inspection/components/inspection/common/InspectionTabDisabled"; diff --git a/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx b/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx index 53a445d49..4f3c8b2dc 100644 --- a/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx +++ b/employee-portal/src/app/(businessModules)/inspection/textblocks/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { GetTextBlocksRequest } from "@eshg/employee-portal-api/inspection"; +import { GetTextBlocksRequest } from "@eshg/inspection-api"; import { SearchParams } from "@eshg/lib-portal/helpers/searchParams"; import { useGetTextBlocks } from "@/lib/businessModules/inspection/api/queries/textblocks"; diff --git a/employee-portal/src/app/(businessModules)/official-medical-service/procedures/[id]/documents/page.tsx b/employee-portal/src/app/(businessModules)/official-medical-service/procedures/[id]/documents/page.tsx new file mode 100644 index 000000000..7b07a7b28 --- /dev/null +++ b/employee-portal/src/app/(businessModules)/official-medical-service/procedures/[id]/documents/page.tsx @@ -0,0 +1,15 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { OfficialMedicalServiceDetailsPageProps } from "@/app/(businessModules)/official-medical-service/procedures/[id]/layout"; +import { DocumentsTable } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentsTable"; + +export default function OfficialMedicalServiceProcedureDetailsPage( + props: OfficialMedicalServiceDetailsPageProps, +) { + return <DocumentsTable procedureId={props.params.id} />; +} diff --git a/employee-portal/src/app/(businessModules)/official-medical-service/waiting-room/page.tsx b/employee-portal/src/app/(businessModules)/official-medical-service/waiting-room/page.tsx new file mode 100644 index 000000000..42113fd3a --- /dev/null +++ b/employee-portal/src/app/(businessModules)/official-medical-service/waiting-room/page.tsx @@ -0,0 +1,19 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { WaitingRoomTable } from "@/lib/businessModules/officialMedicalService/components/waitingRoom/WaitingRoomTable"; +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 OmsWaitingRoomPage() { + return ( + <StickyToolbarLayout toolbar={<Toolbar title="Wartezimmer" />}> + <MainContentLayout fullViewportHeight> + <WaitingRoomTable /> + </MainContentLayout> + </StickyToolbarLayout> + ); +} diff --git a/employee-portal/src/app/(businessModules)/statistics/evaluations/[id]/table/page.tsx b/employee-portal/src/app/(businessModules)/statistics/evaluations/[id]/table/page.tsx index fb98cb49a..387f460ae 100644 --- a/employee-portal/src/app/(businessModules)/statistics/evaluations/[id]/table/page.tsx +++ b/employee-portal/src/app/(businessModules)/statistics/evaluations/[id]/table/page.tsx @@ -5,12 +5,13 @@ "use client"; -import { ApiSortDirection } from "@eshg/employee-portal-api/stiProtection"; +import { ApiSortDirection } from "@eshg/statistics-api"; import { startTransition, useEffect, useState } from "react"; -import { isDefined, isNullish } from "remeda"; +import { isDefined } from "remeda"; import { isValidAttributeKey, + mapAttributeSelectionToKey, mapKeyToAttributeSelection, } from "@/lib/businessModules/statistics/api/mapper/mapAttributeSelectionKey"; import { ProcedureReferences } from "@/lib/businessModules/statistics/api/models/evaluationDetailsTableProcedureReferences"; @@ -56,20 +57,16 @@ export default function EvaluationDetailsTablePage( props.params.id, ); - const sortableAccessorKey = evaluation.attributes.find( - (attribute) => attribute.type !== "ProcedureReferenceAttribute", - )?.key; - const { sortKey: tableSortKey, sortDirection: tableSortDirection, manualSortingProps, } = useTableSorting({ onSortingChange: () => resetPageNumber(), - initialSorting: isDefined(sortableAccessorKey) + initialSorting: evaluation.sortAttribute ? { - id: sortableAccessorKey, - desc: false, + id: mapAttributeSelectionToKey(evaluation.sortAttribute), + desc: evaluation.sortDirection === "DESC", } : undefined, }); @@ -95,7 +92,6 @@ export default function EvaluationDetailsTablePage( resetPageNumber(); }), filterTemplates: filterTemplates, - loading: isNullish(sortKey), }; return ( diff --git a/employee-portal/src/app/(businessModules)/sti-protection/text-templates/page.tsx b/employee-portal/src/app/(businessModules)/sti-protection/text-templates/page.tsx new file mode 100644 index 000000000..0e48f3d2a --- /dev/null +++ b/employee-portal/src/app/(businessModules)/sti-protection/text-templates/page.tsx @@ -0,0 +1,19 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { TextTemplatesOverviewTable } from "@/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTable"; +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 TextTemplatesOverviewPage() { + return ( + <StickyToolbarLayout toolbar={<Toolbar title="Textvorlagen" />}> + <MainContentLayout> + <TextTemplatesOverviewTable /> + </MainContentLayout> + </StickyToolbarLayout> + ); +} diff --git a/employee-portal/src/app/playground/charts/page.tsx b/employee-portal/src/app/playground/charts/page.tsx index 8d2638b5c..1beced5ed 100644 --- a/employee-portal/src/app/playground/charts/page.tsx +++ b/employee-portal/src/app/playground/charts/page.tsx @@ -9,8 +9,6 @@ import { Option, Select, Sheet, Stack, Switch, Typography } from "@mui/joy"; import { ReactNode, useState } from "react"; import { - AnalysisLineDiagramConfiguration, - AnalysisScatterDiagramConfiguration, DiagramAxisRange, DiagramCharacteristicParameter, DiagramColorScheme, @@ -19,7 +17,6 @@ import { DiagramScaling, DiagramType, } from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; -import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; import { AnalysisDiagramBox } from "@/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisDiagramBox"; import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart"; import { ChoroplethMap } from "@/lib/businessModules/statistics/components/shared/charts/ChoroplethMap"; @@ -634,13 +631,13 @@ export default function PlaygroundChartsPage() { type: "IntegerAttribute", name: "Größe", unit: "m", - } as FlatAttribute, + }, yAttribute: { type: "IntegerAttribute", name: "Gewicht", unit: "kg", - } as FlatAttribute, - } as AnalysisLineDiagramConfiguration; + }, + }; const lineChartGroupedWithLongValues = [ { @@ -676,13 +673,13 @@ export default function PlaygroundChartsPage() { 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 AnalysisLineDiagramConfiguration; + }, + }; const scatterChartSimple = [ { @@ -745,13 +742,13 @@ export default function PlaygroundChartsPage() { type: "IntegerAttribute", name: "Größe", unit: "m", - } as FlatAttribute, + }, yAttribute: { type: "IntegerAttribute", name: "Gewicht", unit: "kg", - } as FlatAttribute, - } as AnalysisScatterDiagramConfiguration; + }, + }; const choroplethData = [ { diff --git a/employee-portal/src/app/playground/filter-settings/page.tsx b/employee-portal/src/app/playground/filter-settings/page.tsx index 7daab474f..9f36e574f 100644 --- a/employee-portal/src/app/playground/filter-settings/page.tsx +++ b/employee-portal/src/app/playground/filter-settings/page.tsx @@ -89,6 +89,7 @@ const filterDefinitions: FilterDefinition[] = [ name: "Date Comparison", }, { type: "Year", key: "year", name: "Year" }, + { type: "Text", key: "text", name: "Text" }, ]; const initialValues: FilterValue[] = [ diff --git a/employee-portal/src/app/playground/page.tsx b/employee-portal/src/app/playground/page.tsx index 0164fc27b..65160e2ea 100644 --- a/employee-portal/src/app/playground/page.tsx +++ b/employee-portal/src/app/playground/page.tsx @@ -115,6 +115,9 @@ export default function PlaygroundIndexPage() { Appointment Picker Field </InternalLink> </li> + <li> + <InternalLink href="/playground/teeth">Zahn-Icons</InternalLink> + </li> </ul> </MainContentLayout> </StickyToolbarLayout> diff --git a/employee-portal/src/app/playground/teeth/page.tsx b/employee-portal/src/app/playground/teeth/page.tsx new file mode 100644 index 000000000..263a21429 --- /dev/null +++ b/employee-portal/src/app/playground/teeth/page.tsx @@ -0,0 +1,180 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Grid, Typography } from "@mui/joy"; + +import { + Cuspid, + Incisor, + Molar, + Premolar, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth"; +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 TeethPlaygroundPage() { + return ( + <StickyToolbarLayout + toolbar={<Toolbar title={"Teeth"} backHref="/playground" />} + > + <MainContentLayout> + <Grid container spacing={2}> + <Grid xxs={12}> + <Typography level="h3">Incisor (Schneidezahn)</Typography> + </Grid> + + <Grid xxs={1}> + <Incisor variant="upperJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Incisor + variant="upperJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Incisor variant="upperJaw" /> + </Grid> + <Grid xxs={1}> + <Incisor variant="upperJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={1}> + <Incisor variant="lowerJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Incisor + variant="lowerJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Incisor variant="lowerJaw" /> + </Grid> + <Grid xxs={1}> + <Incisor variant="lowerJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={12}> + <Typography level="h3">Cuspid (Eckzahn)</Typography> + </Grid> + + <Grid xxs={1}> + <Cuspid variant="upperJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Cuspid + variant="upperJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Cuspid variant="upperJaw" /> + </Grid> + <Grid xxs={1}> + <Cuspid variant="upperJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={1}> + <Cuspid variant="lowerJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Cuspid + variant="lowerJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Cuspid variant="lowerJaw" /> + </Grid> + <Grid xxs={1}> + <Cuspid variant="lowerJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={12}> + <Typography level="h3">Premolar (Backenzahn)</Typography> + </Grid> + + <Grid xxs={1}> + <Premolar variant="upperJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Premolar + variant="upperJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Premolar variant="upperJaw" /> + </Grid> + <Grid xxs={1}> + <Premolar variant="upperJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={1}> + <Premolar variant="lowerJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Premolar + variant="lowerJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Premolar variant="lowerJaw" /> + </Grid> + <Grid xxs={1}> + <Premolar variant="lowerJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={12}> + <Typography level="h3">Molar (Mahlzahn)</Typography> + </Grid> + + <Grid xxs={1}> + <Molar variant="upperJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Molar + variant="upperJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Molar variant="upperJaw" /> + </Grid> + <Grid xxs={1}> + <Molar variant="upperJaw" hasPreviousExaminationResult /> + </Grid> + + <Grid xxs={1}> + <Molar variant="lowerJaw" isPrimaryTooth /> + </Grid> + <Grid xxs={1}> + <Molar + variant="lowerJaw" + isPrimaryTooth + hasPreviousExaminationResult + /> + </Grid> + <Grid xxs={1}> + <Molar variant="lowerJaw" /> + </Grid> + <Grid xxs={1}> + <Molar variant="lowerJaw" hasPreviousExaminationResult /> + </Grid> + </Grid> + </MainContentLayout> + </StickyToolbarLayout> + ); +} diff --git a/employee-portal/src/lib/auditlog/api/clients.ts b/employee-portal/src/lib/auditlog/api/clients.ts index 1688468f7..8de634e50 100644 --- a/employee-portal/src/lib/auditlog/api/clients.ts +++ b/employee-portal/src/lib/auditlog/api/clients.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { AuditLogApi, Configuration } from "@eshg/employee-portal-api/auditlog"; +import { AuditLogApi, Configuration } from "@eshg/auditlog-api"; import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; function useConfiguration() { diff --git a/employee-portal/src/lib/auditlog/api/models/auditlog.ts b/employee-portal/src/lib/auditlog/api/models/auditlog.ts index a2e841d4f..ff4ec917a 100644 --- a/employee-portal/src/lib/auditlog/api/models/auditlog.ts +++ b/employee-portal/src/lib/auditlog/api/models/auditlog.ts @@ -7,7 +7,7 @@ import { ApiAuditLogGrantedAccessCount, ApiAuditLogSource, ApiGetAvailableAuditLogsResponse, -} from "@eshg/employee-portal-api/auditlog/models"; +} from "@eshg/auditlog-api"; interface GetAvailableAuditLogsResponse { logs: AuditLog[]; diff --git a/employee-portal/src/lib/auditlog/components/AuditLogDecryptSidebar.tsx b/employee-portal/src/lib/auditlog/components/AuditLogDecryptSidebar.tsx index 02e0cd2a1..a9f25be73 100644 --- a/employee-portal/src/lib/auditlog/components/AuditLogDecryptSidebar.tsx +++ b/employee-portal/src/lib/auditlog/components/AuditLogDecryptSidebar.tsx @@ -7,7 +7,7 @@ import { ApiAuditLogSource, ApiAuditLogSourceFromJSON, AuditLogApi, -} from "@eshg/employee-portal-api/auditlog"; +} from "@eshg/auditlog-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { encodeReservedHtmlCharacters } from "@eshg/lib-portal/helpers/htmlStringEncoder"; diff --git a/employee-portal/src/lib/auditlog/components/AuditLogSheet.tsx b/employee-portal/src/lib/auditlog/components/AuditLogSheet.tsx index d22d012c2..8e8053ce7 100644 --- a/employee-portal/src/lib/auditlog/components/AuditLogSheet.tsx +++ b/employee-portal/src/lib/auditlog/components/AuditLogSheet.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiAuditLogSourceFromJSON } from "@eshg/employee-portal-api/auditlog"; +import { ApiAuditLogSourceFromJSON } from "@eshg/auditlog-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { FeedOutlined } from "@mui/icons-material"; import { Sheet } from "@mui/joy"; diff --git a/employee-portal/src/lib/auditlog/components/auditLogAccessibleColumns.tsx b/employee-portal/src/lib/auditlog/components/auditLogAccessibleColumns.tsx index e94f14bde..65df71f9c 100644 --- a/employee-portal/src/lib/auditlog/components/auditLogAccessibleColumns.tsx +++ b/employee-portal/src/lib/auditlog/components/auditLogAccessibleColumns.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiAccessibleAuditLog } from "@eshg/employee-portal-api/auditlog"; +import { ApiAccessibleAuditLog } from "@eshg/auditlog-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { VisibilityOutlined } from "@mui/icons-material"; import { Chip, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx index a8f390f28..b8668845b 100644 --- a/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx +++ b/employee-portal/src/lib/auditlog/components/authorize/AuditLogAuthorizeSidebar.tsx @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUser } from "@eshg/base-api"; import { ApiAuditLogSource, ApiGetAuditLogGrantedAccessesResponse, -} from "@eshg/employee-portal-api/auditlog"; +} from "@eshg/auditlog-api"; +import { ApiUser } from "@eshg/base-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { List, ListItem, Sheet, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/auditlog/components/authorize/useAuditLogAdminFilterSettings.ts b/employee-portal/src/lib/auditlog/components/authorize/useAuditLogAdminFilterSettings.ts index 3ad45c4bf..d9279aab7 100644 --- a/employee-portal/src/lib/auditlog/components/authorize/useAuditLogAdminFilterSettings.ts +++ b/employee-portal/src/lib/auditlog/components/authorize/useAuditLogAdminFilterSettings.ts @@ -6,7 +6,7 @@ import { ApiAuditLogSource, ApiAuditLogSourceFromJSON, -} from "@eshg/employee-portal-api/auditlog"; +} from "@eshg/auditlog-api"; import { ensureArray } from "@eshg/lib-portal/helpers/guards"; import { SearchParams } from "@eshg/lib-portal/helpers/searchParams"; import { isString } from "remeda"; diff --git a/employee-portal/src/lib/auditlog/components/crypto.ts b/employee-portal/src/lib/auditlog/components/crypto.ts index 23188c669..1d6179d06 100644 --- a/employee-portal/src/lib/auditlog/components/crypto.ts +++ b/employee-portal/src/lib/auditlog/components/crypto.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ApiGetEncryptedSymmetricKeyResponse } from "@eshg/auditlog-api"; import { ApiEmployeeUserKeys } from "@eshg/base-api"; -import { ApiGetEncryptedSymmetricKeyResponse } from "@eshg/employee-portal-api/auditlog"; import { AeadId, CipherSuite, KdfId, KemId } from "hpke-js"; const suite = new CipherSuite({ diff --git a/employee-portal/src/lib/auditlog/mutations/auditlog.ts b/employee-portal/src/lib/auditlog/mutations/auditlog.ts index 77f0c56e0..486bc15c1 100644 --- a/employee-portal/src/lib/auditlog/mutations/auditlog.ts +++ b/employee-portal/src/lib/auditlog/mutations/auditlog.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGrantAuditLogAccessRequest } from "@eshg/employee-portal-api/auditlog"; +import { ApiGrantAuditLogAccessRequest } from "@eshg/auditlog-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/auditlog/queries/auditlog.ts b/employee-portal/src/lib/auditlog/queries/auditlog.ts index 1637c16cf..912a48d71 100644 --- a/employee-portal/src/lib/auditlog/queries/auditlog.ts +++ b/employee-portal/src/lib/auditlog/queries/auditlog.ts @@ -8,7 +8,7 @@ import { GetAuditLogGrantedAccessesRequest, GetAuditLogGranteesCandidatesRequest, GetAvailableLogsRequest, -} from "@eshg/employee-portal-api/auditlog"; +} from "@eshg/auditlog-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useSuspenseQuery } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/baseModule/api/mutations/facility.ts b/employee-portal/src/lib/baseModule/api/mutations/facility.ts index 460ef864c..02e6c6e39 100644 --- a/employee-portal/src/lib/baseModule/api/mutations/facility.ts +++ b/employee-portal/src/lib/baseModule/api/mutations/facility.ts @@ -4,7 +4,7 @@ */ import { ApiAddFacilityFileStateRequest } from "@eshg/base-api"; -import { ApiDataOrigin } from "@eshg/employee-portal-api/inspection"; +import { ApiDataOrigin } from "@eshg/inspection-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { isNullish } from "remeda"; diff --git a/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts b/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts index 049599249..ab6928acd 100644 --- a/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts +++ b/employee-portal/src/lib/baseModule/api/mutations/gdpr.ts @@ -8,12 +8,12 @@ import { ApiAddGdprProcedureRequest, ApiGdprProcedureStatus, } from "@eshg/base-api"; +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiGdprProcedureType, GdprValidationTaskApiInterface, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/lib-procedures-api"; import { useGdprProcedureApi } from "@/lib/baseModule/api/clients"; diff --git a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx index 4819b829f..933cdb2b8 100644 --- a/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx +++ b/employee-portal/src/lib/baseModule/components/contacts/forms/merge/MergeStringField.tsx @@ -33,6 +33,7 @@ export function MergeStringField({ } function normalizeValue(value: string | undefined) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing return value === undefined || value === "" ? (emptyValue ?? " ") : value; } diff --git a/employee-portal/src/lib/baseModule/components/gdpr/constants.ts b/employee-portal/src/lib/baseModule/components/gdpr/constants.ts index 8f50e49a2..317471d16 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/constants.ts +++ b/employee-portal/src/lib/baseModule/components/gdpr/constants.ts @@ -7,7 +7,6 @@ import { ApiGdprProcedureStatus } from "@eshg/base-api"; import { DefaultColorPalette } from "@mui/joy/styles/types"; export const gdprProcedureStatusColor = { - OPEN: "neutral", DRAFT: "warning", IN_PROGRESS: "primary", CLOSED: "success", diff --git a/employee-portal/src/lib/baseModule/components/gdpr/helpers.ts b/employee-portal/src/lib/baseModule/components/gdpr/helpers.ts index 35f8d1d01..97d32bc1d 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/helpers.ts +++ b/employee-portal/src/lib/baseModule/components/gdpr/helpers.ts @@ -10,8 +10,8 @@ import { instanceOfApiGdprFacility, instanceOfApiGdprPerson, } from "@eshg/base-api"; -import { ApiGdprValidationTaskIdentificationData } from "@eshg/employee-portal-api/businessProcedures"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; +import { ApiGdprValidationTaskIdentificationData } from "@eshg/lib-procedures-api"; export function isGdprPerson( identificationData: ApiGdprIdentificationData, diff --git a/employee-portal/src/lib/baseModule/components/gdpr/i18n.ts b/employee-portal/src/lib/baseModule/components/gdpr/i18n.ts index 13c2271be..9d9290ffd 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/i18n.ts +++ b/employee-portal/src/lib/baseModule/components/gdpr/i18n.ts @@ -8,7 +8,6 @@ import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; export const statusTranslation = { DRAFT: "Entwurf", - OPEN: "Offen", IN_PROGRESS: "In Bearbeitung", CLOSED: "Abgeschlossen", ABORTED: "Abgebrochen", 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 c996d8cb3..38f7d9b8d 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/overview/CreateGDPRProcedureSidebar.tsx @@ -10,7 +10,10 @@ import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { SALUTATION_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; -import { validateLength } from "@eshg/lib-portal/helpers/validators"; +import { + validateDateOfBirth, + validateLength, +} from "@eshg/lib-portal/helpers/validators"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { Divider, Grid } from "@mui/joy"; import { Formik } from "formik"; @@ -127,6 +130,7 @@ function CreateGDPRProcedureSidebar({ name={fieldName("dateOfBirth")} label={"Geburtsdatum"} required={"Bitte ein Geburtsdatum eingeben"} + validate={validateDateOfBirth} /> </Grid> <Grid xxs={12}> diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/MatterOfConcernDisplayField.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/MatterOfConcernDisplayField.tsx new file mode 100644 index 000000000..e64140254 --- /dev/null +++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/MatterOfConcernDisplayField.tsx @@ -0,0 +1,37 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import InfoIcon from "@mui/icons-material/InfoOutlined"; +import { isString } from "remeda"; + +import { multiLineEllipsis } from "@/lib/baseModule/theme/theme"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; + +export function MatterOfConcernDisplayField({ + value, + editable, +}: Readonly<{ + value: string | undefined; + editable: boolean; +}>) { + const isPresent = isString(value) && value.trim().length > 0; + return ( + <DetailsItem + label="Anliegen" + value={isPresent || !editable ? value : "Bitte Anliegen eintragen."} + slotProps={{ + value: { + startDecorator: isPresent ? undefined : ( + <InfoIcon color="danger" size="md" /> + ), + sx: { + maxWidth: "100%", + ...multiLineEllipsis(3), + }, + }, + }} + /> + ); +} diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprDownloadPackagesTile.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprDownloadPackagesTile.tsx index 1eeba1854..f6f05b32d 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprDownloadPackagesTile.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprDownloadPackagesTile.tsx @@ -4,8 +4,8 @@ */ import { ApiGetGdprProcedureResponse } from "@eshg/base-api"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import CheckmarkIcon from "@mui/icons-material/Check"; import DownloadIcon from "@mui/icons-material/SimCardDownloadOutlined"; import { diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprFacilityDataTile.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprFacilityDataTile.tsx index ec553086d..393eb971a 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprFacilityDataTile.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprFacilityDataTile.tsx @@ -4,16 +4,16 @@ */ import { ApiGdprFacility } from "@eshg/base-api"; -import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink"; import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; import VerifiedIcon from "@mui/icons-material/VerifiedOutlined"; -import { Stack, Typography } from "@mui/joy"; +import { Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider"; import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { ExternalLinkDetailsItem } from "@/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; export function GdprFacilityDataTile({ @@ -31,19 +31,17 @@ export function GdprFacilityDataTile({ divider={<ResponsiveDivider />} > <DetailsColumn sx={columnSx}> - <DetailsCell name="name" label="Name" value={identity.name} /> + <DetailsItem label="Name" value={identity.name} /> {identity.dataTransmitterPseudonymId && ( - <DetailsCell - name="dataTransmitterPseudonymId" + <DetailsItem label="Mein Unternehmenskonto" - value={ - <Typography - startDecorator={<VerifiedIcon color="success" />} - noWrap - > - Authentifiziert - </Typography> - } + value="Authentifiziert" + slotProps={{ + value: { + startDecorator: <VerifiedIcon color="success" />, + noWrap: true, + }, + }} /> )} </DetailsColumn> @@ -53,22 +51,12 @@ export function GdprFacilityDataTile({ {(isNonEmptyString(identity.emailAddress) || isNonEmptyString(identity.phoneNumber)) && ( <DetailsColumn sx={columnSx}> - {isNonEmptyString(identity.emailAddress) && ( - <DetailsCell - name={"emailAddress"} - label={"E-Mail-Adresse"} - value={ - <ExternalLink href={`mailto:${identity.emailAddress}`}> - {identity.emailAddress} - </ExternalLink> - } - /> - )} - <DetailsCell - name={"phoneNumber"} - label={"Telefonnummer"} - value={identity.phoneNumber} + <ExternalLinkDetailsItem + label={"E-Mail-Adresse"} + value={identity.emailAddress} + href={(value) => `mailto:${value}`} /> + <DetailsItem label={"Telefonnummer"} value={identity.phoneNumber} /> </DetailsColumn> )} </Stack> diff --git a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprPersonDataTile.tsx b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprPersonDataTile.tsx index e0b56222c..36cd741e1 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprPersonDataTile.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/procedure/tiles/GdprPersonDataTile.tsx @@ -11,7 +11,7 @@ import { import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; import VerifiedIcon from "@mui/icons-material/VerifiedOutlined"; -import { Stack, Typography } from "@mui/joy"; +import { Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { @@ -20,10 +20,10 @@ import { } from "@/lib/baseModule/components/gdpr/procedure/tiles/SectionTile"; import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider"; import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow"; -import { ExternalLinkDetailsCell } from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { ExternalLinkDetailsItem } from "@/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem"; export function GdprPersonDataTile({ identity, @@ -46,46 +46,39 @@ export function GdprPersonDataTile({ <DetailsRow> {isNonEmptyString(identity.salutation) && identity.salutation !== ApiSalutation.NotSpecified && ( - <DetailsCell - name={"salutation"} + <DetailsItem label={PERSON_FIELD_NAME.salutation} value={SALUTATION_VALUES[identity.salutation]} /> )} - <DetailsCell - name={"title"} + <DetailsItem label={PERSON_FIELD_NAME.title} value={identity.title} avoidWrap /> </DetailsRow> - <DetailsCell - name={"firstName"} + <DetailsItem label={PERSON_FIELD_NAME.firstName} value={identity.firstName} /> - <DetailsCell - name={"lastName"} + <DetailsItem label={PERSON_FIELD_NAME.lastName} value={identity.lastName} /> - <DetailsCell - name={"dateOfBirth"} + <DetailsItem label={PERSON_FIELD_NAME.dateOfBirth} value={formatDate(identity.dateOfBirth)} /> {identity.bpk2 && ( - <DetailsCell - name="bpk2" + <DetailsItem label="BundID" - value={ - <Typography - startDecorator={<VerifiedIcon color="success" />} - noWrap - > - Authentifiziert - </Typography> - } + value="Authentifiziert" + slotProps={{ + value: { + startDecorator: <VerifiedIcon color="success" />, + noWrap: true, + }, + }} /> )} </DetailsColumn> @@ -95,16 +88,12 @@ export function GdprPersonDataTile({ {(isNonEmptyString(identity.emailAddress) || isNonEmptyString(identity.phoneNumber)) && ( <DetailsColumn sx={columnSx}> - {isNonEmptyString(identity.emailAddress) && ( - <ExternalLinkDetailsCell - name={"emailAddress"} - label={PERSON_FIELD_NAME.emailAddresses} - value={identity.emailAddress} - href={(value) => `mailto:${value}`} - /> - )} - <DetailsCell - name={"phoneNumber"} + <ExternalLinkDetailsItem + label={PERSON_FIELD_NAME.emailAddresses} + value={identity.emailAddress} + href={(value: string) => `mailto:${value}`} + /> + <DetailsItem label={PERSON_FIELD_NAME.phoneNumbers} value={identity.phoneNumber} /> 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 856e783c9..a0742c7f9 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 @@ -18,7 +18,6 @@ import { } from "@eshg/lib-portal/errorHandling/AlertContext"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import EditIcon from "@mui/icons-material/EditOutlined"; -import InfoIcon from "@mui/icons-material/InfoOutlined"; import RefreshIcon from "@mui/icons-material/Refresh"; import { Button, Divider, IconButton, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; @@ -37,14 +36,14 @@ import { typeTranslation, } from "@/lib/baseModule/components/gdpr/i18n"; import { DownloadReportButton } from "@/lib/baseModule/components/gdpr/procedure/DownloadReportButton"; +import { MatterOfConcernDisplayField } from "@/lib/baseModule/components/gdpr/procedure/MatterOfConcernDisplayField"; import { useEditMatterOfConcernSidebar } from "@/lib/baseModule/components/gdpr/procedure/sidebars/EditMatterOfConcernSidebar"; import { SectionTile, SectionTitle, } from "@/lib/baseModule/components/gdpr/procedure/tiles/SectionTile"; -import { multiLineEllipsis } from "@/lib/baseModule/theme/theme"; import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; @@ -115,13 +114,11 @@ export function ProcedureDetailsTile({ <AlertSlot /> - <DetailsCell - name={"createdAt"} + <DetailsItem label={"Erstellt"} value={formatDateTime(procedure.createdAt)} /> - <DetailsCell - name={"type"} + <DetailsItem label={"Vorgangsart"} value={ isGdprPerson(procedure.identificationData) @@ -130,13 +127,11 @@ export function ProcedureDetailsTile({ } avoidWrap /> - <DetailsCell - name={"status"} + <DetailsItem label={"Status"} value={statusTranslation[procedure.status]} /> - <DetailsCell - name="internalNote" + <DetailsItem label={ procedure.status === ApiGdprProcedureStatus.Closed ? "Ergebnis" @@ -147,25 +142,9 @@ export function ProcedureDetailsTile({ value={procedure.internalNote} /> {requiresMatterOfConcern && ( - <DetailsCell - name="matterOfConcern" - label="Anliegen" - value={ - procedure.matterOfConcern ?? - (isEditable ? ( - <Typography - startDecorator={<InfoIcon color="danger" size="md" />} - > - Bitte Anliegen eintragen. - </Typography> - ) : ( - "" - )) - } - valueSx={{ - ...multiLineEllipsis(3), - maxWidth: "100%", - }} + <MatterOfConcernDisplayField + value={procedure.matterOfConcern} + editable={isEditable} /> )} diff --git a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/UseCloseValidationTaskDialog.tsx b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/UseCloseValidationTaskDialog.tsx index 20e91c744..0e640c22e 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/UseCloseValidationTaskDialog.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/UseCloseValidationTaskDialog.tsx @@ -8,7 +8,7 @@ import { ApiBusinessProcedureWithInclusionStatus, ApiGdprProcedureType, GdprValidationTaskApiInterface, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { Typography } from "@mui/joy"; import { useCloseValidationTask } from "@/lib/baseModule/api/mutations/gdpr"; diff --git a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTaskProceduresTable.tsx b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTaskProceduresTable.tsx index f13653411..82eaebf70 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTaskProceduresTable.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTaskProceduresTable.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { ApiBusinessProcedureInclusionStatus, ApiBusinessProcedureWithInclusionStatus, @@ -11,8 +12,7 @@ import { ApiProcedure, ApiProcedureStatus, GdprValidationTaskApiInterface, -} from "@eshg/employee-portal-api/businessProcedures"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/lib-procedures-api"; import IncludedIcon from "@mui/icons-material/CheckOutlined"; import UndecidedIcon from "@mui/icons-material/ClearOutlined"; import { Button, Chip, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTasksTable.tsx b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTasksTable.tsx index 50a3aa11c..bf8939b75 100644 --- a/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTasksTable.tsx +++ b/employee-portal/src/lib/baseModule/components/gdpr/validationTasks/ValidationTasksTable.tsx @@ -10,7 +10,7 @@ import { ApiGdprValidationTask, ApiGdprValidationTaskStatus, GetAllGdprValidationTasksRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { Chip } from "@mui/joy"; import { useSuspenseQuery } from "@tanstack/react-query"; import { createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/baseModule/components/inboxProcedures/ContactForm.tsx b/employee-portal/src/lib/baseModule/components/inboxProcedures/ContactForm.tsx index d4a3c75e3..3dead1f47 100644 --- a/employee-portal/src/lib/baseModule/components/inboxProcedures/ContactForm.tsx +++ b/employee-portal/src/lib/baseModule/components/inboxProcedures/ContactForm.tsx @@ -4,7 +4,6 @@ */ import { ApiSalutation } from "@eshg/base-api"; -import { ApiContactType } from "@eshg/employee-portal-api/businessProcedures"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; @@ -17,11 +16,13 @@ import { buildEnumOptions, createFieldNameMapper, } from "@eshg/lib-portal/helpers/form"; +import { validateDateOfBirth } from "@eshg/lib-portal/helpers/validators"; import { NestedFormProps, OptionalFieldValue, } from "@eshg/lib-portal/types/form"; import { EnumMap } from "@eshg/lib-portal/types/helpers"; +import { ApiContactType } from "@eshg/lib-procedures-api"; import { Divider, Grid, Stack, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; @@ -114,7 +115,11 @@ export function ContactForm(props: NestedFormProps) { : undefined } /> - <DateField name={fieldName("dateOfBirth")} label="Geburtsdatum" /> + <DateField + name={fieldName("dateOfBirth")} + label="Geburtsdatum" + validate={validateDateOfBirth} + /> <Divider /> <AddressForm name={fieldName("address")} /> </Stack> diff --git a/employee-portal/src/lib/baseModule/components/inboxProcedures/CreateInboxProcedureForm.tsx b/employee-portal/src/lib/baseModule/components/inboxProcedures/CreateInboxProcedureForm.tsx index 6ec1f3441..8c9a568b2 100644 --- a/employee-portal/src/lib/baseModule/components/inboxProcedures/CreateInboxProcedureForm.tsx +++ b/employee-portal/src/lib/baseModule/components/inboxProcedures/CreateInboxProcedureForm.tsx @@ -4,11 +4,11 @@ */ import { ApiBaseFeature, ApiBusinessModule } from "@eshg/base-api"; -import { ApiProcedureType } from "@eshg/employee-portal-api/businessProcedures"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +import { ApiProcedureType } from "@eshg/lib-procedures-api"; import { Box, Grid, Typography } from "@mui/joy"; import { Formik } from "formik"; import { useMemo } from "react"; diff --git a/employee-portal/src/lib/baseModule/components/inboxProcedures/InboxProgressEntryForm.tsx b/employee-portal/src/lib/baseModule/components/inboxProcedures/InboxProgressEntryForm.tsx index 0bbc40eb4..5910be1e5 100644 --- a/employee-portal/src/lib/baseModule/components/inboxProcedures/InboxProgressEntryForm.tsx +++ b/employee-portal/src/lib/baseModule/components/inboxProcedures/InboxProgressEntryForm.tsx @@ -5,7 +5,6 @@ "use client"; -import { ApiInboxProgressEntryType } from "@eshg/employee-portal-api/businessProcedures"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; @@ -19,6 +18,7 @@ import { OptionalFieldValue, } from "@eshg/lib-portal/types/form"; import { EnumMap } from "@eshg/lib-portal/types/helpers"; +import { ApiInboxProgressEntryType } from "@eshg/lib-procedures-api"; import { Grid, Stack, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; diff --git a/employee-portal/src/lib/baseModule/components/inboxProcedures/mapper.ts b/employee-portal/src/lib/baseModule/components/inboxProcedures/mapper.ts index 136563a0b..73db9e032 100644 --- a/employee-portal/src/lib/baseModule/components/inboxProcedures/mapper.ts +++ b/employee-portal/src/lib/baseModule/components/inboxProcedures/mapper.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { AcademicTitle } from "@eshg/lib-portal/components/formFields/constants"; +import { toUtcDate } from "@eshg/lib-portal/helpers/dateTime"; +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ApiContactDetails, ApiContactType, @@ -11,10 +14,7 @@ import { ApiInboxProcedureAddress, ApiInboxProgressEntryType, ApiTitle, -} from "@eshg/employee-portal-api/businessProcedures"; -import { AcademicTitle } from "@eshg/lib-portal/components/formFields/constants"; -import { toUtcDate } from "@eshg/lib-portal/helpers/dateTime"; -import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +} from "@eshg/lib-procedures-api"; import { isEmpty } from "remeda"; import { AddressValues } from "@/lib/baseModule/components/inboxProcedures/AddressForm"; diff --git a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListCollapsed.tsx b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListCollapsed.tsx index 073ff5c48..28bda5c38 100644 --- a/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListCollapsed.tsx +++ b/employee-portal/src/lib/baseModule/components/layout/sideNavigation/NavigationListCollapsed.tsx @@ -38,45 +38,52 @@ export function NavigationListCollapsed({ setCollapsed?: Dispatch<SetStateAction<boolean>>; itemGroups: SideNavItemGroups; }) { - const [menuIndex, setMenuIndex] = useState<null | number>(null); + const [openMenuItemName, setOpenMenuItemName] = useState<string | null>(null); const itemProps = { - onClick: () => setMenuIndex(null), + onClick: () => setOpenMenuItemName(null), }; const pathname = usePathname(); - // eslint-disable-next-line func-style - const createHandleLeaveMenu = - (index: number) => (getIsOnButton: () => boolean) => { + + function createHandleLeaveMenu(itemName: string) { + return (getIsOnButton: () => boolean) => { setTimeout(() => { const isOnButton = getIsOnButton(); if (!isOnButton) { - setMenuIndex((latestIndex: null | number) => { - if (index === latestIndex) { + setOpenMenuItemName((previousOpenMenuItemName) => { + if (itemName === previousOpenMenuItemName) { return null; } - return latestIndex; + return previousOpenMenuItemName; }); } }, 200); }; + } function getNavItemGroup(itemGroup: SideNavigationItem[]) { - if (itemGroup.length > 0) { - const list = itemGroup.map((item, index) => - "subItems" in item ? ( + if (itemGroup.length === 0) { + return undefined; + } + + const list = itemGroup.map((item) => { + if ("subItems" in item) { + const isItemMenuOpen = openMenuItemName === item.name; + + return ( <NavigationIconItemWithSubItems key={item.name} item={item} - open={menuIndex === index} - onOpen={() => setMenuIndex(index)} - onLeaveMenu={createHandleLeaveMenu(index)} + open={isItemMenuOpen} + onOpen={() => setOpenMenuItemName(item.name)} + onLeaveMenu={createHandleLeaveMenu(item.name)} selected={ - menuIndex !== index && + !isItemMenuOpen && item.subItems.some((subItem) => isItemSelected(subItem, pathname)) } menu={ <Menu - onClose={() => setMenuIndex(null)} + onClose={() => setOpenMenuItemName(null)} keepMounted={true} disablePortal={true} > @@ -110,16 +117,17 @@ export function NavigationListCollapsed({ > {item.decorator} </NavigationIconItemWithSubItems> - ) : ( - <NavigationIconItemWithoutSubItems - key={`${item.href}-${item.name}`} - item={item} - resetActiveIndex={() => setMenuIndex(index)} - /> - ), + ); + } + return ( + <NavigationIconItemWithoutSubItems + key={item.name} + item={item} + resetActiveIndex={() => setOpenMenuItemName(null)} + /> ); - return <StyledList sx={listStyling}>{list}</StyledList>; - } else return undefined; + }); + return <StyledList sx={listStyling}>{list}</StyledList>; } return ( diff --git a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx index 9ffb533c9..7d016a9ad 100644 --- a/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx +++ b/employee-portal/src/lib/baseModule/components/procedureMetrics/ProcedureMetricsDisplay.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiProcedureMetric } from "@eshg/employee-portal-api/inspection"; +import { ApiProcedureMetric } from "@eshg/inspection-api"; import { CheckOutlined, DeviceHubOutlined, diff --git a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx index 49a342be8..e1a528478 100644 --- a/employee-portal/src/lib/baseModule/components/task/Teamview.tsx +++ b/employee-portal/src/lib/baseModule/components/task/Teamview.tsx @@ -5,11 +5,7 @@ "use client"; -import { - ApiBusinessModule, - ApiTask, - ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule, ApiTask, ApiUser } from "@eshg/lib-procedures-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { differenceInDays } from "date-fns"; import { useState } from "react"; diff --git a/employee-portal/src/lib/baseModule/theme/theme.ts b/employee-portal/src/lib/baseModule/theme/theme.ts index 1da006b03..ba1d5d77c 100644 --- a/employee-portal/src/lib/baseModule/theme/theme.ts +++ b/employee-portal/src/lib/baseModule/theme/theme.ts @@ -35,6 +35,12 @@ declare module "@mui/joy/styles/types/zIndex" { } } +declare module "@mui/joy/ToggleButtonGroup" { + interface ToggleButtonGroupPropsVariantOverrides { + tabs: true; + } +} + type FontSizeOverrides = { [_k in keyof FontSize]: true }; declare module "@mui/joy/SvgIcon" { // eslint-disable-next-line @typescript-eslint/no-empty-object-type @@ -49,7 +55,7 @@ const noBoxShadow = { boxShadow: "none", }; -export function multiLineEllipsis(linesToShow = 2): SxProps { +export function multiLineEllipsis(linesToShow = 2) { return { display: "-webkit-box", WebkitBoxOrient: "vertical", @@ -57,7 +63,7 @@ export function multiLineEllipsis(linesToShow = 2): SxProps { lineClamp: String(linesToShow), overflow: "hidden", textOverflow: "ellipsis", - }; + } satisfies SxProps; } function fixOutlinedHeight(variant?: string, size?: string) { @@ -447,16 +453,33 @@ export const theme = extendTheme({ }, JoyToggleButtonGroup: { styleOverrides: { - root: ({ ownerState }) => { - if (ownerState.color === "primary") { - return { - ".MuiButton-variantOutlined": { - color: "var(--joy-palette-primary-600)", + root: ({ ownerState, theme }) => ({ + ...(ownerState.color === "primary" && { + ".MuiButton-variantOutlined": { + color: "var(--joy-palette-primary-600)", + }, + }), + ...(ownerState.variant === "tabs" && { + ".MuiButton-variantTabs": { + backgroundColor: theme.palette.background.level1, + "--ButtonGroup-separatorColor": "#636B744D", + "&[aria-pressed=true]": { + zIndex: 2, + backgroundColor: theme.palette.primary.solidBg, + color: "white", + "&[data-first-child]": { + borderLeft: `1px solid ${theme.palette.primary.solidBg}`, + }, }, - }; - } - return {}; - }, + "&[aria-pressed=false]": { + fontWeight: 400, + "&:hover": { + zIndex: 1, + }, + }, + }, + }), + }), }, }, JoyCheckbox: { diff --git a/employee-portal/src/lib/businessModules/chat/api/clients.ts b/employee-portal/src/lib/businessModules/chat/api/clients.ts index f098d50cf..ec71169b9 100644 --- a/employee-portal/src/lib/businessModules/chat/api/clients.ts +++ b/employee-portal/src/lib/businessModules/chat/api/clients.ts @@ -7,7 +7,7 @@ import { ChatFeatureTogglesApi, Configuration, UserSettingsApi, -} from "@eshg/employee-portal-api/chatManagement"; +} from "@eshg/chat-management-api"; import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; function useConfiguration() { diff --git a/employee-portal/src/lib/businessModules/chat/api/mutations/userSettingsApi.ts b/employee-portal/src/lib/businessModules/chat/api/mutations/userSettingsApi.ts index 94854799a..fef4629e6 100644 --- a/employee-portal/src/lib/businessModules/chat/api/mutations/userSettingsApi.ts +++ b/employee-portal/src/lib/businessModules/chat/api/mutations/userSettingsApi.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUserSettingsRequest } from "@eshg/employee-portal-api/chatManagement"; +import { ApiUserSettingsRequest } from "@eshg/chat-management-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { useMutation } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/chat/api/queries/featureTogglesApi.ts b/employee-portal/src/lib/businessModules/chat/api/queries/featureTogglesApi.ts index 5ee360658..a1068c6a5 100644 --- a/employee-portal/src/lib/businessModules/chat/api/queries/featureTogglesApi.ts +++ b/employee-portal/src/lib/businessModules/chat/api/queries/featureTogglesApi.ts @@ -6,7 +6,7 @@ import { ApiChatFeature, ApiGetFeatureTogglesResponse, -} from "@eshg/employee-portal-api/chatManagement"; +} from "@eshg/chat-management-api"; import { FeatureToggleQueryOptions, selectDisabledOldFeature, diff --git a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx index b68e637e3..b93cc9514 100644 --- a/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx +++ b/employee-portal/src/lib/businessModules/chat/components/ChatAvatar.tsx @@ -42,7 +42,7 @@ export function ChatAvatar({ variant="solid" color="warning" size={size} - src={props.avatarUrl ? props.avatarUrl : undefined} + src={props.avatarUrl ?? undefined} > <GroupOutlinedIcon size="md" sx={{ color: "white" }} /> </Avatar> diff --git a/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx b/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx index 3e25f2a30..a5b86ee12 100644 --- a/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx +++ b/employee-portal/src/lib/businessModules/chat/shared/ChatProvider.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiChatFeature } from "@eshg/employee-portal-api/chatManagement"; +import { ApiChatFeature } from "@eshg/chat-management-api"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; import { createContext, useContext, useMemo } from "react"; import { doNothing, isNullish, omit } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/dental/features/children/ChildrenFilterSettings.tsx b/employee-portal/src/lib/businessModules/dental/features/children/ChildrenFilterSettings.tsx index bd39d03a7..1c93e5276 100644 --- a/employee-portal/src/lib/businessModules/dental/features/children/ChildrenFilterSettings.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/children/ChildrenFilterSettings.tsx @@ -85,6 +85,7 @@ export function ChildrenFilterSettings(props: ChildrenFilterSettingsProps) { onChange={(institutionId) => { props.setFilterFormValue("institutionIdFilter", institutionId); }} + placeholder="Schule/Kita suchen" /> </FormControl> <FormControl> diff --git a/employee-portal/src/lib/businessModules/dental/features/children/ChildrenTable.tsx b/employee-portal/src/lib/businessModules/dental/features/children/ChildrenTable.tsx index df1acbe3a..f8b1eb8ff 100644 --- a/employee-portal/src/lib/businessModules/dental/features/children/ChildrenTable.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/children/ChildrenTable.tsx @@ -9,9 +9,9 @@ import { ApiChildSortKey } from "@eshg/dental-api"; import { Child } from "@eshg/dental/api/models/Child"; import { useGetChildrenQuery } from "@eshg/dental/api/queries/childApi"; import { routes } from "@eshg/dental/shared/routes"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { useToggleableState } from "@eshg/lib-portal/hooks/useToggleableState"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { ColumnSort, createColumnHelper } from "@tanstack/react-table"; import { ReactNode } from "react"; diff --git a/employee-portal/src/lib/businessModules/dental/features/children/details/ChildDetails.tsx b/employee-portal/src/lib/businessModules/dental/features/children/details/ChildDetails.tsx index 347031cee..f95c7d0e3 100644 --- a/employee-portal/src/lib/businessModules/dental/features/children/details/ChildDetails.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/children/details/ChildDetails.tsx @@ -14,8 +14,8 @@ import { useUpdateAnnualChildSidebar } from "@/lib/businessModules/dental/featur import { IconTooltipButton } from "@/lib/shared/components/buttons/IconTooltipButton"; import { CentralFilePersonDetails } from "@/lib/shared/components/centralFile/display/CentralFilePersonDetails"; import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { PageGrid } from "@/lib/shared/components/page/PageGrid"; import { displayBoolean } from "@/lib/shared/helpers/booleans"; @@ -58,16 +58,8 @@ export function ChildDetailsPage(props: ChildDetailsProps) { canEdit={!disabled} > <Stack gap={1}> - <DetailsCell - name="institution" - label="Einrichtung" - value={child.institution.name} - /> - <DetailsCell - name="group" - label="Gruppe" - value={child.groupName} - /> + <DetailsItem label="Einrichtung" value={child.institution.name} /> + <DetailsItem label="Gruppe" value={child.groupName} /> {child.currentFluoridationConsent ? ( <> <Divider /> @@ -84,22 +76,19 @@ export function ChildDetailsPage(props: ChildDetailsProps) { /> </Typography> <Stack direction="row" gap={2} flexWrap="wrap"> - <DetailsCell - name="fluoridationConsent" + <DetailsItem label="Einverständis" value={displayBoolean( child.currentFluoridationConsent.consented, )} /> - <DetailsCell - name="fluoridationConsentDate" + <DetailsItem label="Datum der Einverständniserklärung" value={formatDate( child.currentFluoridationConsent.dateOfConsent, )} /> - <DetailsCell - name="allergy" + <DetailsItem label="Allergie" value={displayBoolean( child.currentFluoridationConsent.hasAllergy, @@ -108,8 +97,7 @@ export function ChildDetailsPage(props: ChildDetailsProps) { </Stack> </> ) : ( - <DetailsCell - name="fluoridationConsent" + <DetailsItem label="Einverständis zur Fluoridierung" value="Liegt nicht vor" /> diff --git a/employee-portal/src/lib/businessModules/dental/features/children/details/ChildExaminationForm.tsx b/employee-portal/src/lib/businessModules/dental/features/children/details/ChildExaminationForm.tsx index 531130142..2afecf483 100644 --- a/employee-portal/src/lib/businessModules/dental/features/children/details/ChildExaminationForm.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/children/details/ChildExaminationForm.tsx @@ -5,23 +5,57 @@ "use client"; -import { FormProps } from "@eshg/lib-portal/types/form"; +import { + ApiExaminationResult, + UpdateExaminationRequest, +} from "@eshg/dental-api"; +import { Examination } from "@eshg/dental/api/models/Examination"; +import { ToothDiagnoses } from "@eshg/dental/api/models/ExaminationResult"; +import { useUpdateExamination } from "@eshg/dental/api/mutations/childApi"; +import { + mapOptionalValue, + mapRequiredValue, +} from "@eshg/lib-portal/helpers/form"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; import { Formik } from "formik"; -import { ExaminationFormValues } from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; +import { + ExaminationFormValues, + mapToExaminationFormValues, +} from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; import { FormFooter } from "@/lib/businessModules/schoolEntry/features/procedures/examinations/FormFooter"; import { FormStack } from "@/lib/shared/components/form/FormStack"; -interface ChildExaminationFormProps - extends FormProps<ExaminationFormValues>, - RequiresChildren {} +interface ChildExaminationFormProps extends RequiresChildren { + examination: Examination; +} export function ChildExaminationForm(props: ChildExaminationFormProps) { + const { examination } = props; + const getToothDiagnoses = useDentalExaminationStore( + (state) => state.getToothDiagnoses, + ); + const updateExamination = useUpdateExamination(examination.id); + + async function handleSubmit(values: ExaminationFormValues) { + try { + const toothDiagnoses = getToothDiagnoses(); + await updateExamination.mutateAsync( + mapToRequest(examination, values, toothDiagnoses), + ); + } catch { + // TODO handle invalid tooth diagnoses + } + } + return ( <Formik - initialValues={props.initialValues} - onSubmit={props.onSubmit} + initialValues={mapToExaminationFormValues( + examination.result, + examination.note, + )} + onSubmit={handleSubmit} enableReinitialize > {({ handleSubmit, isSubmitting }) => { @@ -35,3 +69,49 @@ export function ChildExaminationForm(props: ChildExaminationFormProps) { </Formik> ); } + +function mapToRequest( + examination: Examination, + formValues: ExaminationFormValues, + toothDiagnoses: ToothDiagnoses, +): UpdateExaminationRequest { + return { + examinationId: examination.id, + apiUpdateExaminationRequest: { + version: examination.version, + note: mapOptionalValue(formValues.note), + result: mapExaminationResultRequest( + examination, + formValues, + toothDiagnoses, + ), + }, + }; +} + +function mapExaminationResultRequest( + examination: Examination, + formValues: ExaminationFormValues, + toothDiagnoses: ToothDiagnoses, +): ApiExaminationResult | undefined { + if (examination.screening) { + return { + type: "ScreeningExaminationResult", + oralHygieneStatus: mapOptionalValue(formValues.oralHygieneStatus), + fluorideVarnishApplied: + mapOptionalValue(formValues.fluorideVarnishApplied) ?? false, + toothDiagnoses: Object.values(toothDiagnoses), + }; + } + + if (examination.fluoridation) { + return { + type: "FluoridationExaminationResult", + fluorideVarnishApplied: mapRequiredValue( + formValues.fluorideVarnishApplied, + ), + }; + } + + return undefined; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/children/new/CreateChildSidebar.tsx b/employee-portal/src/lib/businessModules/dental/features/children/new/CreateChildSidebar.tsx index 9534a6f06..fec50204e 100644 --- a/employee-portal/src/lib/businessModules/dental/features/children/new/CreateChildSidebar.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/children/new/CreateChildSidebar.tsx @@ -73,6 +73,7 @@ function DentalSearchFormComponent( label="Einrichtung" categories={SCHOOL_OR_DAYCARE} required="Bitte eine Schule/Kita angeben." + placeholder="Schule/Kita suchen" getOptionLabel={getInstitutionOptionLabel} /> <SearchGroupField diff --git a/employee-portal/src/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection.tsx b/employee-portal/src/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection.tsx index cf5decc3f..20f7e9706 100644 --- a/employee-portal/src/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection.tsx @@ -4,29 +4,21 @@ */ import { ApiOralHygieneStatus } from "@eshg/dental-api"; +import { ExaminationStatus } from "@eshg/dental/api/models/ExaminationStatus"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { - BooleanSelectField, - BooleanSelectFieldProps, -} from "@eshg/lib-portal/components/formFields/BooleanSelectField"; -import { - SelectField, - SelectFieldProps, -} from "@eshg/lib-portal/components/formFields/SelectField"; + SoftRequiredBooleanSelectField, + SoftRequiredSelectField, +} from "@eshg/lib-portal/components/form/fieldVariants"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; -import { ReactNode } from "react"; +import { ExaminationStatusChip } from "@/lib/businessModules/dental/features/examinations/ExaminationStatusChip"; import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet"; import { ORAL_HYGIENE_STATUS } from "./translations"; -const DEFAULT_COMPONENTS: AdditionalInformationFormComponents = { - SelectField, - BooleanSelectField, -}; - export const ORAL_HYGIENE_STATUS_OPTIONS = buildEnumOptions<ApiOralHygieneStatus>(ORAL_HYGIENE_STATUS, true); @@ -35,44 +27,32 @@ export interface AdditionalInformationFormValues { fluorideVarnishApplied: OptionalFieldValue<boolean>; } -export interface AdditionalInformationFormComponents { - SelectField: < - TMultiple extends boolean = false, - TOptionLabel extends string | ReactNode = string, - >( - props: SelectFieldProps<TMultiple, TOptionLabel>, - ) => ReactNode; - BooleanSelectField: (props: BooleanSelectFieldProps) => ReactNode; -} - interface AdditionalInformationFormSectionProps { screening: boolean; fluoridation: boolean; fluoridationConsentGiven?: boolean; - components?: AdditionalInformationFormComponents; + status: ExaminationStatus; } export function AdditionalInformationFormSection( props: AdditionalInformationFormSectionProps, ) { - const { screening, fluoridation, fluoridationConsentGiven, components } = - props; - - const { SelectField, BooleanSelectField } = components ?? DEFAULT_COMPONENTS; + const { screening, fluoridation, fluoridationConsentGiven } = props; return ( <InformationSheet> <DetailsSection title="Zusatzinfos"> + <ExaminationStatusChip status={props.status} /> {screening && ( - <SelectField + <SoftRequiredSelectField name="oralHygieneStatus" label="Mundhygienestatus" options={ORAL_HYGIENE_STATUS_OPTIONS} + orientation="vertical" /> )} {fluoridation && ( <FluoridationField - component={BooleanSelectField} fluoridationConsentGiven={fluoridationConsentGiven} /> )} @@ -82,7 +62,6 @@ export function AdditionalInformationFormSection( } interface FluoridationFieldProps { - component: AdditionalInformationFormComponents["BooleanSelectField"]; fluoridationConsentGiven?: boolean; } @@ -96,13 +75,12 @@ function FluoridationField(props: FluoridationFieldProps) { ); } - const FieldComponent = props.component; - return ( - <FieldComponent + <SoftRequiredBooleanSelectField name="fluorideVarnishApplied" label="Fluoridierung" - required="Bitte angeben, ob fluoridiert wurde." + orientation="vertical" + softRequired /> ); } diff --git a/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationFormLayout.tsx b/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationFormLayout.tsx index 6e5a4f30c..80a268ed7 100644 --- a/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationFormLayout.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationFormLayout.tsx @@ -9,6 +9,7 @@ import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; import { parseOptionalValue } from "@eshg/lib-portal/helpers/form"; import { Grid } from "@mui/joy"; import { ReactNode } from "react"; +import { isDefined } from "remeda"; import { PageGrid } from "@/lib/shared/components/page/PageGrid"; @@ -21,17 +22,21 @@ export interface ExaminationFormValues interface ExaminationFormLayoutProps { additionalInformation: ReactNode; + dentalExamination?: ReactNode; note: ReactNode; } export function ExaminationFormLayout(props: ExaminationFormLayoutProps) { return ( <PageGrid> - <Grid xxs={12} md={4}> + <Grid xxs={12} md={3}> {props.additionalInformation} </Grid> - <Grid xxs={12} md={8}> - {props.note} + <Grid container xxs={12} md={9}> + {isDefined(props.dentalExamination) && ( + <Grid xxs={12}>{props.dentalExamination}</Grid> + )} + <Grid xxs={12}>{props.note}</Grid> </Grid> </PageGrid> ); diff --git a/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationStatusChip.tsx b/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationStatusChip.tsx index 0bff96c02..f1cb4c927 100644 --- a/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationStatusChip.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/examinations/ExaminationStatusChip.tsx @@ -11,6 +11,7 @@ import { EXAMINATION_STATUS } from "./translations"; const examinationStatusColors: Record<ExaminationStatus, ChipProps["color"]> = { OPEN: "neutral", CLOSED: "success", + NOT_PRESENT: "danger", }; interface ExaminationStatusChipProps { diff --git a/employee-portal/src/lib/businessModules/dental/features/examinations/translations.ts b/employee-portal/src/lib/businessModules/dental/features/examinations/translations.ts index 42d96b8b9..27bc98260 100644 --- a/employee-portal/src/lib/businessModules/dental/features/examinations/translations.ts +++ b/employee-portal/src/lib/businessModules/dental/features/examinations/translations.ts @@ -9,6 +9,7 @@ import { ExaminationStatus } from "@eshg/dental/api/models/ExaminationStatus"; export const EXAMINATION_STATUS: Record<ExaminationStatus, string> = { OPEN: "offen", CLOSED: "abgeschlossen", + NOT_PRESENT: "Nicht anwesend", }; export const ORAL_HYGIENE_STATUS: Record<ApiOralHygieneStatus, string> = { diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ChangeReasonForAbsenceModal.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ChangeReasonForAbsenceModal.tsx new file mode 100644 index 000000000..b2e8bd084 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ChangeReasonForAbsenceModal.tsx @@ -0,0 +1,101 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiReasonForAbsence, + UpdateExaminationRequest, +} from "@eshg/dental-api"; +import { ChildExamination } from "@eshg/dental/api/models/ChildExamination"; +import { useUpdateExamination } from "@eshg/dental/api/mutations/childApi"; +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { + buildEnumOptions, + mapRequiredValue, +} from "@eshg/lib-portal/helpers/form"; +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +import { isDefined } from "remeda"; + +import { FormDialog } from "@/lib/shared/components/formDialog/FormDialog"; + +const ABSENCE_VALUES: Record<ApiReasonForAbsence, string> = { + [ApiReasonForAbsence.NotAppeared]: "Nicht erschienen", + [ApiReasonForAbsence.Refused]: "Verweigert", + [ApiReasonForAbsence.Shifted]: "Versetzt", + [ApiReasonForAbsence.Moved]: "Umgezogen", +}; + +const ABSENCE_OPTIONS = buildEnumOptions(ABSENCE_VALUES); + +interface ReasonForAbsenceFormValues { + reasonForAbsence: OptionalFieldValue<ApiReasonForAbsence>; +} + +interface ChangeReasonForAbsenceModalProps { + onClose: () => void; + examination: ChildExamination; +} + +export function ChangeReasonForAbsenceModal( + props: ChangeReasonForAbsenceModalProps, +) { + const updateExamination = useUpdateExamination( + props.examination.examinationId, + ); + + async function onSubmit(values: ReasonForAbsenceFormValues) { + await updateExamination.mutateAsync( + mapToRequest( + props.examination.examinationId, + values, + props.examination.examinationVersion, + ), + { onSuccess: props.onClose }, + ); + } + const examinationResult = props.examination.result; + const initialReasonForAbsence = + isDefined(examinationResult) && examinationResult.type === "absence" + ? examinationResult.reasonForAbsence + : ""; + + return ( + <FormDialog<ReasonForAbsenceFormValues> + open + onClose={props.onClose} + onSubmit={onSubmit} + initialValues={{ reasonForAbsence: initialReasonForAbsence }} + title="Abwesenheit vermerken" + description="Bitte geben Sie einen Grund für die Abwesenheit ein." + color="primary" + confirmLabel="Speichern" + cancelLabel="Abbrechen" + > + <SelectField + sx={{ paddingTop: 2 }} + name="reasonForAbsence" + label="Grund" + required="Bitte einen Grund angeben." + options={ABSENCE_OPTIONS} + /> + </FormDialog> + ); +} + +function mapToRequest( + examinationId: string, + values: ReasonForAbsenceFormValues, + version: number, +): UpdateExaminationRequest { + return { + examinationId, + apiUpdateExaminationRequest: { + version, + result: { + type: "AbsenceExaminationResult", + reasonForAbsence: mapRequiredValue(values.reasonForAbsence), + }, + }, + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/CreateProphylaxisSessionSidebar.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/CreateProphylaxisSessionSidebar.tsx index caed5316b..3766357bd 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/CreateProphylaxisSessionSidebar.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/CreateProphylaxisSessionSidebar.tsx @@ -5,234 +5,88 @@ "use client"; -import { ApiAddContact200Response } from "@eshg/base-api"; -import { - ApiCreateProphylaxisSessionRequest, - ApiFluoridationVarnish, - ApiProphylaxisType, -} from "@eshg/dental-api"; import { useCreateProphylaxisSession } from "@eshg/dental/api/mutations/prophylaxisSessionApi"; -import { - getAllDentalAssistantsQuery, - getAllDentistsQuery, -} from "@eshg/dental/api/queries/staffApi"; -import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; -import { mapRequiredValue } from "@eshg/lib-portal/helpers/form"; -import { useHasChanged } from "@eshg/lib-portal/hooks/useHasChanged"; -import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; -import { Stack, Typography } from "@mui/joy"; -import { useSuspenseQueries } from "@tanstack/react-query"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Formik } from "formik"; -import { forwardRef, useEffect } from "react"; -import { useUserApi } from "@/lib/baseModule/api/clients"; -import { SCHOOL_OR_DAYCARE } from "@/lib/baseModule/api/queries/contacts"; -import { FluoridationField } from "@/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField"; -import { SearchGroupField } from "@/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField"; -import { PROPHYLAXIS_TYPE_OPTIONS } from "@/lib/businessModules/dental/features/prophylaxisSessions/options"; -import { AppointmentStaffField } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffField"; -import { SelectionOption } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection"; -import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { - SidebarForm, - SidebarFormHandle, -} from "@/lib/shared/components/form/SidebarForm"; -import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; -import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField"; -import { SelectContactField } from "@/lib/shared/components/formFields/SelectContactField"; + ProphylaxisSessionForm, + ProphylaxisSessionValues, + mapValues, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm"; +import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; +import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; -import { fullName } from "@/lib/shared/components/users/userFormatter"; -import { getInstitutionOptionLabel } from "@/lib/shared/helpers/selectOptionMapper"; import { SidebarWithFormRefProps, useSidebarWithFormRef, } from "@/lib/shared/hooks/useSidebarWithFormRef"; +import { useGetStaff } from "./staff"; + export function useCreateProphylaxisSessionSidebar() { return useSidebarWithFormRef({ component: CreateProphylaxisSessionSidebar, }); } -export interface CreateProphylaxisSessionValues { - dateAndTime: string; - institution: ApiAddContact200Response | null; - groupName: OptionalFieldValue<string>; - type: OptionalFieldValue<ApiProphylaxisType>; - isScreening: boolean; - isFluoridation: boolean; - fluoridationVarnish: OptionalFieldValue<ApiFluoridationVarnish>; - dentistIds: string[]; - zfaIds: string[]; -} - function CreateProphylaxisSessionSidebar(props: SidebarWithFormRefProps) { const createSession = useCreateProphylaxisSession(); - const userApi = useUserApi(); - const [{ data: allDentists }, { data: allDentalAssistants }] = - useSuspenseQueries({ - queries: [ - getAllDentistsQuery(userApi), - getAllDentalAssistantsQuery(userApi), - ], - }); + const { allDentists, allDentalAssistants } = useGetStaff(); + const snackbar = useSnackbar(); - const dentistOptions = allDentists.map((dentist) => ({ - value: dentist.userId, - label: fullName(dentist), - })); + const initialValues: ProphylaxisSessionValues = { + dateAndTime: "", + institution: "", + groupName: "", + type: "", + isScreening: false, + isFluoridation: false, + fluoridationVarnish: "", + dentistIds: [], + zfaIds: [], + }; - const dentalAssistantOptions = allDentalAssistants.map((dentalAssistant) => ({ - value: dentalAssistant.userId, - label: fullName(dentalAssistant), - })); + function onSubmit(values: ProphylaxisSessionValues) { + createSession + .mutateAsync( + { + ...mapValues(values), + }, + { + onSuccess: () => props.onClose(true), + }, + ) + .catch(() => + snackbar.error("Die Daten konnten nicht gespeichert werden."), + ); + } return ( - <Formik<CreateProphylaxisSessionValues> - initialValues={{ - dateAndTime: "", - institution: null, - groupName: "", - type: "", - isScreening: false, - isFluoridation: false, - fluoridationVarnish: "", - dentistIds: [], - zfaIds: [], - }} - onSubmit={(values) => - createSession.mutateAsync(mapValues(values), { - onSuccess: () => { - props.onClose(true); - }, - }) - } + <Formik<ProphylaxisSessionValues> + initialValues={initialValues} + onSubmit={onSubmit} > {({ values, handleSubmit, isSubmitting, setFieldValue }) => ( - <CreateProphylaxisSessionSidebarForm - ref={props.formRef} - onSubmit={handleSubmit} - onClose={() => props.onClose(false)} - dentistOptions={dentistOptions} - zfaOptions={dentalAssistantOptions} - values={values} - isSubmitting={isSubmitting} - setFieldValue={setFieldValue} - /> + <SidebarForm ref={props.formRef} onSubmit={handleSubmit}> + <SidebarContent title="Prophylaxe anlegen"> + <ProphylaxisSessionForm + values={values} + setFieldValue={setFieldValue} + dentistOptions={allDentists} + dentalAssistantOptions={allDentalAssistants} + /> + </SidebarContent> + <SidebarActions> + <FormButtonBar + submitLabel="Anlegen" + submitting={isSubmitting} + onCancel={() => props.onClose(false)} + /> + </SidebarActions> + </SidebarForm> )} </Formik> ); } - -interface CreateProphylaxisSessionSidebarFormProps { - onSubmit: () => void; - values: CreateProphylaxisSessionValues; - isSubmitting: boolean; - onClose: () => void; - dentistOptions: SelectionOption[]; - zfaOptions: SelectionOption[]; - setFieldValue: (field: "groupName", value: "") => void; -} - -const CreateProphylaxisSessionSidebarForm = forwardRef< - SidebarFormHandle, - CreateProphylaxisSessionSidebarFormProps ->(function CreateProphylaxisSessionSidebarForm( - { - onSubmit, - values, - isSubmitting, - onClose, - dentistOptions, - zfaOptions, - setFieldValue, - }: CreateProphylaxisSessionSidebarFormProps, - ref, -) { - const shouldClearGroupName = useHasChanged(values.institution); - useEffect(() => { - if (shouldClearGroupName) { - void setFieldValue("groupName", ""); - } - }, [shouldClearGroupName, setFieldValue, values]); - - return ( - <SidebarForm ref={ref} onSubmit={onSubmit}> - <SidebarContent title="Prophylaxe anlegen"> - <Stack gap={3}> - <DateTimeField - name="dateAndTime" - label="Datum und Uhrzeit" - required="Bitte ein Datum mit Uhrzeit angeben." - /> - <SelectContactField - name="institution" - label="Einrichtung" - categories={SCHOOL_OR_DAYCARE} - required="Bitte eine Schule/Kita angeben." - getOptionLabel={getInstitutionOptionLabel} - /> - <SearchGroupField - name="groupName" - label="Gruppe" - institutionId={values.institution?.id ?? ""} - /> - <SelectField - name="type" - label="Typ" - options={PROPHYLAXIS_TYPE_OPTIONS} - required="Bitte den Typ der Prophylaxe angeben." - /> - <CheckboxField name="isScreening" label="Reihenuntersuchung" /> - <FluoridationField /> - <Typography component="h3" level="title-sm"> - Durchführende Personen - </Typography> - <AppointmentStaffField - name="dentistIds" - options={dentistOptions} - blockedStaff={[]} - freeStaff={[]} - label="Zahnarzt/-ärztin" - required="Bitte mindestens eine/n Zahnarzt/-ärztin angeben." - /> - <AppointmentStaffField - name="zfaIds" - options={zfaOptions} - blockedStaff={[]} - freeStaff={[]} - label="ZFA" - required="Bitte mindestens eine/n ZFA angeben." - /> - </Stack> - </SidebarContent> - <SidebarActions> - <FormButtonBar - submitLabel="Anlegen" - submitting={isSubmitting} - onCancel={() => { - onClose(); - }} - /> - </SidebarActions> - </SidebarForm> - ); -}); - -function mapValues( - values: CreateProphylaxisSessionValues, -): ApiCreateProphylaxisSessionRequest { - return { - dateAndTime: new Date(values.dateAndTime), - institutionId: mapRequiredValue(values.institution)?.id, - groupName: mapRequiredValue(values.groupName), - type: mapRequiredValue(values.type), - isScreening: values.isScreening, - fluoridationVarnish: values.isFluoridation - ? mapRequiredValue(values.fluoridationVarnish) - : undefined, - dentistIds: values.dentistIds, - zfaIds: values.zfaIds, - }; -} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField.tsx index 5e24c86e2..89d68da31 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField.tsx @@ -10,18 +10,27 @@ import { useField } from "formik"; import { FLUORIDATION_VARNISH_OPTIONS } from "@/lib/businessModules/dental/features/prophylaxisSessions/options"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; -export function FluoridationField() { +interface FluoridationFieldProps { + disabled?: boolean; +} + +export function FluoridationField(props: FluoridationFieldProps) { const [isFluoridation] = useField<boolean>("isFluoridation"); return ( <Stack gap={3}> - <CheckboxField name="isFluoridation" label="Fluoridierung" /> + <CheckboxField + name="isFluoridation" + label="Fluoridierung" + disabled={props.disabled} + /> {isFluoridation.value && ( <SelectField name="fluoridationVarnish" label="Lack" options={FLUORIDATION_VARNISH_OPTIONS} required="Bitte den Lack auswählen." + disabled={props.disabled} /> )} </Stack> diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ParticipantFilter.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ParticipantFilter.tsx index eb47d99b3..f3c6f936b 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ParticipantFilter.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ParticipantFilter.tsx @@ -6,8 +6,8 @@ import { Radio, RadioGroup, Typography } from "@mui/joy"; import { useId } from "react"; -import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; -import { ParticipantFilters } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/participantFilters"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; +import { ParticipantFilters } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantFilters"; export interface ParticipantFilterDef<TValue extends string> { label: string; diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionDetails.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionDetails.tsx index 816a2290e..b73e79ce6 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionDetails.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionDetails.tsx @@ -10,57 +10,64 @@ import { Person } from "@mui/icons-material"; import { Stack, Typography } from "@mui/joy"; import { ProphylaxisSessionParticipantsTable } from "@/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionParticipantsTable"; -import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; +import { useUpdateProphylaxisSessionSidebar } from "@/lib/businessModules/dental/features/prophylaxisSessions/UpdateProphylaxisSessionSidebar"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; import { PROPHYLAXIS_TYPES, fluoridationDescription, } from "@/lib/businessModules/dental/features/prophylaxisSessions/translations"; import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow"; import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { displayBoolean } from "@/lib/shared/helpers/booleans"; export function ProphylaxisSessionDetails() { const prophylaxisSession = useProphylaxisSessionStore((state) => state); + const updateProphylaxisSidebar = useUpdateProphylaxisSessionSidebar(); return ( <Stack gap={4}> <ContentPanel testId="prophylaxis-session-panel"> <DetailsSection title="Allgemeine Informationen" + onEdit={() => + updateProphylaxisSidebar.open({ + prophylaxisSession: prophylaxisSession, + }) + } data-testid="prophylaxis-details" > <DetailsRow> <DetailsColumn> - <DetailsCell + <DetailsItem label="Datum" value={formatDateTime(prophylaxisSession.dateAndTime)} /> - <DetailsCell + <DetailsItem label="Einrichtung" value={prophylaxisSession.institution.name} /> - <DetailsCell + <DetailsItem label="Gruppe" value={prophylaxisSession.groupName} /> </DetailsColumn> <DetailsColumn> - <DetailsCell + <DetailsItem label="Typ" value={PROPHYLAXIS_TYPES[prophylaxisSession.type]} /> - <DetailsCell + <DetailsItem label="Reihenuntersuchung" value={displayBoolean(prophylaxisSession.isScreening)} /> - <DetailsCell + <DetailsItem label="Teilnehmer" value={prophylaxisSession.participants.length} /> - <DetailsCell + <DetailsItem label="Fluoridierung" value={fluoridationDescription( prophylaxisSession.fluoridationVarnish, @@ -68,13 +75,13 @@ export function ProphylaxisSessionDetails() { /> </DetailsColumn> <DetailsColumn> - <DetailsCell + <DetailsItem label="Zahnarzt/-ärztin" value={ <PerformingPersons persons={prophylaxisSession.dentists} /> } /> - <DetailsCell + <DetailsItem label="ZFA" value={<PerformingPersons persons={prophylaxisSession.zfas} />} /> diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionFilterSettings.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionFilterSettings.tsx index ee43b0173..91c2981d1 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionFilterSettings.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionFilterSettings.tsx @@ -71,6 +71,7 @@ export function ProphylaxisSessionFilterSettings( onChange={(institutionId) => props.setFilterFormValue("institutionIdFilter", institutionId) } + placeholder="Schule/Kita suchen" /> </FormControl> <FormControl> diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm.tsx new file mode 100644 index 000000000..64b81425b --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm.tsx @@ -0,0 +1,146 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiFluoridationVarnish, ApiProphylaxisType } from "@eshg/dental-api"; +import { Institution } from "@eshg/dental/api/models/Institution"; +import { Alert } from "@eshg/lib-portal/components/Alert"; +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { + mapOptionalValue, + mapRequiredValue, +} from "@eshg/lib-portal/helpers/form"; +import { useHasChanged } from "@eshg/lib-portal/hooks/useHasChanged"; +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +import { Stack, Typography } from "@mui/joy"; +import { useEffect } from "react"; + +import { SCHOOL_OR_DAYCARE } from "@/lib/baseModule/api/queries/contacts"; +import { FluoridationField } from "@/lib/businessModules/dental/features/prophylaxisSessions/FluoridationField"; +import { SearchGroupField } from "@/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField"; +import { PROPHYLAXIS_TYPE_OPTIONS } from "@/lib/businessModules/dental/features/prophylaxisSessions/options"; +import { + AppointmentStaffField, + StaffUser, +} from "@/lib/shared/components/appointmentBlocks/AppointmentStaffField"; +import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; +import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField"; +import { SelectContactField } from "@/lib/shared/components/formFields/SelectContactField"; +import { getInstitutionOptionLabel } from "@/lib/shared/helpers/selectOptionMapper"; + +interface ProphylaxisSessionFormProps { + values: ProphylaxisSessionValues; + setFieldValue: (field: "groupName", value: "") => void; + dentistOptions: StaffUser[]; + dentalAssistantOptions: StaffUser[]; + hasExaminationResults?: boolean; +} + +export interface ProphylaxisSessionValues { + dateAndTime: string; + institution: OptionalFieldValue<Institution>; + groupName: string; + type: OptionalFieldValue<ApiProphylaxisType>; + isScreening: boolean; + isFluoridation: boolean; + fluoridationVarnish: OptionalFieldValue<ApiFluoridationVarnish>; + dentistIds: string[]; + zfaIds: string[]; +} + +export function ProphylaxisSessionForm(props: ProphylaxisSessionFormProps) { + const { + values, + setFieldValue, + dentistOptions, + dentalAssistantOptions, + hasExaminationResults, + } = props; + + const shouldClearGroupName = useHasChanged(values.institution); + useEffect(() => { + if (shouldClearGroupName) { + void setFieldValue("groupName", ""); + } + }, [shouldClearGroupName, setFieldValue, values]); + + return ( + <Stack gap={3}> + {hasExaminationResults && ( + <Alert + color="primary" + message="Da es bereits Untersuchungsergebnisse zu dieser Prophylaxe gibt, können einige Daten nicht mehr geändert werden." + /> + )} + <DateTimeField + name="dateAndTime" + label="Datum und Uhrzeit" + required="Bitte ein Datum mit Uhrzeit angeben." + /> + <SelectContactField + name="institution" + label="Einrichtung" + placeholder="Schule/Kita suchen" + categories={SCHOOL_OR_DAYCARE} + required="Bitte eine Schule/Kita angeben." + getOptionLabel={(institution) => + institution ? getInstitutionOptionLabel(institution) : "" + } + disabled={hasExaminationResults} + /> + <SearchGroupField + name="groupName" + label="Gruppe" + institutionId={mapOptionalValue(values.institution)?.id ?? ""} + disabled={hasExaminationResults} + /> + <SelectField + name="type" + label="Typ" + options={PROPHYLAXIS_TYPE_OPTIONS} + required="Bitte den Typ der Prophylaxe angeben." + /> + <CheckboxField + name="isScreening" + label="Reihenuntersuchung" + disabled={hasExaminationResults} + /> + <FluoridationField disabled={hasExaminationResults} /> + <Typography component="h3" level="title-sm"> + Durchführende Personen + </Typography> + <AppointmentStaffField + name="dentistIds" + options={dentistOptions} + blockedStaff={[]} + freeStaff={[]} + label="Zahnarzt/-ärztin" + required="Bitte mindestens eine/n Zahnarzt/-ärztin angeben." + /> + <AppointmentStaffField + name="zfaIds" + options={dentalAssistantOptions} + blockedStaff={[]} + freeStaff={[]} + label="ZFA" + required="Bitte mindestens eine/n ZFA angeben." + /> + </Stack> + ); +} + +export function mapValues(values: ProphylaxisSessionValues) { + return { + dateAndTime: new Date(values.dateAndTime), + institutionId: mapRequiredValue(values.institution)?.id, + groupName: mapRequiredValue(values.groupName), + type: mapRequiredValue(values.type), + isScreening: values.isScreening, + fluoridationVarnish: values.isFluoridation + ? mapRequiredValue(values.fluoridationVarnish) + : undefined, + dentistIds: values.dentistIds, + zfaIds: values.zfaIds, + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionParticipantsTable.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionParticipantsTable.tsx index 0993924c0..0f83d269f 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionParticipantsTable.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionParticipantsTable.tsx @@ -10,14 +10,29 @@ import { GENDER_VALUES } from "@eshg/lib-portal/components/formFields/constants" import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { Add } from "@mui/icons-material"; +import CancelIcon from "@mui/icons-material/Cancel"; import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import { Button, Divider, Stack, Typography } from "@mui/joy"; import { createColumnHelper } from "@tanstack/react-table"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { isDefined } from "remeda"; import { ExaminationStatusChip } from "@/lib/businessModules/dental/features/examinations/ExaminationStatusChip"; import { useAddChildToProphylaxisSessionSidebar } from "@/lib/businessModules/dental/features/prophylaxisSessions/AddChildToProphylaxisSessionSidebar"; +import { ChangeReasonForAbsenceModal } from "@/lib/businessModules/dental/features/prophylaxisSessions/ChangeReasonForAbsenceModal"; +import { + useFilteredParticipants, + useProphylaxisSessionStore, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; +import { + FluoridationConsentFilter, + GenderFilter, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantFilters"; +import { + ParticipantSortKey, + ParticipantSorting, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantSorting"; +import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary"; import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu"; import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; import { DataTable } from "@/lib/shared/components/table/DataTable"; @@ -30,18 +45,6 @@ import { import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; import { ParticipantFilter, ParticipantFilterDef } from "./ParticipantFilter"; -import { - useFilteredParticipants, - useProphylaxisSessionStore, -} from "./store/ProphylaxisSessionStoreProvider"; -import { - FluoridationConsentFilter, - GenderFilter, -} from "./store/participantFilters"; -import { - ParticipantSortKey, - ParticipantSorting, -} from "./store/participantSorting"; const GENDER_FILTERS: ParticipantFilterDef<GenderFilter>[] = [ { label: "Alle", value: "ANY" }, @@ -78,6 +81,10 @@ export function ProphylaxisSessionParticipantsTable() { prophylaxisSessionVersion, allParticipants, ); + const [ + openReasonForAbsenceModalOfParticipant, + setOpenReasonForAbsenceModalOfParticipant, + ] = useState<ChildExamination>(); function handleRemoveParticipant(childExternalId: string) { openConfirmationDialog({ @@ -93,6 +100,14 @@ export function ProphylaxisSessionParticipantsTable() { }); } + function handleAbsentParticipant(examination: ChildExamination) { + setOpenReasonForAbsenceModalOfParticipant(examination); + } + + function closeAbsenceModal() { + setOpenReasonForAbsenceModalOfParticipant(undefined); + } + function routeToExamination(participantIndex: number) { return routes.prophylaxisSessions .byId(prophylaxisSessionId) @@ -153,9 +168,11 @@ export function ProphylaxisSessionParticipantsTable() { right={ <> <AddChildButton /> - <InternalLinkButton href={routeToExamination(0)}> - Reihenuntersuchung starten - </InternalLinkButton> + {filteredParticipants.length > 0 && ( + <InternalLinkButton href={routeToExamination(0)}> + Prophylaxe starten + </InternalLinkButton> + )} </> } /> @@ -163,10 +180,7 @@ export function ProphylaxisSessionParticipantsTable() { > <DataTable data={filteredParticipants} - columns={columnDefs( - filteredParticipants.length, - handleRemoveParticipant, - )} + columns={columnDefs(handleRemoveParticipant, handleAbsentParticipant)} rowNavigation={{ focusColumnAccessorKey: "lastName", route: (row) => routeToExamination(row.index), @@ -175,6 +189,14 @@ export function ProphylaxisSessionParticipantsTable() { enableSortingRemoval={false} minWidth={1200} /> + {isDefined(openReasonForAbsenceModalOfParticipant) && ( + <OverlayBoundary> + <ChangeReasonForAbsenceModal + onClose={closeAbsenceModal} + examination={openReasonForAbsenceModalOfParticipant} + /> + </OverlayBoundary> + )} </TablePage> ); } @@ -182,10 +204,10 @@ export function ProphylaxisSessionParticipantsTable() { const columnHelper = createColumnHelper<ChildExamination>(); function columnDefs( - participantsSize: number, onRemoveParticipant: (participantId: string) => void, + onAbsentParticipant: (examination: ChildExamination) => void, ) { - const columnDefs = [ + return [ columnHelper.accessor("firstName", { header: "Vorname", cell: (props) => props.getValue(), @@ -252,17 +274,22 @@ function columnDefs( width: 110, }, }), - ]; - - if (participantsSize > 1) { - return [ - ...columnDefs, - columnHelper.display({ - header: "Aktionen", - id: "actions", - cell: (props) => ( + columnHelper.display({ + header: "Aktionen", + id: "actions", + cell: (props) => + childCanBeRemoved(props.row.original) ? ( <ActionsMenu actionItems={[ + ...(props.row.original.status !== "CLOSED" + ? [ + { + label: "Nicht anwesend", + startDecorator: <CancelIcon />, + onClick: () => onAbsentParticipant(props.row.original), + }, + ] + : []), { label: "Entfernen", startDecorator: <DeleteIcon />, @@ -271,17 +298,14 @@ function columnDefs( }, ]} /> - ), - meta: { - width: 80, - cellStyle: "button", - textAlign: "right", - }, - }), - ]; - } - - return columnDefs; + ) : undefined, + meta: { + width: 80, + cellStyle: "button", + textAlign: "right", + }, + }), + ]; } function resolveTableSorting( @@ -331,3 +355,7 @@ function AddChildButton() { </Button> ); } + +function childCanBeRemoved(child: ChildExamination) { + return child.result === undefined; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField.tsx index 0b4c42496..0ac10f039 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/SearchGroupField.tsx @@ -5,7 +5,6 @@ import { useSearchInstitutionGroups } from "@eshg/dental/api/queries/childApi"; import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; -import { SearchOutlined } from "@mui/icons-material"; import { mapToSelectOption } from "@/lib/shared/helpers/selectOptionMapper"; @@ -14,6 +13,7 @@ interface SearchGroupFieldProps { label: string; institutionId: string; freeSolo?: boolean; + disabled?: boolean; } export function SearchGroupField(props: SearchGroupFieldProps) { @@ -28,9 +28,9 @@ export function SearchGroupField(props: SearchGroupFieldProps) { required="Bitte eine Gruppe angeben." options={options} placeholder="Gruppe suchen" - endDecorator={<SearchOutlined />} loading={searchGroups.isLoading} freeSolo={props.freeSolo} + disabled={props.disabled} /> ); } diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/UpdateProphylaxisSessionSidebar.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/UpdateProphylaxisSessionSidebar.tsx new file mode 100644 index 000000000..d0ed6cd55 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/UpdateProphylaxisSessionSidebar.tsx @@ -0,0 +1,109 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { ProphylaxisSessionDetails } from "@eshg/dental/api/models/ProphylaxisSessionDetails"; +import { useUpdateProphylaxisSession } from "@eshg/dental/api/mutations/prophylaxisSessionApi"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { toDateTimeString } from "@eshg/lib-portal/helpers/dateTime"; +import { parseOptionalValue } from "@eshg/lib-portal/helpers/form"; +import { Formik } from "formik"; + +import { + ProphylaxisSessionForm, + ProphylaxisSessionValues, + mapValues, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/ProphylaxisSessionForm"; +import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; +import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; +import { + SidebarWithFormRefProps, + UseSidebarWithFormRefResult, + useSidebarWithFormRef, +} from "@/lib/shared/hooks/useSidebarWithFormRef"; + +import { useGetStaff } from "./staff"; + +export function useUpdateProphylaxisSessionSidebar(): UseSidebarWithFormRefResult<UpdateProphylaxisSessionSidebarProps> { + return useSidebarWithFormRef({ + component: UpdateProphylaxisSessionSidebar, + }); +} + +interface UpdateProphylaxisSessionSidebarProps extends SidebarWithFormRefProps { + prophylaxisSession: ProphylaxisSessionDetails; +} + +function UpdateProphylaxisSessionSidebar( + props: UpdateProphylaxisSessionSidebarProps, +) { + const prophylaxisSession = props.prophylaxisSession; + const updateSession = useUpdateProphylaxisSession(prophylaxisSession.id); + const { allDentists, allDentalAssistants } = useGetStaff(); + const snackbar = useSnackbar(); + + const hasExaminationResults = prophylaxisSession.participants.some( + (item) => !!item.result, + ); + + const initialValues: ProphylaxisSessionValues = { + dateAndTime: toDateTimeString(prophylaxisSession.dateAndTime), + institution: prophylaxisSession.institution, + groupName: prophylaxisSession.groupName, + type: prophylaxisSession.type, + isScreening: prophylaxisSession.isScreening, + isFluoridation: !!prophylaxisSession.fluoridationVarnish, + fluoridationVarnish: parseOptionalValue( + prophylaxisSession.fluoridationVarnish, + ), + dentistIds: prophylaxisSession.dentists.map((dentist) => dentist.id), + zfaIds: prophylaxisSession.zfas.map((zfa) => zfa.id), + }; + + function onSubmit(values: ProphylaxisSessionValues) { + updateSession + .mutateAsync( + { + version: prophylaxisSession.version, + ...mapValues(values), + }, + { + onSuccess: () => props.onClose(true), + }, + ) + .catch(() => snackbar.error("Die Daten konnten nicht geändert werden.")); + } + + return ( + <Formik<ProphylaxisSessionValues> + initialValues={initialValues} + onSubmit={onSubmit} + > + {({ values, handleSubmit, isSubmitting, setFieldValue }) => ( + <SidebarForm ref={props.formRef} onSubmit={handleSubmit}> + <SidebarContent title="Prophylaxe ändern"> + <ProphylaxisSessionForm + values={values} + setFieldValue={setFieldValue} + dentistOptions={allDentists} + dentalAssistantOptions={allDentalAssistants} + hasExaminationResults={hasExaminationResults} + /> + </SidebarContent> + <SidebarActions> + <FormButtonBar + submitLabel="Ändern" + submitting={isSubmitting} + onCancel={() => props.onClose(false)} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/AddToothButton.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/AddToothButton.tsx new file mode 100644 index 000000000..b3f5c30c3 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/AddToothButton.tsx @@ -0,0 +1,33 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import { IconButton } from "@mui/joy"; + +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { QuadrantNumber } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +interface AddToothButtonProps { + index: number; + quadrantNumber: QuadrantNumber; +} + +export function AddToothButton(props: AddToothButtonProps) { + const addTooth = useDentalExaminationStore((state) => state.addTooth); + + return ( + <IconButton + sx={{ padding: 2 }} + onClick={() => { + addTooth({ + quadrantNumber: props.quadrantNumber, + toothIndex: props.index, + }); + }} + > + <AddCircleIcon color="primary" /> + </IconButton> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection.tsx new file mode 100644 index 000000000..73b753bd5 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection.tsx @@ -0,0 +1,24 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { DentalExaminationJawTabs } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationJawTabs"; +import { FullDentitionOverview } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/FullDentitionOverview"; +import { Legend } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Legend"; +import { LowerJawForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/LowerJawForm"; +import { UpperJawForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/UpperJawForm"; +import { InformationSheet } from "@/lib/shared/components/infoTile/InformationSheet"; + +export function DentalExaminationFormSection() { + return ( + <InformationSheet> + <DentalExaminationJawTabs + upperJaw={<UpperJawForm />} + lowerJaw={<LowerJawForm />} + fullDentition={<FullDentitionOverview />} + /> + <Legend /> + </InformationSheet> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationJawTabs.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationJawTabs.tsx new file mode 100644 index 000000000..2de94be37 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationJawTabs.tsx @@ -0,0 +1,70 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Box, Button, Stack, ToggleButtonGroup } from "@mui/joy"; +import { ReactNode } from "react"; + +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { DentalExaminationView } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +interface DentalExaminationJawTabsProps { + upperJaw: ReactNode; + lowerJaw: ReactNode; + fullDentition: ReactNode; +} + +export function DentalExaminationJawTabs({ + upperJaw, + lowerJaw, + fullDentition, +}: DentalExaminationJawTabsProps) { + const currentView = useDentalExaminationStore((state) => state.currentView); + const setView = useDentalExaminationStore((state) => state.setView); + + function getCurrentContent(view: DentalExaminationView) { + switch (view) { + case "UPPER_JAW": + return upperJaw; + case "LOWER_JAW": + return lowerJaw; + case "FULL_DENTITION": + return fullDentition; + } + } + + return ( + <Stack alignItems="center" spacing={2}> + <ToggleButtonGroup + variant="tabs" + color="primary" + size="md" + value={currentView} + onChange={(_, newValue) => setView(newValue ?? "UPPER_JAW")} + sx={{ + width: { xxs: "100%", md: "65%" }, + display: "flex", + }} + > + <Button sx={{ flex: "1 1 0%" }} value="UPPER_JAW"> + Oberkiefer + </Button> + <Button sx={{ flex: "1 1 0%" }} value="LOWER_JAW"> + Unterkiefer + </Button> + <Button sx={{ flex: "1 1 0%" }} value="FULL_DENTITION"> + Gesamtgebiss + </Button> + </ToggleButtonGroup> + <Box + sx={{ + overflow: "auto", + maxWidth: "100%", + }} + > + <Box sx={{ minWidth: "1121px" }}>{getCurrentContent(currentView)}</Box> + </Box> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/FullDentitionOverview.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/FullDentitionOverview.tsx new file mode 100644 index 000000000..0ab8b9582 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/FullDentitionOverview.tsx @@ -0,0 +1,129 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Button, Grid, Stack, Typography } from "@mui/joy"; +import { SxProps } from "@mui/joy/styles/types"; + +import { Quadrant } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant"; +import { + QuadrantHeading, + QuadrantHeadingRow, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading"; +import { ToothIcon } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth"; +import { ToothNumber } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber"; +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { + QuadrantNumber, + Tooth, + isToothWithDiagnosis, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +export function FullDentitionOverview() { + return ( + <Stack> + <QuadrantHeadingRow marginBottom="24px"> + <QuadrantHeading name="Oberkiefer rechts" index={1} /> + <QuadrantHeading name="Oberkiefer links" index={2} /> + </QuadrantHeadingRow> + <Grid container> + <QuadrantSection quadrantNumber="Q1" /> + <QuadrantSection quadrantNumber="Q2" /> + </Grid> + <Grid container> + <QuadrantSection quadrantNumber="Q4" /> + <QuadrantSection quadrantNumber="Q3" /> + </Grid> + <QuadrantHeadingRow> + <QuadrantHeading name="Unterkiefer rechts" index={4} /> + <QuadrantHeading name="Unterkiefer links" index={3} /> + </QuadrantHeadingRow> + </Stack> + ); +} + +interface QuadrantSectionProps { + quadrantNumber: QuadrantNumber; +} + +function QuadrantSection({ quadrantNumber }: QuadrantSectionProps) { + const styles: SxProps = { + padding: + quadrantNumber === "Q1" || quadrantNumber === "Q4" + ? "20px 20px 20px 0" + : "20px 0 20px 20px", + borderTop: + quadrantNumber === "Q3" || quadrantNumber === "Q4" + ? "0.5px solid black" + : "none", + borderRight: + quadrantNumber === "Q1" || quadrantNumber === "Q4" + ? "0.5px solid black" + : "none", + borderBottom: + quadrantNumber === "Q1" || quadrantNumber === "Q2" + ? "0.5px solid black" + : "none", + borderLeft: + quadrantNumber === "Q2" || quadrantNumber === "Q3" + ? "0.5px solid black" + : "none", + }; + + const setFocus = useDentalExaminationStore((state) => state.setFocus); + return ( + <Grid xxs={6} sx={styles}> + <Quadrant quadrantNumber={quadrantNumber} gap={0}> + {(tooth, index) => ( + <Button + key={tooth.toothNumber} + variant="plain" + sx={{ + padding: "4px", + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "flex-start", + gap: 2, + backgroundColor: "none", + }} + onClick={() => + setFocus({ + toothContext: { + quadrantNumber, + toothIndex: index, + }, + field: "main", + }) + } + > + <ToothNumber tooth={tooth} /> + <ToothIcon tooth={tooth} /> + <ExaminationResult tooth={tooth} /> + </Button> + )} + </Quadrant> + </Grid> + ); +} + +function ExaminationResult({ tooth }: { tooth: Tooth }) { + if (!isToothWithDiagnosis(tooth)) { + return undefined; + } + const mainResult = tooth.mainResult; + const secondaryResult1 = tooth.secondaryResult1; + const secondaryResult2 = tooth.secondaryResult2; + return ( + <Stack sx={{ alignItems: "center" }}> + <Typography>{mainResult?.value ? mainResult.value : "-"}</Typography> + <Typography> + {secondaryResult1?.value ? secondaryResult1.value : undefined} + </Typography> + <Typography> + {secondaryResult2?.value ? secondaryResult2.value : undefined} + </Typography> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm.tsx new file mode 100644 index 000000000..62657d112 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm.tsx @@ -0,0 +1,93 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiMainResult } from "@eshg/dental-api"; +import { isEmptyString } from "@eshg/lib-portal/helpers/guards"; +import { Stack, Typography } from "@mui/joy"; + +import { AddToothButton } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/AddToothButton"; +import { Quadrant } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant"; +import { ToothIcon } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth"; +import { ToothNumber } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber"; +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { + QuadrantNumber, + ToothWithDiagnosis, + isAddableTooth, + isToothWithDiagnosis, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +import { ResultInputField } from "./ResultInputField"; + +export function GeneralJawForm(props: { quadrantNumber: QuadrantNumber }) { + const setMainResult = useDentalExaminationStore( + (state) => state.setMainResult, + ); + const setSecondaryResult1 = useDentalExaminationStore( + (state) => state.setSecondaryResult1, + ); + const setSecondaryResult2 = useDentalExaminationStore( + (state) => state.setSecondaryResult2, + ); + + return ( + <Quadrant quadrantNumber={props.quadrantNumber}> + {(tooth, index) => ( + <Stack key={tooth.toothNumber} sx={{ gap: 2, alignItems: "center" }}> + {isToothWithDiagnosis(tooth) && ( + <> + <ToothNumber tooth={tooth} /> + <ToothIcon tooth={tooth} /> + <ResultInputField + result={tooth.mainResult} + index={index} + quadrantNumber={props.quadrantNumber} + setResultAction={setMainResult} + field="main" + variant={ + isEmptyString(tooth.mainResult.value) ? "soft" : "outlined" + } + /> + <ResultInputField + result={tooth.secondaryResult1} + index={index} + quadrantNumber={props.quadrantNumber} + setResultAction={setSecondaryResult1} + field="secondary1" + /> + <ResultInputField + result={tooth.secondaryResult2} + index={index} + quadrantNumber={props.quadrantNumber} + setResultAction={setSecondaryResult2} + field="secondary2" + /> + {hasPreviousExaminationResult(tooth) && ( + <Typography color="danger"> + {tooth.previousResults.join(",")} + </Typography> + )} + </> + )} + {isAddableTooth(tooth) && ( + <AddToothButton + index={index} + quadrantNumber={props.quadrantNumber} + /> + )} + </Stack> + )} + </Quadrant> + ); +} + +export function hasPreviousExaminationResult( + tooth: ToothWithDiagnosis, +): boolean { + return ( + tooth.previousResults.length > 0 && + tooth.previousResults[0] !== ApiMainResult.S + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading.tsx new file mode 100644 index 000000000..4991dae5e --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading.tsx @@ -0,0 +1,39 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Grid, Stack } from "@mui/joy"; +import { ReactNode } from "react"; + +interface JawFormWithHeadingProps { + heading: ReactNode; + left: ReactNode; + right: ReactNode; +} + +export function JawWithHeading(props: JawFormWithHeadingProps) { + return ( + <Stack> + {props.heading} + <Grid container sx={{ flexWrap: "nowrap" }}> + <Grid + sx={{ + borderRight: "0.5px solid black", + padding: "24px 24px 24px 4px", + }} + > + {props.left} + </Grid> + <Grid + sx={{ + borderLeft: "0.5px solid black", + padding: "24px 4px 24px 24px", + }} + > + {props.right} + </Grid> + </Grid> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Legend.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Legend.tsx new file mode 100644 index 000000000..ad524cd84 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Legend.tsx @@ -0,0 +1,138 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import CircleIcon from "@mui/icons-material/Circle"; +import CircleOutlinedIcon from "@mui/icons-material/CircleOutlined"; +import ErrorIcon from "@mui/icons-material/Error"; +import { Button, Stack, Typography } from "@mui/joy"; +import { ReactNode } from "react"; + +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 Legend() { + const findingsOverviewSidebar = useSidebar({ + component: FindingsOverviewSidebar, + }); + return ( + <Stack direction="row" sx={{ justifyContent: "space-between" }}> + <Stack direction="row" gap={3}> + <LegendItem + icon={<ErrorIcon color="danger" />} + helpText="Vorbefund vorhanden" + /> + <LegendItem + icon={<CircleIcon color="neutral" />} + helpText="Bleibender Zahn" + /> + <LegendItem + icon={<CircleOutlinedIcon color="neutral" />} + helpText="Milchzahn" + /> + </Stack> + <Button variant="plain" onClick={findingsOverviewSidebar.open}> + <Typography component="u" color="primary"> + Befundwerte? + </Typography> + </Button> + </Stack> + ); +} + +interface LegendItemProps { + icon: ReactNode; + helpText: string; +} + +function LegendItem({ icon, helpText }: LegendItemProps) { + return ( + <Stack direction="row" gap={0.5} alignItems="center"> + {icon} + <Typography>= {helpText}</Typography> + </Stack> + ); +} + +function FindingsOverviewSidebar({ onClose }: DrawerProps) { + return ( + <> + <SidebarContent title="Mögliche Befundwerte"> + <Stack> + {Object.entries(POSSIBLE_DIAGNOSES).map(([abbr, expl]) => ( + <Diagnosis + key={abbr} + abbreviation={abbr as Abbreviation} + explanation={expl} + /> + ))} + </Stack> + </SidebarContent> + <SidebarActions> + <ButtonBar + right={[ + <Button + color="neutral" + variant="soft" + key="close" + onClick={() => onClose()} + > + Schließen + </Button>, + ]} + /> + </SidebarActions> + </> + ); +} + +type Abbreviation = keyof typeof POSSIBLE_DIAGNOSES; +type Explanation = (typeof POSSIBLE_DIAGNOSES)[keyof typeof POSSIBLE_DIAGNOSES]; + +const POSSIBLE_DIAGNOSES = { + S: "Kariesfrei", + I: "Initialkaries", + D: "Kariös", + F: "gefüllt", + E: "Extrahiert", + Y: "KFO-Extr.", + X: "Nichtanlage", + Z: "Zerstört", + T: "Trauma", + H: "Hypoplasie", + O: "Trep/Fistel", + V: "versiegelt", + N: "Nicht beurteilbar", + P: "Platzhalter", + da: "Doppelte Anlage", + fa: "Formanomalie", + fis: "Fistel", + id: "Im Durchbruch", + ins: "insuffizie", + K: "Krone", + lü: "Lückenschluss", + ret: "retinierter Zahn", + tr: "trepaniert", + wr: "Wurzelrest", + za: "Zapfenzahn", +}; + +interface DiagnosisProp { + abbreviation: Abbreviation; + explanation: Explanation; +} + +function Diagnosis({ abbreviation, explanation }: DiagnosisProp) { + return ( + <Stack direction="row" gap={2}> + <Typography sx={{ fontWeight: 600, width: 24 }}> + {abbreviation} + </Typography> + <Typography>= {explanation}</Typography> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/LowerJawForm.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/LowerJawForm.tsx new file mode 100644 index 000000000..947c229ba --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/LowerJawForm.tsx @@ -0,0 +1,26 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { GeneralJawForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm"; +import { JawWithHeading } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading"; +import { + QuadrantHeading, + QuadrantHeadingRow, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading"; + +export function LowerJawForm() { + return ( + <JawWithHeading + heading={ + <QuadrantHeadingRow marginBottom="24px"> + <QuadrantHeading name="Unterkiefer rechts" index={4} /> + <QuadrantHeading name="Unterkiefer links" index={3} /> + </QuadrantHeadingRow> + } + left={<GeneralJawForm quadrantNumber="Q4" />} + right={<GeneralJawForm quadrantNumber="Q3" />} + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant.tsx new file mode 100644 index 000000000..d8510a2a3 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Quadrant.tsx @@ -0,0 +1,31 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Stack } from "@mui/joy"; +import { Property } from "csstype"; +import { ReactNode } from "react"; + +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { + QuadrantNumber, + Tooth, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +interface QuadrantProps { + quadrantNumber: QuadrantNumber; + children: (tooth: Tooth, index: number) => ReactNode; + gap?: Property.Gap; +} + +export function Quadrant(props: QuadrantProps) { + const dentition = useDentalExaminationStore((state) => state.dentition); + return ( + <Stack gap={props.gap ?? 1} direction="row"> + {dentition[props.quadrantNumber].teeth.map((tooth, index) => + props.children(tooth, index), + )} + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading.tsx new file mode 100644 index 000000000..73f26ea9e --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading.tsx @@ -0,0 +1,47 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { Stack, Typography } from "@mui/joy"; +import { Property } from "csstype"; + +import { theme } from "@/lib/baseModule/theme/theme"; + +interface QuadrantHeadingRowProps extends RequiresChildren { + marginTop?: Property.MarginTop; + marginBottom?: Property.MarginBottom; +} + +export function QuadrantHeadingRow(props: QuadrantHeadingRowProps) { + return ( + <Stack + direction="row" + sx={{ + justifyContent: "space-between", + marginTop: props.marginTop, + marginBottom: props.marginBottom, + }} + > + {props.children} + </Stack> + ); +} + +export function QuadrantHeading(props: { name: string; index: number }) { + return ( + <Typography component="h3"> + <Typography + component="span" + sx={{ + fontSize: theme.fontSize.md, + fontWeight: theme.fontWeight.lg, + }} + > + {props.name} + </Typography>{" "} + - Quadrant {props.index} + </Typography> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ResultInputField.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ResultInputField.tsx new file mode 100644 index 000000000..aff3bf8b7 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ResultInputField.tsx @@ -0,0 +1,75 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Input, VariantProp } from "@mui/joy"; +import { useEffect, useRef } from "react"; + +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { SetToothResultAction } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/dentalExaminationStore"; +import { + FieldVariant, + QuadrantNumber, + ToothResult, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +interface ResultInputFieldProps { + quadrantNumber: QuadrantNumber; + index: number; + setResultAction: SetToothResultAction; + field: FieldVariant; + result: ToothResult; + variant?: VariantProp; +} + +export function ResultInputField(props: ResultInputFieldProps) { + const focus = useDentalExaminationStore((state) => state.focus); + const setFocus = useDentalExaminationStore((state) => state.setFocus); + const input = useRef<HTMLInputElement>(null); + + const { quadrantNumber, toothIndex } = focus.toothContext; + const focusReferencesThisInput = + quadrantNumber === props.quadrantNumber && + toothIndex === props.index && + focus.field === props.field; + + useEffect(() => { + if (focusReferencesThisInput) { + input?.current?.focus(); + } + }, [input, focusReferencesThisInput]); + + function handleOnFocus() { + if (!focusReferencesThisInput) { + setFocus({ + toothContext: { + quadrantNumber: props.quadrantNumber, + toothIndex: props.index, + }, + field: props.field, + }); + } + } + + return ( + <Input + slotProps={{ input: { ref: input } }} + value={props.result.value} + sx={{ width: 60 }} + color={props.result.isInvalid ? "danger" : "primary"} + type="text" + variant={props.variant} + onFocus={handleOnFocus} + onChange={(event) => { + props.setResultAction( + { + quadrantNumber: props.quadrantNumber, + toothIndex: props.index, + }, + event.target.value.toUpperCase(), + ); + }} + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth.tsx new file mode 100644 index 000000000..0000aaf6e --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/Teeth.tsx @@ -0,0 +1,262 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import ClearIcon from "@mui/icons-material/Clear"; +import { Box } from "@mui/joy"; +import SvgIcon from "@mui/joy/SvgIcon"; +import { SxProps } from "@mui/joy/styles/types"; + +import { theme } from "@/lib/baseModule/theme/theme"; +import { + Tooth, + isInUpperJaw, + isToothWithDiagnosis, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +import { hasPreviousExaminationResult } from "./GeneralJawForm"; + +const FILL_COLOR = "#555E68"; +const ICON_SIZE: SxProps = { + width: 60, + height: 66, +}; + +type ToothKey = keyof typeof TOOTH_COMPONENTS; + +const TOOTH_COMPONENTS = { + 1: Incisor, + 2: Cuspid, + 3: Cuspid, + 4: Premolar, + 5: Premolar, + 6: Molar, + 7: Molar, + 8: Molar, +} as const; + +interface ToothProps { + tooth: Tooth; +} + +export function ToothIcon({ tooth }: ToothProps) { + const inUpperJaw = isInUpperJaw(tooth); + + if (!isToothWithDiagnosis(tooth)) { + return <NoToothIcon isInUpperJaw={inUpperJaw} />; + } + + const toothKey = getToothKey(tooth); + const variant = inUpperJaw ? "upperJaw" : "lowerJaw"; + const ToothIcon = TOOTH_COMPONENTS[toothKey]; + + return ( + <ToothIcon + variant={variant} + isPrimaryTooth={tooth.toothType === "PRIMARY_TOOTH"} + hasPreviousExaminationResult={hasPreviousExaminationResult(tooth)} + /> + ); +} + +interface ToothIconProps { + hasPreviousExaminationResult?: boolean; + isPrimaryTooth?: boolean; + variant: "upperJaw" | "lowerJaw"; +} + +export function Incisor(props: ToothIconProps) { + return ( + <SvgIcon sx={ICON_SIZE} viewBox="0 0 60 66" fill="none"> + <g transform={props.variant === "upperJaw" ? "" : "rotate(180, 30, 33)"}> + <path + d="M30.8944 12.0249L34.6584 19.5528C34.9908 20.2177 34.5073 21 33.7639 21H26.2361C25.4927 21 25.0092 20.2177 25.3416 19.5528L29.1056 12.0249C29.4741 11.2879 30.5259 11.2879 30.8944 12.0249Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M11 34C11 30.134 14.134 27 18 27H42C45.866 27 49 30.134 49 34V58C49 61.866 45.866 65 42 65H18C14.134 65 11 61.866 11 58V34Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + {props.hasPreviousExaminationResult && ( + <g + transform={ + props.variant === "upperJaw" ? "" : "rotate(180, 30, 46)" + } + > + <path + d="M21 46C21 41.0294 25.0294 37 30 37C34.9706 37 39 41.0294 39 46C39 50.9706 34.9706 55 30 55C25.0294 55 21 50.9706 21 46Z" + fill={theme.palette.danger.solidBg} + /> + <path + d="M30.616 41.66L30.46 47.48H29.32L29.164 41.66H30.616ZM29.932 50.084C29.684 50.084 29.476 50 29.308 49.832C29.14 49.664 29.056 49.456 29.056 49.208C29.056 48.96 29.14 48.752 29.308 48.584C29.476 48.416 29.684 48.332 29.932 48.332C30.172 48.332 30.376 48.416 30.544 48.584C30.712 48.752 30.796 48.96 30.796 49.208C30.796 49.456 30.712 49.664 30.544 49.832C30.376 50 30.172 50.084 29.932 50.084Z" + fill="white" + /> + </g> + )} + </g> + </SvgIcon> + ); +} + +export function Premolar(props: ToothIconProps) { + return ( + <SvgIcon sx={ICON_SIZE} viewBox="0 0 60 66" fill="none"> + <g transform={props.variant === "upperJaw" ? "" : "rotate(180, 30, 33)"}> + <path + d="M22.8944 4.02492L26.6584 11.5528C26.9908 12.2177 26.5073 13 25.7639 13H18.2361C17.4927 13 17.0092 12.2177 17.3416 11.5528L21.1056 4.02492C21.4741 3.28787 22.5259 3.28787 22.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M38.8944 4.02492L42.6584 11.5528C42.9908 12.2177 42.5073 13 41.7639 13H34.2361C33.4927 13 33.0092 12.2177 33.3416 11.5528L37.1056 4.02492C37.4741 3.28787 38.5259 3.28787 38.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M7 26C7 22.134 10.134 19 14 19H46C49.866 19 53 22.134 53 26V58C53 61.866 49.866 65 46 65H14C10.134 65 7 61.866 7 58V26Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + {props.hasPreviousExaminationResult && ( + <g + transform={ + props.variant === "upperJaw" ? "" : "rotate(180, 30, 42)" + } + > + <path + d="M21 42C21 37.0294 25.0294 33 30 33C34.9706 33 39 37.0294 39 42C39 46.9706 34.9706 51 30 51C25.0294 51 21 46.9706 21 42Z" + fill={theme.palette.danger.solidBg} + /> + <path + d="M30.616 37.66L30.46 43.48H29.32L29.164 37.66H30.616ZM29.932 46.084C29.684 46.084 29.476 46 29.308 45.832C29.14 45.664 29.056 45.456 29.056 45.208C29.056 44.96 29.14 44.752 29.308 44.584C29.476 44.416 29.684 44.332 29.932 44.332C30.172 44.332 30.376 44.416 30.544 44.584C30.712 44.752 30.796 44.96 30.796 45.208C30.796 45.456 30.712 45.664 30.544 45.832C30.376 46 30.172 46.084 29.932 46.084Z" + fill="white" + /> + </g> + )} + </g> + </SvgIcon> + ); +} + +export function Cuspid(props: ToothIconProps) { + return ( + <SvgIcon sx={ICON_SIZE} viewBox="0 0 60 66" fill="none"> + <g transform={props.variant === "upperJaw" ? "" : "rotate(180, 30, 33)"}> + <path + d="M30.8944 4.02492L34.6584 11.5528C34.9908 12.2177 34.5073 13 33.7639 13H26.2361C25.4927 13 25.0092 12.2177 25.3416 11.5528L29.1056 4.02492C29.4741 3.28787 30.5259 3.28787 30.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M11 26C11 22.134 14.134 19 18 19H42C45.866 19 49 22.134 49 26V58C49 61.866 45.866 65 42 65H18C14.134 65 11 61.866 11 58V26Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + {props.hasPreviousExaminationResult && ( + <g + transform={ + props.variant === "upperJaw" ? "" : "rotate(180, 30, 42)" + } + > + <path + d="M21 42C21 37.0294 25.0294 33 30 33C34.9706 33 39 37.0294 39 42C39 46.9706 34.9706 51 30 51C25.0294 51 21 46.9706 21 42Z" + fill={theme.palette.danger.solidBg} + /> + <path + d="M30.616 37.66L30.46 43.48H29.32L29.164 37.66H30.616ZM29.932 46.084C29.684 46.084 29.476 46 29.308 45.832C29.14 45.664 29.056 45.456 29.056 45.208C29.056 44.96 29.14 44.752 29.308 44.584C29.476 44.416 29.684 44.332 29.932 44.332C30.172 44.332 30.376 44.416 30.544 44.584C30.712 44.752 30.796 44.96 30.796 45.208C30.796 45.456 30.712 45.664 30.544 45.832C30.376 46 30.172 46.084 29.932 46.084Z" + fill="white" + /> + </g> + )} + </g> + </SvgIcon> + ); +} + +export function Molar(props: ToothIconProps) { + return ( + <SvgIcon sx={ICON_SIZE} viewBox="0 0 60 66" fill="none"> + <g transform={props.variant === "upperJaw" ? "" : "rotate(180, 30, 33)"}> + <path + d="M14.8944 4.02492L18.6584 11.5528C18.9908 12.2177 18.5073 13 17.7639 13H10.2361C9.49269 13 9.00919 12.2177 9.34164 11.5528L13.1056 4.02492C13.4741 3.28787 14.5259 3.28787 14.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M30.8944 4.02492L34.6584 11.5528C34.9908 12.2177 34.5073 13 33.7639 13H26.2361C25.4927 13 25.0092 12.2177 25.3416 11.5528L29.1056 4.02492C29.4741 3.28787 30.5259 3.28787 30.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M46.8944 4.02492L50.6584 11.5528C50.9908 12.2177 50.5073 13 49.7639 13H42.2361C41.4927 13 41.0092 12.2177 41.3416 11.5528L45.1056 4.02492C45.4741 3.28787 46.5259 3.28787 46.8944 4.02492Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + <path + d="M1 26C1 22.134 4.13401 19 8 19H52C55.866 19 59 22.134 59 26V58C59 61.866 55.866 65 52 65H8C4.13401 65 1 61.866 1 58V26Z" + fill={props.isPrimaryTooth ? "white" : FILL_COLOR} + stroke={FILL_COLOR} + stroke-width="2" + /> + {props.hasPreviousExaminationResult && ( + <g + transform={ + props.variant === "upperJaw" ? "" : "rotate(180, 30, 42)" + } + > + <path + d="M21 42C21 37.0294 25.0294 33 30 33C34.9706 33 39 37.0294 39 42C39 46.9706 34.9706 51 30 51C25.0294 51 21 46.9706 21 42Z" + fill={theme.palette.danger.solidBg} + /> + <path + d="M30.616 37.66L30.46 43.48H29.32L29.164 37.66H30.616ZM29.932 46.084C29.684 46.084 29.476 46 29.308 45.832C29.14 45.664 29.056 45.456 29.056 45.208C29.056 44.96 29.14 44.752 29.308 44.584C29.476 44.416 29.684 44.332 29.932 44.332C30.172 44.332 30.376 44.416 30.544 44.584C30.712 44.752 30.796 44.96 30.796 45.208C30.796 45.456 30.712 45.664 30.544 45.832C30.376 46 30.172 46.084 29.932 46.084Z" + fill="white" + /> + </g> + )} + </g> + </SvgIcon> + ); +} + +interface NoToothIconProps { + isInUpperJaw: boolean; +} + +function NoToothIcon(props: NoToothIconProps) { + return ( + <Box + sx={{ + ...ICON_SIZE, + padding: props.isInUpperJaw + ? "32px 18px 10px 18px" + : "10px 18px 32px 18px", + }} + > + <ClearIcon color="neutral" /> + </Box> + ); +} + +function getToothKey(tooth: Tooth): ToothKey { + const toothKey = parseInt(tooth.toothNumber.substring(2, 3)); + if (toothKey < 1 || toothKey > 8) { + throw new Error("Invalid tooth key"); + } + return toothKey as ToothKey; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber.tsx new file mode 100644 index 000000000..16a5fa0ad --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/ToothNumber.tsx @@ -0,0 +1,31 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Typography } from "@mui/joy"; + +import { theme } from "@/lib/baseModule/theme/theme"; +import { Tooth } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types"; + +export function ToothNumber(props: { tooth: Tooth }) { + return ( + <Typography + sx={{ + fontSize: theme.fontSize.md, + borderRadius: theme.radius.sm, + backgroundColor: theme.palette.background.level3, + fontWeight: theme.fontWeight.lg, + width: 36, + height: 24, + textAlign: "center", + }} + > + {getToothNumber(props.tooth)} + </Typography> + ); +} + +export function getToothNumber(tooth: Tooth): number { + return Number.parseInt(tooth.toothNumber.substring(1)); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/UpperJawForm.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/UpperJawForm.tsx new file mode 100644 index 000000000..32238f88c --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/UpperJawForm.tsx @@ -0,0 +1,26 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { GeneralJawForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/GeneralJawForm"; +import { JawWithHeading } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/JawWithHeading"; +import { + QuadrantHeading, + QuadrantHeadingRow, +} from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/QuadrantHeading"; + +export function UpperJawForm() { + return ( + <JawWithHeading + heading={ + <QuadrantHeadingRow marginBottom="24px"> + <QuadrantHeading name="Oberkiefer rechts" index={1} /> + <QuadrantHeading name="Oberkiefer links" index={2} /> + </QuadrantHeadingRow> + } + left={<GeneralJawForm quadrantNumber="Q1" />} + right={<GeneralJawForm quadrantNumber="Q2" />} + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider.tsx new file mode 100644 index 000000000..c837c33cf --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider.tsx @@ -0,0 +1,59 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; +import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { createContext, useContext, useState } from "react"; +import { useStore } from "zustand"; + +import { + DentalExaminationStore, + createDentalExaminationStore, + initDentalExaminationStore, +} from "./dentalExaminationStore"; + +type DentalExaminationStoreApi = ReturnType< + typeof createDentalExaminationStore +>; + +const DentalExaminationStoreContext = + createContext<DentalExaminationStoreApi | null>(null); + +interface DentalExaminationStoreProviderProps extends RequiresChildren { + examinationResult?: ExaminationResult; +} + +export function DentalExaminationStoreProvider({ + examinationResult, + children, +}: DentalExaminationStoreProviderProps) { + const [store] = useState(() => + createDentalExaminationStore(initDentalExaminationStore(examinationResult)), + ); + + // TODO: handle updated examinationResult + + return ( + <DentalExaminationStoreContext.Provider value={store}> + {children} + </DentalExaminationStoreContext.Provider> + ); +} + +export function useDentalExaminationStore<T>( + selector: (store: DentalExaminationStore) => T, +): T { + const dentalExaminationStoreContext = useContext( + DentalExaminationStoreContext, + ); + + if (dentalExaminationStoreContext === null) { + throw new Error("Missing DentalExaminationStoreProvider"); + } + + return useStore(dentalExaminationStoreContext, selector); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/actions.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/actions.ts new file mode 100644 index 000000000..574503a4a --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/actions.ts @@ -0,0 +1,243 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiMainResult, ApiSecondaryResult } from "@eshg/dental-api"; +import { ToothDiagnoses } from "@eshg/dental/api/models/ExaminationResult"; +import { isEmptyString } from "@eshg/lib-portal/helpers/guards"; + +import { createToothResult, createToothWithDiagnosis } from "./factories"; +import { + DentalExaminationView, + Dentition, + Focus, + ToothContext, + ToothResult, + ToothWithDiagnosis, + isAddableTooth, +} from "./types"; + +export function setMainResult( + toothContext: ToothContext, + newValue: string, + dentition: Dentition, +) { + const tooth = getToothFromToothContext(dentition, toothContext); + + const isInvalid = isEmptyString(newValue) + ? !isEmptyString(tooth.secondaryResult1.value) || + !isEmptyString(tooth.secondaryResult2.value) + : !isValidMainResult(newValue); + + return updateToothWithDiagnosis(toothContext, dentition, { + mainResult: createToothResult(newValue, isInvalid), + }); +} + +export function setSecondaryResult1( + toothContext: ToothContext, + newValue: string, + dentition: Dentition, +) { + const tooth = getToothFromToothContext(dentition, toothContext); + + const isInvalid = + !isEmptyString(newValue) && !isValidSecondaryResult(newValue); + + const mainResult = setMainResultInvalidIfEmpty( + tooth.mainResult, + tooth.secondaryResult2, + newValue, + ); + + return updateToothWithDiagnosis(toothContext, dentition, { + mainResult, + secondaryResult1: createToothResult(newValue, isInvalid), + }); +} + +export function setSecondaryResult2( + toothContext: ToothContext, + newValue: string, + dentition: Dentition, +) { + const tooth = getToothFromToothContext(dentition, toothContext); + + const isInvalid = + !isEmptyString(newValue) && !isValidSecondaryResult(newValue); + + const mainResult = setMainResultInvalidIfEmpty( + tooth.mainResult, + tooth.secondaryResult1, + newValue, + ); + + return updateToothWithDiagnosis(toothContext, dentition, { + mainResult, + secondaryResult2: createToothResult(newValue, isInvalid), + }); +} + +function setMainResultInvalidIfEmpty( + mainResult: ToothResult, + secondaryResult: ToothResult, + newValue: string, +) { + if (isEmptyToothResult(mainResult)) { + if (isEmptyString(newValue) && isEmptyToothResult(secondaryResult)) { + return createToothResult(mainResult.value, false); + } else { + return createToothResult("", true); + } + } + return mainResult; +} + +function getToothFromToothContext( + dentition: Dentition, + toothContext: ToothContext, +) { + return dentition[toothContext.quadrantNumber].teeth[ + toothContext.toothIndex + ] as ToothWithDiagnosis; +} + +function isValidSecondaryResult( + newValue: string, +): newValue is ApiSecondaryResult { + return Object.values(ApiSecondaryResult).includes( + newValue as ApiSecondaryResult, + ); +} + +function isValidMainResult(newValue: string): newValue is ApiMainResult { + return Object.values(ApiMainResult).includes(newValue as ApiMainResult); +} + +export function addTooth( + toothContext: ToothContext, + dentition: Dentition, +): Dentition { + const { quadrantNumber, toothIndex } = toothContext; + const targetQuadrant = dentition[quadrantNumber]; + const tooth = targetQuadrant.teeth[toothIndex]; + + if (tooth === undefined) { + throw new Error( + `Tooth with index ${toothIndex} does not exist in quadrant ${quadrantNumber}`, + ); + } + + if (!isAddableTooth(tooth)) { + throw new Error("Tooth must be of type AddableTooth"); + } + + const newTooth = createToothWithDiagnosis(tooth.toothNumber); + + return { + ...dentition, + [quadrantNumber]: { + ...targetQuadrant, + teeth: targetQuadrant.teeth.with(toothContext.toothIndex, newTooth), + }, + }; +} + +function updateToothWithDiagnosis( + toothContext: ToothContext, + dentition: Dentition, + newTooth: Partial<ToothWithDiagnosis>, +): Dentition { + const { quadrantNumber, toothIndex } = toothContext; + const targetQuadrant = dentition[quadrantNumber]; + const tooth = targetQuadrant.teeth[toothIndex]; + + if (tooth === undefined) { + throw new Error( + `Tooth with index ${toothIndex} does not exist in quadrant ${quadrantNumber}`, + ); + } + + if (tooth.type !== "ToothWithDiagnosis") { + throw new Error("Tooth must be of type ToothWithDiagnosis"); + } + + return { + ...dentition, + [quadrantNumber]: { + ...targetQuadrant, + teeth: targetQuadrant.teeth.with(toothIndex, { ...tooth, ...newTooth }), + }, + }; +} + +export function getToothDiagnoses(dentition: Dentition): ToothDiagnoses { + const toothDiagnoses: ToothDiagnoses = {}; + + Object.values(dentition) + .flatMap((quadrant) => quadrant.teeth) + .forEach((tooth) => { + if (tooth.type !== "ToothWithDiagnosis") { + return; + } + + const { toothNumber, mainResult, secondaryResult1, secondaryResult2 } = + tooth; + + assertIsValid(mainResult); + + if ( + isEmptyToothResult(mainResult) || + !isValidMainResult(mainResult.value) + ) { + return; + } + + toothDiagnoses[toothNumber] = { + tooth: toothNumber, + mainResult: mainResult.value, + secondaryResult1: resolveSecondaryResult(secondaryResult1), + secondaryResult2: resolveSecondaryResult(secondaryResult2), + }; + }); + + return toothDiagnoses; +} + +function resolveSecondaryResult( + toothResult: ToothResult, +): ApiSecondaryResult | undefined { + assertIsValid(toothResult); + + if (!isValidSecondaryResult(toothResult.value)) { + return undefined; + } + + return toothResult.value; +} + +function assertIsValid(toothResult: ToothResult): void { + if (toothResult.isInvalid) { + throw new Error("Invalid tooth result"); + } +} + +function isEmptyToothResult(toothResult: ToothResult): boolean { + return toothResult.value === ""; +} + +export function setFocus(focus: Focus): { + focus: Focus; + currentView: DentalExaminationView; +} { + const quadrantNumber = focus.toothContext.quadrantNumber; + const nextView = + quadrantNumber === "Q1" || quadrantNumber === "Q2" + ? "UPPER_JAW" + : "LOWER_JAW"; + return { + focus, + currentView: nextView, + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/constants.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/constants.ts new file mode 100644 index 000000000..a4f548ed9 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/constants.ts @@ -0,0 +1,141 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiTooth } from "@eshg/dental-api"; + +import { ToothType } from "./types"; + +/** + * Defines a mapping from milk teeth to permanent teeth and vice versa + */ +export const RELATED_TEETH: Partial<Record<ApiTooth, ApiTooth>> = { + T11: "T51", + T12: "T52", + T13: "T53", + T14: "T54", + T15: "T55", + + T21: "T61", + T22: "T62", + T23: "T63", + T24: "T64", + T25: "T65", + + T31: "T71", + T32: "T72", + T33: "T73", + T34: "T74", + T35: "T75", + + T41: "T81", + T42: "T82", + T43: "T83", + T44: "T84", + T45: "T85", + + T51: "T11", + T52: "T12", + T53: "T13", + T54: "T14", + T55: "T15", + + T61: "T21", + T62: "T22", + T63: "T23", + T64: "T24", + T65: "T25", + + T71: "T31", + T72: "T32", + T73: "T33", + T74: "T34", + T75: "T35", + + T81: "T41", + T82: "T42", + T83: "T43", + T84: "T44", + T85: "T45", +}; + +export const TOOTH_TYPES: Record<ApiTooth, ToothType> = { + T11: "SECONDARY_TOOTH", + T12: "SECONDARY_TOOTH", + T13: "SECONDARY_TOOTH", + T14: "SECONDARY_TOOTH", + T15: "SECONDARY_TOOTH", + T16: "SECONDARY_TOOTH", + T17: "SECONDARY_TOOTH", + T18: "SECONDARY_TOOTH", + + T21: "SECONDARY_TOOTH", + T22: "SECONDARY_TOOTH", + T23: "SECONDARY_TOOTH", + T24: "SECONDARY_TOOTH", + T25: "SECONDARY_TOOTH", + T26: "SECONDARY_TOOTH", + T27: "SECONDARY_TOOTH", + T28: "SECONDARY_TOOTH", + + T31: "SECONDARY_TOOTH", + T32: "SECONDARY_TOOTH", + T33: "SECONDARY_TOOTH", + T34: "SECONDARY_TOOTH", + T35: "SECONDARY_TOOTH", + T36: "SECONDARY_TOOTH", + T37: "SECONDARY_TOOTH", + + T38: "SECONDARY_TOOTH", + T41: "SECONDARY_TOOTH", + T42: "SECONDARY_TOOTH", + T43: "SECONDARY_TOOTH", + T44: "SECONDARY_TOOTH", + T45: "SECONDARY_TOOTH", + T46: "SECONDARY_TOOTH", + T47: "SECONDARY_TOOTH", + T48: "SECONDARY_TOOTH", + + T51: "PRIMARY_TOOTH", + T52: "PRIMARY_TOOTH", + T53: "PRIMARY_TOOTH", + T54: "PRIMARY_TOOTH", + T55: "PRIMARY_TOOTH", + + T61: "PRIMARY_TOOTH", + T62: "PRIMARY_TOOTH", + T63: "PRIMARY_TOOTH", + T64: "PRIMARY_TOOTH", + T65: "PRIMARY_TOOTH", + + T71: "PRIMARY_TOOTH", + T72: "PRIMARY_TOOTH", + T73: "PRIMARY_TOOTH", + T74: "PRIMARY_TOOTH", + T75: "PRIMARY_TOOTH", + + T81: "PRIMARY_TOOTH", + T82: "PRIMARY_TOOTH", + T83: "PRIMARY_TOOTH", + T84: "PRIMARY_TOOTH", + T85: "PRIMARY_TOOTH", +}; + +/** + * Defines teeth which can be added and removed + */ +export const OPTIONAL_TEETH = new Set<ApiTooth>([ + "T16", + "T17", + "T18", + "T26", + "T27", + "T28", + "T36", + "T37", + "T38", + "T46", + "T47", + "T48", +]); diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/dentalExaminationStore.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/dentalExaminationStore.ts new file mode 100644 index 000000000..08cd0e677 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/dentalExaminationStore.ts @@ -0,0 +1,106 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/* eslint-disable unused-imports/no-unused-vars */ +import { + ExaminationResult, + ToothDiagnoses, +} from "@eshg/dental/api/models/ExaminationResult"; +import { createStore } from "zustand"; + +import { + addTooth, + getToothDiagnoses, + setFocus, + setMainResult, + setSecondaryResult1, + setSecondaryResult2, +} from "./actions"; +import { createSecondaryDentition } from "./factories"; +import { DentalExaminationView, Dentition, Focus, ToothContext } from "./types"; + +export interface DentalExaminationState { + currentView: DentalExaminationView; + dentition: Dentition; + focus: Focus; +} + +export interface DentalExaminationActions { + setView: (newView: DentalExaminationView) => void; + + addTooth: ToothAction; + removeTooth: ToothAction; + toggleToothType: ToothAction; + setFocus: (focus: Focus) => void; + + setMainResult: SetToothResultAction; + setSecondaryResult1: SetToothResultAction; + setSecondaryResult2: SetToothResultAction; + getToothDiagnoses: () => ToothDiagnoses; +} + +export type ToothAction = (toothContext: ToothContext) => void; +export type SetToothResultAction = ( + toothContext: ToothContext, + newValue: string, +) => void; + +export type DentalExaminationStore = DentalExaminationState & + DentalExaminationActions; + +export function initDentalExaminationStore( + examinationResult: ExaminationResult | undefined, +): DentalExaminationState { + const toothDiagnoses = + examinationResult?.type === "screening" + ? examinationResult.toothDiagnoses + : {}; + + return { + currentView: "UPPER_JAW", + // TODO ISSUE-6584: distinguish between type of dentition + dentition: createSecondaryDentition(toothDiagnoses), + focus: { + toothContext: { quadrantNumber: "Q1", toothIndex: 0 }, + field: "main", + }, + }; +} + +export function createDentalExaminationStore( + initialState: DentalExaminationState, +) { + return createStore<DentalExaminationStore>()((set, get) => ({ + ...initialState, + setView: (newView: DentalExaminationView) => set({ currentView: newView }), + addTooth: (toothContext: ToothContext) => { + set((state) => ({ + dentition: addTooth(toothContext, state.dentition), + })); + }, + removeTooth: (toothContext: ToothContext) => { + throw new Error("Not yet implemented"); + }, + toggleToothType: (toothContext: ToothContext) => { + throw new Error("Not yet implemented"); + }, + setFocus: (focus: Focus) => { + set(setFocus(focus)); + }, + setMainResult: (toothContext: ToothContext, newValue: string) => + set((state) => ({ + dentition: setMainResult(toothContext, newValue, state.dentition), + })), + setSecondaryResult1: (toothContext: ToothContext, newValue: string) => + set((state) => ({ + dentition: setSecondaryResult1(toothContext, newValue, state.dentition), + })), + setSecondaryResult2: (toothContext: ToothContext, newValue: string) => + set((state) => ({ + dentition: setSecondaryResult2(toothContext, newValue, state.dentition), + })), + getToothDiagnoses: () => getToothDiagnoses(get().dentition), + })); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/factories.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/factories.ts new file mode 100644 index 000000000..e9c7a3cf2 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/factories.ts @@ -0,0 +1,135 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiTooth } from "@eshg/dental-api"; +import { ToothDiagnoses } from "@eshg/dental/api/models/ExaminationResult"; +import { ToothDiagnosis } from "@eshg/dental/api/models/ToothDiagnosis"; +import { isDefined } from "remeda"; + +import { OPTIONAL_TEETH, RELATED_TEETH, TOOTH_TYPES } from "./constants"; +import { + AddableTooth, + Dentition, + Quadrant, + QuadrantNumber, + Tooth, + ToothResult, + ToothWithDiagnosis, +} from "./types"; + +export function createPrimaryDentition( + toothDiagnoses: ToothDiagnoses = {}, +): Dentition { + return createDentition( + ["T18", "T17", "T16", "T55", "T54", "T53", "T52", "T51"], + ["T61", "T62", "T63", "T64", "T65", "T26", "T27", "T28"], + ["T71", "T72", "T73", "T74", "T75", "T36", "T37", "T38"], + ["T48", "T47", "T46", "T85", "T84", "T83", "T82", "T81"], + toothDiagnoses, + true, + ); +} + +export function createSecondaryDentition( + toothDiagnoses: ToothDiagnoses = {}, +): Dentition { + return createDentition( + ["T18", "T17", "T16", "T15", "T14", "T13", "T12", "T11"], + ["T21", "T22", "T23", "T24", "T25", "T26", "T27", "T28"], + ["T31", "T32", "T33", "T34", "T35", "T36", "T37", "T38"], + ["T48", "T47", "T46", "T45", "T44", "T43", "T42", "T41"], + toothDiagnoses, + false, + ); +} + +function createDentition( + teethQuadrant1: ApiTooth[], + teethQuadrant2: ApiTooth[], + teethQuadrant3: ApiTooth[], + teethQuadrant4: ApiTooth[], + toothDiagnoses: ToothDiagnoses = {}, + isPrimaryDentition: boolean, +): Dentition { + function createToothWithType( + tooth: ApiTooth, + ): ToothWithDiagnosis | AddableTooth { + return OPTIONAL_TEETH.has(tooth) && + resolveToothDiagnosis(tooth, toothDiagnoses) === undefined && + isPrimaryDentition + ? createAddableTooth(tooth) + : createToothWithDiagnosis(tooth, toothDiagnoses); + } + + function processQuadrant(teeth: ApiTooth[]) { + return teeth.map(createToothWithType); + } + + return { + Q1: createQuadrant("Q1", processQuadrant(teethQuadrant1)), + Q2: createQuadrant("Q2", processQuadrant(teethQuadrant2)), + Q3: createQuadrant("Q3", processQuadrant(teethQuadrant3)), + Q4: createQuadrant("Q4", processQuadrant(teethQuadrant4)), + }; +} + +function createQuadrant( + quadrantNumber: QuadrantNumber, + teeth: Tooth[], +): Quadrant { + return { + quadrantNumber, + teeth, + }; +} + +export function createToothWithDiagnosis( + tooth: ApiTooth, + toothDiagnoses: ToothDiagnoses = {}, +): ToothWithDiagnosis { + const diagnosis = resolveToothDiagnosis(tooth, toothDiagnoses); + const toothNumber = diagnosis?.tooth ?? tooth; + + return { + type: "ToothWithDiagnosis", + toothNumber, + toothType: TOOTH_TYPES[toothNumber], + isRemovable: OPTIONAL_TEETH.has(toothNumber), + mainResult: createToothResult(diagnosis?.mainResult), + secondaryResult1: createToothResult(diagnosis?.secondaryResult1), + secondaryResult2: createToothResult(diagnosis?.secondaryResult2), + previousResults: [], + }; +} + +function resolveToothDiagnosis( + toothNumber: ApiTooth, + toothDiagnoses: ToothDiagnoses, +): ToothDiagnosis | undefined { + if (isDefined(toothDiagnoses[toothNumber])) { + return toothDiagnoses[toothNumber]; + } + + const relatedTooth = RELATED_TEETH[toothNumber]; + if (relatedTooth === undefined) { + return undefined; + } + + return toothDiagnoses[relatedTooth]; +} + +export function createToothResult(value = "", isInvalid = false): ToothResult { + return { + value, + isInvalid, + }; +} + +export function createAddableTooth(tooth: ApiTooth): AddableTooth { + return { + type: "AddableTooth", + toothNumber: tooth, + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types.ts new file mode 100644 index 000000000..3e73437e5 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/types.ts @@ -0,0 +1,79 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiMainResult, ApiSecondaryResult, ApiTooth } from "@eshg/dental-api"; + +export type DentalExaminationView = + | "UPPER_JAW" + | "LOWER_JAW" + | "FULL_DENTITION"; + +export type Dentition = Record<QuadrantNumber, Quadrant>; + +export interface Quadrant { + quadrantNumber: QuadrantNumber; + teeth: Tooth[]; +} + +export type QuadrantNumber = "Q1" | "Q2" | "Q3" | "Q4"; + +export type Tooth = ToothWithDiagnosis | AddableTooth; + +export interface ToothWithDiagnosis { + type: "ToothWithDiagnosis"; + toothNumber: ApiTooth; + toothType: ToothType; + isRemovable: boolean; + mainResult: ToothResult; + secondaryResult1: ToothResult; + secondaryResult2: ToothResult; + previousResults: ToothDiagnosisResult[]; +} + +export interface AddableTooth { + type: "AddableTooth"; + toothNumber: ApiTooth; +} + +export function isToothWithDiagnosis( + tooth: Tooth, +): tooth is ToothWithDiagnosis { + return tooth.type === "ToothWithDiagnosis"; +} + +export function isAddableTooth(tooth: Tooth): tooth is AddableTooth { + return tooth.type === "AddableTooth"; +} + +export function isInUpperJaw(tooth: Tooth) { + const quadrantIdentifier = parseInt(tooth.toothNumber.substring(1, 2)); + return ( + quadrantIdentifier === 1 || + quadrantIdentifier === 2 || + quadrantIdentifier === 5 || + quadrantIdentifier === 6 + ); +} + +export type ToothType = "PRIMARY_TOOTH" | "SECONDARY_TOOTH"; + +type ToothDiagnosisResult = ApiMainResult | ApiSecondaryResult; + +export interface ToothResult { + value: string; + isInvalid: boolean; +} + +export interface ToothContext { + quadrantNumber: QuadrantNumber; + toothIndex: number; +} + +export type FieldVariant = "main" | "secondary1" | "secondary2"; + +export interface Focus { + toothContext: ToothContext; + field: FieldVariant; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/formComponents.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/formComponents.tsx deleted file mode 100644 index 509149c67..000000000 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/formComponents.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { - SoftRequiredBooleanSelectField, - SoftRequiredSelectField, -} from "@eshg/lib-portal/components/form/fieldVariants"; -import { SelectFieldProps } from "@eshg/lib-portal/components/formFields/SelectField"; -import { ReactNode } from "react"; -import { isDefined } from "remeda"; - -import { AdditionalInformationFormComponents } from "@/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection"; - -export const ADDITIONAL_INFO_FORM_COMPONENTS: AdditionalInformationFormComponents = - { - SelectField: < - TMultiple extends boolean, - TOptionLabel extends string | ReactNode = string, - >( - props: SelectFieldProps<TMultiple, TOptionLabel>, - ) => ( - <SoftRequiredSelectField<TMultiple, TOptionLabel> - {...toSoftRequiredProps(props)} - /> - ), - BooleanSelectField: (props) => ( - <SoftRequiredBooleanSelectField {...toSoftRequiredProps(props)} /> - ), - }; - -function toSoftRequiredProps<TProps extends { required?: string }>({ - required, - ...props -}: TProps) { - return { - ...props, - softRequired: isDefined(required), - orientation: "vertical", - } as const; -} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationForm.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationForm.ts deleted file mode 100644 index 0992303a2..000000000 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationForm.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; -import { - mapOptionalValue, - mapRequiredValue, -} from "@eshg/lib-portal/helpers/form"; -import { useFormik } from "formik"; - -import { - ExaminationFormValues, - mapToExaminationFormValues, -} from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; -import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; - -interface UseProphylaxisSessionExaminationFormParams { - examinationResult: ExaminationResult | undefined; - note: string | undefined; - onSubmit: (examinationResult: ExaminationResult) => void; -} - -export function useProphylaxisSessionExaminationForm( - params: UseProphylaxisSessionExaminationFormParams, -) { - const { examinationResult, note, onSubmit } = params; - - const isScreening = useProphylaxisSessionStore((state) => state.isScreening); - - const examinationForm = useFormik({ - initialValues: mapToExaminationFormValues( - examinationResult, - note, // TODO pass actual note - ), - onSubmit: (formValues: ExaminationFormValues) => { - onSubmit(mapToExaminationResult(isScreening, formValues)); - }, - enableReinitialize: true, - }); - - return examinationForm; -} - -function mapToExaminationResult( - screening: boolean, - formValues: ExaminationFormValues, -): ExaminationResult { - if (screening) { - return { - type: "screening", - oralHygieneStatus: mapOptionalValue(formValues.oralHygieneStatus), - fluorideVarnishApplied: - mapOptionalValue(formValues.fluorideVarnishApplied) ?? false, - }; - } - - // TODO: Remove when fluoridation only examination is handled without form - return { - type: "fluoridation", - fluorideVarnishApplied: mapRequiredValue(formValues.fluorideVarnishApplied), - }; -} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationBottomBar.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationBottomBar.tsx similarity index 90% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationBottomBar.tsx rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationBottomBar.tsx index 08a2d0959..80d00a63c 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationBottomBar.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationBottomBar.tsx @@ -12,14 +12,14 @@ import { isDefined } from "remeda"; import { StickyBottomButtonBar } from "@/lib/shared/components/buttons/StickyBottomButtonBar"; -interface ProphylaxisSessionExaminationBottomBarProps { +interface ParticipantExaminationBottomBarProps { onPreviousParticipantClicked?: () => void; onNextParticipantClicked?: () => void; onOverviewClicked: () => void; } -export function ProphylaxisSessionExaminationBottomBar( - props: ProphylaxisSessionExaminationBottomBarProps, +export function ParticipantExaminationBottomBar( + props: ParticipantExaminationBottomBarProps, ) { const { onPreviousParticipantClicked, diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationForm.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationForm.tsx similarity index 84% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationForm.tsx rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationForm.tsx index d4e74e6b0..436a2659e 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationForm.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationForm.tsx @@ -20,14 +20,13 @@ const FullHeightFormPlus = styled(FormPlus)({ height: "100%", }); -export interface ProphylaxisSessionExaminationFormProps - extends RequiresChildren { +export interface ParticipantExaminationFormProps extends RequiresChildren { form: FormikProps<ExaminationFormValues>; bottomBar: ReactNode; } -export function ProphylaxisSessionExaminationForm( - props: ProphylaxisSessionExaminationFormProps, +export function ParticipantExaminationForm( + props: ParticipantExaminationFormProps, ) { return ( <FormikProvider value={props.form}> diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationPage.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationPage.tsx new file mode 100644 index 000000000..99ca07496 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationPage.tsx @@ -0,0 +1,118 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ChildExamination } from "@eshg/dental/api/models/ChildExamination"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { isDefined } from "remeda"; + +import { AdditionalInformationFormSection } from "@/lib/businessModules/dental/features/examinations/AdditionalInformationFormSection"; +import { ExaminationFormLayout } from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; +import { NoteFormSection } from "@/lib/businessModules/dental/features/examinations/NoteFormSection"; +import { DentalExaminationFormSection } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExamination/DentalExaminationFormSection"; +import { useDentalExaminationStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/dentalExaminationStore/DentalExaminationStoreProvider"; +import { ParticipantExaminationBottomBar } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationBottomBar"; +import { ParticipantExaminationForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationForm"; +import { ParticipantExaminationToolbar } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationToolbar"; +import { useParticipantExaminationForm } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantExaminationForm"; +import { useParticipantNavigation } from "@/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantNavigation"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; +import { StickyToolbarLayout } from "@/lib/shared/components/layout/StickyToolbarLayout"; + +interface ParticipantExaminationPageProps { + participant: ChildExamination; + participantIndex: number; + participantsLength: number; +} + +export function ParticipantExaminationPage( + props: ParticipantExaminationPageProps, +) { + const { participant, participantIndex, participantsLength } = props; + const router = useRouter(); + const prophylaxisSessionId = useProphylaxisSessionStore((state) => state.id); + const isScreening = useProphylaxisSessionStore((state) => state.isScreening); + const fluoridationVarnish = useProphylaxisSessionStore( + (state) => state.fluoridationVarnish, + ); + const setExamination = useProphylaxisSessionStore( + (state) => state.setExamination, + ); + const getToothDiagnoses = useDentalExaminationStore( + (state) => state.getToothDiagnoses, + ); + + const [nextRoute, setNextRoute] = useState<string>(); + const examinationForm = useParticipantExaminationForm({ + initialValues: participant, + onSubmit: (values) => { + try { + const toothDiagnoses = getToothDiagnoses(); + const result = + values.result?.type === "screening" + ? { ...values.result, toothDiagnoses } + : values.result; + + setExamination(participant.examinationId, result, values.note); + if (isDefined(nextRoute)) { + router.push(nextRoute); + } + } catch { + // TODO handle invalid tooth diagnoses + } finally { + setNextRoute(undefined); + } + }, + }); + const examinationNavigation = useParticipantNavigation({ + participantIndex, + participantsLength, + onNavigate: (nextRoute) => { + setNextRoute(nextRoute); + void examinationForm.submitForm(); + }, + }); + + return ( + <StickyToolbarLayout + toolbar={ + <ParticipantExaminationToolbar + prophylaxisSessionId={prophylaxisSessionId} + participant={participant} + participantIndex={participantIndex} + onBackClicked={examinationNavigation.gotoOverview} + /> + } + > + <ParticipantExaminationForm + form={examinationForm} + bottomBar={ + <ParticipantExaminationBottomBar + onPreviousParticipantClicked={ + examinationNavigation.gotoPreviousParticipant + } + onNextParticipantClicked={examinationNavigation.gotoNextParticipant} + onOverviewClicked={examinationNavigation.gotoOverview} + /> + } + > + <ExaminationFormLayout + additionalInformation={ + <AdditionalInformationFormSection + screening={isScreening} + fluoridation={isDefined(fluoridationVarnish)} + fluoridationConsentGiven={participant.fluoridationConsentGiven} + status={participant.status} + /> + } + dentalExamination={ + isScreening ? <DentalExaminationFormSection /> : undefined + } + note={<NoteFormSection />} + /> + </ParticipantExaminationForm> + </StickyToolbarLayout> + ); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationToolbar.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationToolbar.tsx similarity index 87% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationToolbar.tsx rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationToolbar.tsx index 3e3b3cf98..c82bd24e6 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/ProphylaxisSessionExaminationToolbar.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/ParticipantExaminationToolbar.tsx @@ -15,15 +15,15 @@ import { TabNavigationToolbar, } from "@/lib/shared/components/tabNavigationToolbar/TabNavigationToolbar"; -interface ProphylaxisSessionExaminationToolbarProps { +interface ParticipantExaminationToolbarProps { prophylaxisSessionId: string; participant: ChildExamination; participantIndex: number; onBackClicked: () => void; } -export function ProphylaxisSessionExaminationToolbar( - props: ProphylaxisSessionExaminationToolbarProps, +export function ParticipantExaminationToolbar( + props: ParticipantExaminationToolbarProps, ) { return ( <TabNavigationToolbar diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantExaminationForm.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantExaminationForm.ts new file mode 100644 index 000000000..b6df30289 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantExaminationForm.ts @@ -0,0 +1,93 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ExaminationResult, + FluoridationExaminationResult, + ScreeningExaminationResult, + isEmptyExaminationResult, +} from "@eshg/dental/api/models/ExaminationResult"; +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +import { useFormik } from "formik"; + +import { + ExaminationFormValues, + mapToExaminationFormValues, +} from "@/lib/businessModules/dental/features/examinations/ExaminationFormLayout"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; + +interface ExaminationInputValues { + result?: ExaminationResult; + note?: string; +} + +interface ExaminationOutputValues { + result?: ExaminationResult; + note?: string; +} + +interface UseParticipantExaminationFormParams { + initialValues: ExaminationInputValues; + onSubmit: (values: ExaminationOutputValues) => void; +} + +export function useParticipantExaminationForm( + params: UseParticipantExaminationFormParams, +) { + const { initialValues, onSubmit } = params; + + const isScreening = useProphylaxisSessionStore((state) => state.isScreening); + + return useFormik({ + initialValues: mapToExaminationFormValues( + initialValues.result, + initialValues.note, + ), + onSubmit: (formValues: ExaminationFormValues) => { + onSubmit(mapToExaminationValues(isScreening, formValues)); + }, + enableReinitialize: true, + }); +} + +function mapToExaminationValues( + screening: boolean, + formValues: ExaminationFormValues, +): ExaminationOutputValues { + return { + result: mapToExaminationResult(screening, formValues), + note: mapOptionalValue(formValues.note), + }; +} + +function mapToExaminationResult( + screening: boolean, + formValues: ExaminationFormValues, +): ExaminationResult | undefined { + let result: FluoridationExaminationResult | ScreeningExaminationResult; + if (screening) { + result = { + type: "screening", + oralHygieneStatus: mapOptionalValue(formValues.oralHygieneStatus), + fluorideVarnishApplied: mapOptionalValue( + formValues.fluorideVarnishApplied, + ), + toothDiagnoses: {}, + }; + } else { + // TODO: Remove when fluoridation only examination is handled without form + result = { + type: "fluoridation", + fluorideVarnishApplied: mapOptionalValue( + formValues.fluorideVarnishApplied, + ), + }; + } + + if (isEmptyExaminationResult(result)) { + return undefined; + } + return result; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationNavigation.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantNavigation.ts similarity index 91% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationNavigation.ts rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantNavigation.ts index b73b28500..f4b36528a 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/examination/useProphylaxisSessionExaminationNavigation.ts +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/participantExamination/useParticipantNavigation.ts @@ -5,7 +5,7 @@ import { routes } from "@eshg/dental/shared/routes"; -import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider"; +import { useProphylaxisSessionStore } from "@/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider"; interface UseParticipantNavigationResult { gotoPreviousParticipant?: () => void; @@ -19,7 +19,7 @@ interface UseParticipantNavigationParams { onNavigate: (route: string) => void; } -export function useProphylaxisSessionExaminationNavigation( +export function useParticipantNavigation( params: UseParticipantNavigationParams, ): UseParticipantNavigationResult { const { participantIndex, participantsLength, onNavigate } = params; diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider.tsx b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider.tsx similarity index 89% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider.tsx rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider.tsx index d616915a8..bf135bed9 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/ProphylaxisSessionStoreProvider.tsx +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/ProphylaxisSessionStoreProvider.tsx @@ -8,7 +8,7 @@ import { ChildExamination } from "@eshg/dental/api/models/ChildExamination"; import { ProphylaxisSessionDetails } from "@eshg/dental/api/models/ProphylaxisSessionDetails"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; -import { createContext, useContext, useEffect, useState } from "react"; +import { createContext, useContext, useState } from "react"; import { useStore } from "zustand"; import { useShallow } from "zustand/react/shallow"; @@ -19,6 +19,7 @@ import { createProphylaxisSessionStore, initProphylaxisSessionStore, } from "./prophylaxisSessionStore"; +import { useSyncIncomingProphylaxisSessionChanges } from "./useSyncIncomingProphylaxisSessionChanges"; type ProphylaxisSessionStoreApi = ReturnType< typeof createProphylaxisSessionStore @@ -41,10 +42,7 @@ export function ProphylaxisSessionStoreProvider({ ), ); - const { setState } = store; - useEffect(() => { - setState(prophylaxisSession); - }, [prophylaxisSession, setState]); + useSyncIncomingProphylaxisSessionChanges(store, prophylaxisSession); return ( <ProphylaxisSessionStoreContext.Provider value={store}> diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/actions.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/actions.ts new file mode 100644 index 000000000..6e7baef74 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/actions.ts @@ -0,0 +1,57 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; + +import { ParticipantFilters } from "./participantFilters"; +import { ProphylaxisSessionState } from "./prophylaxisSessionStore"; + +type SetParticipantFiltersState = Pick< + ProphylaxisSessionState, + "participantFilters" +>; + +export function setParticipantFilters( + filters: Partial<ParticipantFilters>, + state: SetParticipantFiltersState, +): SetParticipantFiltersState { + return { + participantFilters: { + ...state.participantFilters, + ...filters, + }, + }; +} + +type SetExaminationState = Pick< + ProphylaxisSessionState, + "participants" | "changedExaminationsById" +>; + +export function setExamination( + examinationId: string, + result: ExaminationResult | undefined, + note: string | undefined, + state: SetExaminationState, +): SetExaminationState { + const updatedParticipants = state.participants.map((participant) => { + if (participant.examinationId !== examinationId) { + return participant; + } + + return { + ...participant, + result, + note, + }; + }); + + return { + participants: updatedParticipants, + changedExaminationsById: new Set(state.changedExaminationsById).add( + examinationId, + ), + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/participantFilters.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantFilters.ts similarity index 100% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/participantFilters.ts rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantFilters.ts diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/participantSorting.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantSorting.ts similarity index 97% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/participantSorting.ts rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantSorting.ts index ab190f2bd..0e8ad4508 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/participantSorting.ts +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/participantSorting.ts @@ -17,7 +17,7 @@ export interface ParticipantSorting { export type ParticipantSortKey = keyof Omit< ChildExamination, - "childId" | "result" | "note" + "childId" | "result" | "note" | "examinationId" | "examinationVersion" >; export type ParticipantSortDirection = "asc" | "desc"; diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/prophylaxisSessionStore.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/prophylaxisSessionStore.ts similarity index 63% rename from employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/prophylaxisSessionStore.ts rename to employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/prophylaxisSessionStore.ts index 907ad84fb..d68f9ef0c 100644 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/prophylaxisSessionStore.ts +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/prophylaxisSessionStore.ts @@ -7,22 +7,30 @@ import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; import { ProphylaxisSessionDetails } from "@eshg/dental/api/models/ProphylaxisSessionDetails"; import { createStore } from "zustand"; +import { setExamination, setParticipantFilters } from "./actions"; import { ParticipantFilters } from "./participantFilters"; import { ParticipantSorting } from "./participantSorting"; -import { replaceExaminationResult } from "./replaceExaminationResult"; export interface ProphylaxisSessionState extends ProphylaxisSessionDetails { participantFilters: ParticipantFilters; participantSorting: ParticipantSorting; + + changedExaminationsById: Set<string>; } export interface ProphylaxisSessionActions { setParticipantFilters: (filtersChange: Partial<ParticipantFilters>) => void; setParticipantSorting: (sorting: ParticipantSorting) => void; - setExaminationResult: ( - participantId: string, - examinationResult: ExaminationResult, + setProphylaxisSession: ( + prophylaxisSession: ProphylaxisSessionDetails, + ) => void; + setExamination: ( + examinationId: string, + result: ExaminationResult | undefined, + note: string | undefined, ) => void; + + markAsSynchronized: () => void; } export type ProphylaxisSessionStore = ProphylaxisSessionState & @@ -45,6 +53,8 @@ export function initProphylaxisSessionStore( ...prophylaxisSession, participantFilters: initialFilters, participantSorting: initialSorting, + + changedExaminationsById: new Set(), }; } @@ -54,27 +64,16 @@ export function createProphylaxisSessionStore( return createStore<ProphylaxisSessionStore>()((set) => ({ ...initialState, setParticipantFilters: (filters) => { - set((state) => ({ - participantFilters: { - ...state.participantFilters, - ...filters, - }, - })); + set((state) => setParticipantFilters(filters, state)); }, - setParticipantSorting: (participantSorting) => - set({ - participantSorting, - }), - setExaminationResult: ( - participantId: string, - examinationResult: ExaminationResult, - ) => - set((state) => ({ - participants: replaceExaminationResult( - participantId, - examinationResult, - state.participants, - ), - })), + setParticipantSorting: (participantSorting) => set({ participantSorting }), + setProphylaxisSession: (prophylaxisSession) => set(prophylaxisSession), + setExamination: ( + examinationId: string, + result: ExaminationResult | undefined, + note: string | undefined, + ) => set((state) => setExamination(examinationId, result, note, state)), + + markAsSynchronized: () => set({ changedExaminationsById: new Set() }), })); } diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncIncomingProphylaxisSessionChanges.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncIncomingProphylaxisSessionChanges.ts new file mode 100644 index 000000000..c039a6781 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncIncomingProphylaxisSessionChanges.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ProphylaxisSessionDetails } from "@eshg/dental/api/models/ProphylaxisSessionDetails"; +import { useEffect } from "react"; +import { StoreApi, useStore } from "zustand"; + +import { type ProphylaxisSessionStore } from "./prophylaxisSessionStore"; + +export function useSyncIncomingProphylaxisSessionChanges( + store: StoreApi<ProphylaxisSessionStore>, + prophylaxisSession: ProphylaxisSessionDetails, +) { + const changedExaminationsById = useStore( + store, + (state) => state.changedExaminationsById, + ); + const setProphylaxisSession = useStore( + store, + (state) => state.setProphylaxisSession, + ); + + const canUpdate = changedExaminationsById.size === 0; + useEffect(() => { + if (canUpdate) { + setProphylaxisSession(prophylaxisSession); + } + }, [prophylaxisSession, canUpdate, setProphylaxisSession]); +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncOutgoingProphylaxisSessionChanges.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncOutgoingProphylaxisSessionChanges.ts new file mode 100644 index 000000000..256452a44 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/prophylaxisSessionStore/useSyncOutgoingProphylaxisSessionChanges.ts @@ -0,0 +1,117 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiExaminationResult, + ApiUpdateExaminationsInBulkRequest, +} from "@eshg/dental-api"; +import { ChildExamination } from "@eshg/dental/api/models/ChildExamination"; +import { + AbsenceExaminationResult, + ExaminationResult, + FluoridationExaminationResult, + ScreeningExaminationResult, +} from "@eshg/dental/api/models/ExaminationResult"; +import { useUpdateProphylaxisSessionExaminations } from "@eshg/dental/api/mutations/prophylaxisSessionApi"; +import { useEffect, useRef } from "react"; + +import { useProphylaxisSessionStore } from "./ProphylaxisSessionStoreProvider"; + +export function useSyncOutgoingProphylaxisSessionChanges() { + const lastSynchronizedChanges = useRef<Set<string> | null>(null); + const prophylaxisSessionId = useProphylaxisSessionStore((state) => state.id); + const participants = useProphylaxisSessionStore( + (state) => state.participants, + ); + const changedExaminationsById = useProphylaxisSessionStore( + (state) => state.changedExaminationsById, + ); + const markAsSynchronized = useProphylaxisSessionStore( + (state) => state.markAsSynchronized, + ); + const { mutate: updateProphylaxisSessionExaminations } = + useUpdateProphylaxisSessionExaminations(prophylaxisSessionId, { + /** + * pass success handler as hook option to ensure execution + * @see https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire + */ + onSuccess: markAsSynchronized, + }); + + useEffect(() => { + if ( + changedExaminationsById.size > 0 && + changedExaminationsById !== lastSynchronizedChanges.current + ) { + lastSynchronizedChanges.current = changedExaminationsById; + const changedExaminations = participants.filter((participant) => + changedExaminationsById.has(participant.examinationId), + ); + updateProphylaxisSessionExaminations( + changedExaminations.map(mapExaminationToRequest), + ); + } + }, [ + changedExaminationsById, + participants, + updateProphylaxisSessionExaminations, + markAsSynchronized, + ]); +} + +function mapExaminationToRequest( + examination: ChildExamination, +): ApiUpdateExaminationsInBulkRequest { + return { + id: examination.examinationId, + version: examination.examinationVersion, + result: mapExaminationResultToRequest(examination.result), + note: examination.note, + }; +} + +function mapExaminationResultToRequest( + examinationResult: ExaminationResult | undefined, +): ApiExaminationResult | undefined { + switch (examinationResult?.type) { + case "screening": + return mapScreeningResult(examinationResult); + case "fluoridation": + return mapFluoridationResult(examinationResult); + case "absence": + return mapAbsenceResult(examinationResult); + case undefined: + return undefined; + } +} + +function mapScreeningResult( + screeningResult: ScreeningExaminationResult, +): ApiExaminationResult { + return { + type: "ScreeningExaminationResult", + fluorideVarnishApplied: screeningResult.fluorideVarnishApplied, + oralHygieneStatus: screeningResult.oralHygieneStatus, + toothDiagnoses: Object.values(screeningResult.toothDiagnoses), + }; +} + +function mapFluoridationResult( + fluoridationResult: FluoridationExaminationResult, +): ApiExaminationResult { + return { + type: "FluoridationExaminationResult", + fluorideVarnishApplied: fluoridationResult.fluorideVarnishApplied, + }; +} + +function mapAbsenceResult( + absenceResult: AbsenceExaminationResult, +): ApiExaminationResult { + return { + type: "AbsenceExaminationResult", + reasonForAbsence: absenceResult.reasonForAbsence, + }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/staff.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/staff.ts new file mode 100644 index 000000000..427ddd822 --- /dev/null +++ b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/staff.ts @@ -0,0 +1,25 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + getAllDentalAssistantsQuery, + getAllDentistsQuery, +} from "@eshg/dental/api/queries/staffApi"; +import { useSuspenseQueries } from "@tanstack/react-query"; + +import { useUserApi } from "@/lib/baseModule/api/clients"; + +export function useGetStaff() { + const userApi = useUserApi(); + const [{ data: allDentists }, { data: allDentalAssistants }] = + useSuspenseQueries({ + queries: [ + getAllDentistsQuery(userApi), + getAllDentalAssistantsQuery(userApi), + ], + }); + + return { allDentists, allDentalAssistants }; +} diff --git a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/replaceExaminationResult.ts b/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/replaceExaminationResult.ts deleted file mode 100644 index bbd050919..000000000 --- a/employee-portal/src/lib/businessModules/dental/features/prophylaxisSessions/store/replaceExaminationResult.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { ChildExamination } from "@eshg/dental/api/models/ChildExamination"; -import { ExaminationResult } from "@eshg/dental/api/models/ExaminationResult"; - -export function replaceExaminationResult( - participantId: string, - examinationResult: ExaminationResult, - participants: ChildExamination[], -): ChildExamination[] { - return participants.map((participant) => { - if (participant.childId !== participantId) { - return participant; - } - - return { - ...participant, - result: examinationResult, - }; - }); -} diff --git a/employee-portal/src/lib/businessModules/dental/import/ImportChildrenSidebar.tsx b/employee-portal/src/lib/businessModules/dental/import/ImportChildrenSidebar.tsx index f7ccc9123..2a6aaabfa 100644 --- a/employee-portal/src/lib/businessModules/dental/import/ImportChildrenSidebar.tsx +++ b/employee-portal/src/lib/businessModules/dental/import/ImportChildrenSidebar.tsx @@ -78,6 +78,7 @@ function ImportChildrenSidebar(props: SidebarWithFormRefProps) { name="institution" label="Einrichtung" categories={SCHOOL_OR_DAYCARE} + placeholder="Schule/Kita suchen" required="Bitte eine Schule/Kita angeben." getOptionLabel={getInstitutionOptionLabel} /> diff --git a/employee-portal/src/lib/businessModules/inspection/api/clients.ts b/employee-portal/src/lib/businessModules/inspection/api/clients.ts index 8d814fbb3..95c2137b8 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/clients.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/clients.ts @@ -28,7 +28,7 @@ import { TaskApi, TextBlockApi, WebSearchApi, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; export function useConfiguration() { diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklist.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklist.ts index c4635a168..9b132f4b2 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklist.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklist.ts @@ -7,7 +7,7 @@ import { ApiUpdateChecklistElementsInner, ChecklistDeleteFileRequest, UpdateChecklistRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts index 3d7637df8..c3546279f 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/checklistDefinition.ts @@ -12,7 +12,7 @@ import { ApiCreateNewChecklistDefinitionRequest, DeleteChecklistDefinitionFromCentralRepoRequest, UpdateChecklistDefinitionToCentralRepoRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/facility.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/facility.ts index 060dc724d..c998b6369 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/facility.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/facility.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspLinkBaseFacilityRequest } from "@eshg/employee-portal-api/inspection"; +import { ApiInspLinkBaseFacilityRequest } from "@eshg/inspection-api"; import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/incidents.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/incidents.ts index 9bd704e6f..373630a5e 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/incidents.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/incidents.ts @@ -7,7 +7,7 @@ import { CreateIncidentRequest, DeleteIncidentRequest, UpdateIncidentRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; 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 a72e01f72..b89137432 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/inspection.ts @@ -10,7 +10,7 @@ import { ResolveInspectionDuplicateRequest, StartInspectionRequest, UpdateInspectionRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts index f25660533..5f68b7106 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/inventory.ts @@ -4,7 +4,7 @@ */ import { ApiErrorCode } from "@eshg/base-api"; -import { ModifyInventoryRequest } from "@eshg/employee-portal-api/inspection"; +import { ModifyInventoryRequest } from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/objectTypes.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/objectTypes.ts index 4461fafb3..c0d157fe8 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/objectTypes.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/objectTypes.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUpdateObjectTypeRequest } from "@eshg/employee-portal-api/inspection"; +import { ApiUpdateObjectTypeRequest } from "@eshg/inspection-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/packlist.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/packlist.ts index 51a6bb4ba..77bcd2bec 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/packlist.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/packlist.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { CheckPacklistElementRequest } from "@eshg/employee-portal-api/inspection"; +import { CheckPacklistElementRequest } from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/packlistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/packlistDefinition.ts index 6ceab5ef0..629f085fd 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/packlistDefinition.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/packlistDefinition.ts @@ -7,7 +7,7 @@ import { ApiAddPacklistDefinitionRevisionRequest, ApiCreateNewPacklistDefinitionRequest, ApiPacklistDefinitionElement, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { usePacklistDefinitionApi } from "@/lib/businessModules/inspection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts index db55b928c..6397a0bfc 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/processImport.ts @@ -8,7 +8,7 @@ import { ApiImportStatisticsFromJSON, ApiResponse, ImportProcessesRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useImportApi } from "@/lib/businessModules/inspection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/resources.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/resources.ts index 8016467f1..fc3331570 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/resources.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/resources.ts @@ -6,7 +6,7 @@ import { AddResourceRequest, DeleteResourceRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/textblocks.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/textblocks.ts index c95b01387..45c850e92 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/textblocks.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/textblocks.ts @@ -6,7 +6,7 @@ import { ApiTextBlockRequest, UpdateTextBlockRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useTextBlockApi } from "@/lib/businessModules/inspection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/mutations/webSearch.ts b/employee-portal/src/lib/businessModules/inspection/api/mutations/webSearch.ts index cac7cbdaa..d639720fe 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/mutations/webSearch.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/mutations/webSearch.ts @@ -9,7 +9,7 @@ import { SaveQueryRequest, UpdateWebSearchByIdRequest, UpdateWebSearchEntryRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/archiving.ts index 6af542a46..a6fc41ac8 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/inspection/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts index 4f1e6653b..26d380156 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/checklist.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ChecklistApi } from "@eshg/employee-portal-api/inspection"; +import { ChecklistApi } from "@eshg/inspection-api"; import { queryOptions } from "@tanstack/react-query"; import { checklistApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts index 46cabb9d9..974b820ed 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/checklistDefinition.ts @@ -6,7 +6,7 @@ import { ChecklistDefinitionApi, ChecklistDefinitionCentralRepoApi, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { 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 6149b37d5..7813d8656 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/facility.ts @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - FacilityApi, - GetPendingFacilitiesRequest, -} from "@eshg/employee-portal-api/inspection"; +import { FacilityApi, GetPendingFacilitiesRequest } from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/feature.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/feature.ts index c4d9df160..8ff98872a 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/feature.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/feature.ts @@ -6,7 +6,7 @@ import { ApiGetInspectionFeatureTogglesResponse, ApiInspectionFeature, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { FeatureToggleQueryOptions, selectDisabledOldFeature, diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/geo.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/geo.ts index 04e54c370..eb4a1cedf 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/geo.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/geo.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { InspectionGeoApi } from "@eshg/employee-portal-api/inspection"; +import { InspectionGeoApi } from "@eshg/inspection-api"; import { QueryClient } from "@tanstack/react-query"; import { inspectionGeoApiQueryKey } from "@/lib/businessModules/inspection/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts index 0a02ba5cb..451c6e784 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/incidents.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { InspectionIncidentApi } from "@eshg/employee-portal-api/inspection"; +import { InspectionIncidentApi } from "@eshg/inspection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useIncidentApi } from "@/lib/businessModules/inspection/api/clients"; 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 5c4838b5d..d57f40a37 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspection.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { InspectionApi } from "@eshg/employee-portal-api/inspection"; +import { InspectionApi } from "@eshg/inspection-api"; import { queryOptions, useQueryClient, diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts index 5d0daeb65..bc9f6093e 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/inspectionReport.ts @@ -9,7 +9,7 @@ import { EditorApi, InspectionApi, TextBlockApi, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts index 60550f063..42802d135 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/objectTypes.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ObjectTypeApi } from "@eshg/employee-portal-api/inspection"; +import { ObjectTypeApi } from "@eshg/inspection-api"; import { STATIC_QUERY_OPTIONS } from "@eshg/lib-portal/api/queryOptions"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts index e996bfdf7..3e0b82388 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/packlistDefinition.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { PacklistDefinitionApi } from "@eshg/employee-portal-api/inspection"; +import { PacklistDefinitionApi } from "@eshg/inspection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { usePacklistDefinitionApi } from "@/lib/businessModules/inspection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/textblocks.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/textblocks.ts index bf85dab6e..f249e74ba 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/textblocks.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/textblocks.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { GetTextBlocksRequest } from "@eshg/employee-portal-api/inspection"; +import { GetTextBlocksRequest } from "@eshg/inspection-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useTextBlockApi } from "@/lib/businessModules/inspection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/inspection/api/queries/webSearch.ts b/employee-portal/src/lib/businessModules/inspection/api/queries/webSearch.ts index d11476408..5ebf7e05f 100644 --- a/employee-portal/src/lib/businessModules/inspection/api/queries/webSearch.ts +++ b/employee-portal/src/lib/businessModules/inspection/api/queries/webSearch.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { SearchRequest } from "@eshg/employee-portal-api/inspection"; +import { SearchRequest } from "@eshg/inspection-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useSuspenseQuery } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/EditChecklistDefinition.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/EditChecklistDefinition.tsx index 360324e01..0c3c0c463 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/EditChecklistDefinition.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/EditChecklistDefinition.tsx @@ -8,7 +8,7 @@ import { ApiChecklistDefinitionVersion, ApiObjectType, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; import { InfoOutlined } from "@mui/icons-material"; @@ -109,6 +109,7 @@ export function EditChecklistDefinition({ initialValues={formData} onSubmit={sendToBackend} enableReinitialize + validateOnChange={false} > {({ isSubmitting }) => ( <FormPlus> diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElement.tsx index 06ae3f6bb..2482b0566 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElement.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElement.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSectionContextElementsInner } from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElementsList.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElementsList.tsx index 2a84816ac..5c9173aab 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElementsList.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElementsList.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ApiCLSectionContextElementsInner } from "@eshg/inspection-api"; import { useNonce } from "@eshg/lib-portal/components/NonceProvider"; import { DragDropContext, @@ -13,6 +14,7 @@ import { } from "@hello-pangea/dnd"; import { Box, Stack } from "@mui/joy"; import { FieldArray, useFormikContext } from "formik"; +import { memo } from "react"; import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition"; import { ChecklistDefinitionElement } from "@/lib/businessModules/inspection/components/checklistDefinition/editor/elements/ChecklistDefinitionElement"; @@ -50,35 +52,15 @@ export function ChecklistDefinitionElementsList({ > {values.context.sections[sectionIndex]?.elements.map( (element, elementIndex) => ( - <Draggable + <MemoizedDraggableChecklistDefinitionElement key={element.id} - draggableId={element.id} - index={elementIndex} - > - {(provided, snapshot) => ( - <Box - ref={provided.innerRef} - {...provided.draggableProps} - sx={getItemStyle( - snapshot.isDragging, - provided.draggableProps.style, - )} - > - <ChecklistDefinitionElement - dragHandleProps={provided.dragHandleProps} - key={element.id} - element={element} - setElement={(element) => - replace(elementIndex, element) - } - deleteElement={() => remove(elementIndex)} - addElement={(element) => push(element)} - sectionIndex={sectionIndex} - elementIndex={elementIndex} - /> - </Box> - )} - </Draggable> + element={element} + elementIndex={elementIndex} + sectionIndex={sectionIndex} + push={push} + remove={remove} + replace={replace} + /> ), )} {provided.placeholder} @@ -92,6 +74,53 @@ export function ChecklistDefinitionElementsList({ ); } +const MemoizedDraggableChecklistDefinitionElement = memo( + function DraggableChecklistDefinitionElement({ + element, + elementIndex, + sectionIndex, + push, + remove, + replace, + }: { + element: ApiCLSectionContextElementsInner; + elementIndex: number; + sectionIndex: number; + push: (element: ApiCLSectionContextElementsInner) => void; + remove: (elementIndex: number) => void; + replace: ( + elementIndex: number, + element: ApiCLSectionContextElementsInner, + ) => void; + }) { + return ( + <Draggable draggableId={element.id} index={elementIndex}> + {(provided, snapshot) => ( + <Box + ref={provided.innerRef} + {...provided.draggableProps} + sx={getItemStyle( + snapshot.isDragging, + provided.draggableProps.style, + )} + > + <ChecklistDefinitionElement + dragHandleProps={provided.dragHandleProps} + key={element.id} + element={element} + setElement={(element) => replace(elementIndex, element)} + deleteElement={() => remove(elementIndex)} + addElement={(element) => push(element)} + sectionIndex={sectionIndex} + elementIndex={elementIndex} + /> + </Box> + )} + </Draggable> + ); + }, +); + function getItemStyle( isDragging: boolean, draggableStyle: DraggingStyle | NotDraggingStyle | undefined, diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/NoteAndHelpTextInput.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/NoteAndHelpTextInput.tsx index fe5e26824..68df56026 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/NoteAndHelpTextInput.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/NoteAndHelpTextInput.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSectionContextElementsInner } from "@eshg/inspection-api"; import { Box } from "@mui/joy"; import { OptionalInputField } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/OptionalInputField"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionAnswerItem.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionAnswerItem.tsx index 4661d02a9..f37f977c6 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionAnswerItem.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionAnswerItem.tsx @@ -3,12 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLFieldOptionContext } from "@eshg/employee-portal-api/inspection"; +import { + ApiCLFieldOptionContext, + ApiCLSectionContextElementsInner, +} from "@eshg/inspection-api"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { SubdirectoryArrowRight } from "@mui/icons-material"; import { Stack } from "@mui/joy"; import { useFormikContext } from "formik"; -import { useState } from "react"; +import { memo, useState } from "react"; import { FormChecklistDefinitionVersion } from "@/lib/businessModules/inspection/api/mutations/checklistDefinition"; import { FlexInputField } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/FlexInputField"; @@ -21,81 +24,101 @@ interface ChecklistDefinitionAnswerItemProps { elementIndex: number; itemIndex: number; item: ApiCLFieldOptionContext; - setItem: (item: ApiCLFieldOptionContext) => void; onDelete: () => void; hideDeleteButton?: boolean; } -export function ChecklistDefinitionAnswerItem({ - sectionIndex, - elementIndex, - itemIndex, - item, - onDelete, - hideDeleteButton = false, -}: Readonly<ChecklistDefinitionAnswerItemProps>) { +export function ChecklistDefinitionAnswerItem( + props: Readonly<ChecklistDefinitionAnswerItemProps>, +) { const { values } = useFormikContext<FormChecklistDefinitionVersion>(); - const [showTextModules, setShowTextModules] = useState(false); - function validateMultipleAnswers(value: OptionalFieldValue<string>) { - if (value === "") { - return undefined; - } + return ( + <MemoizedChecklistDefinitionAnswerItem + {...props} + element={ + values.context.sections[props.sectionIndex]?.elements[ + props.elementIndex + ] + } + /> + ); +} + +interface InnerChecklistDefinitionAnswerItemProps + extends ChecklistDefinitionAnswerItemProps { + element?: ApiCLSectionContextElementsInner; +} - switch ( - values.context.sections[sectionIndex]?.elements[elementIndex]?.type - ) { - case "MULTI_SELECT": - case "CLMultiSelectContext": - case "SINGLE_SELECT": - case "CLSingleSelectContext": { - const occurances = values.context.sections[sectionIndex]?.elements[ - elementIndex - ].items!.filter((answer) => answer.text === value); - if (occurances.length > 1) { - return "Bitte unterschiedliche Werte eingeben."; - } else { - return undefined; +const MemoizedChecklistDefinitionAnswerItem = memo( + function InnerChecklistDefinitionAnswerItem({ + sectionIndex, + elementIndex, + itemIndex, + item, + onDelete, + hideDeleteButton = false, + element, + }: Readonly<InnerChecklistDefinitionAnswerItemProps>) { + const [showTextModules, setShowTextModules] = useState(false); + function validateMultipleAnswers(value: OptionalFieldValue<string>) { + if (value === "") { + return undefined; + } + + switch (element?.type) { + case "MULTI_SELECT": + case "CLMultiSelectContext": + case "SINGLE_SELECT": + case "CLSingleSelectContext": { + const occurances = element.items!.filter( + (answer) => answer.text === value, + ); + if (occurances.length > 1) { + return "Bitte unterschiedliche Werte eingeben."; + } else { + return undefined; + } } } } - } - return ( - <Stack spacing={2}> - <InputWithDeleteButton - name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.text`} - label={`Antwort ${itemIndex + 1}`} - placeholder="Antwort eingeben" - onDelete={onDelete} - validate={validateMultipleAnswers} - required="Bitte geben Sie eine Antwort ein." - hideDeleteButton={hideDeleteButton} - endDecorator={ - <TextModuleToggle - checked={showTextModules} - onToggle={(show) => setShowTextModules(show)} - count={countTextModules(item)} - /> - } - /> - {showTextModules && ( - <> - <FlexInputField - name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleTrue`} - multiline - label={`Textbaustein für Antwort ${itemIndex + 1} (ausgewählt)`} - placeholder="Textbaustein eingeben" - startDecorator={<SubdirectoryArrowRight />} - /> - <FlexInputField - name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleFalse`} - multiline - label={`Textbaustein für Antwort ${itemIndex + 1} (nicht ausgewählt)`} - placeholder="Textbaustein eingeben" - startDecorator={<SubdirectoryArrowRight />} - /> - </> - )} - </Stack> - ); -} + return ( + <Stack spacing={2}> + <InputWithDeleteButton + name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.text`} + label={`Antwort ${itemIndex + 1}`} + placeholder="Antwort eingeben" + onDelete={onDelete} + validate={validateMultipleAnswers} + required="Bitte geben Sie eine Antwort ein." + hideDeleteButton={hideDeleteButton} + endDecorator={ + <TextModuleToggle + checked={showTextModules} + onToggle={(show) => setShowTextModules(show)} + count={countTextModules(item)} + /> + } + /> + {showTextModules && ( + <> + <FlexInputField + name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleTrue`} + multiline + label={`Textbaustein für Antwort ${itemIndex + 1} (ausgewählt)`} + placeholder="Textbaustein eingeben" + startDecorator={<SubdirectoryArrowRight />} + /> + <FlexInputField + name={`context.sections.${sectionIndex}.elements.${elementIndex}.items.${itemIndex}.textModuleFalse`} + multiline + label={`Textbaustein für Antwort ${itemIndex + 1} (nicht ausgewählt)`} + placeholder="Textbaustein eingeben" + startDecorator={<SubdirectoryArrowRight />} + /> + </> + )} + </Stack> + ); + }, +); diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx index bb056b085..dba410c02 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementCheckboxInner.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLCheckboxContext } from "@eshg/employee-portal-api/inspection"; +import { ApiCLCheckboxContext } from "@eshg/inspection-api"; import { SubdirectoryArrowRight } from "@mui/icons-material"; import { Radio, Stack } from "@mui/joy"; import { useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementInner.tsx index 88e433165..2f92d0d6c 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementInner.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementInner.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSectionContextElementsInner } from "@eshg/inspection-api"; import type { FC } from "react"; import { ChecklistDefinitionElementCheckboxInner } from "@/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementCheckboxInner"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementMultiInner.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementMultiInner.tsx index 1c179d1b8..f07ec65d2 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementMultiInner.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/elements/inner/ChecklistDefinitionElementMultiInner.tsx @@ -7,7 +7,7 @@ import { ApiCLFieldOptionContext, type ApiCLMultiSelectContext, ApiCLSectionContextElementsInner, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Add } from "@mui/icons-material"; import { Button, Stack } from "@mui/joy"; import { v4 as uuidv4 } from "uuid"; @@ -53,13 +53,6 @@ export function ChecklistDefinitionElementMultiInner({ }); } - function setItem(index: number, item: ApiCLFieldOptionContext) { - items[index] = item; - updateElement({ - items: [...items], - }); - } - return ( <Stack spacing={2}> {items.map((item, index) => { @@ -73,7 +66,6 @@ export function ChecklistDefinitionElementMultiInner({ elementIndex={elementIndex} itemIndex={index} item={item} - setItem={(item) => setItem(index, item)} onDelete={() => deleteItem(index)} hideDeleteButton={hideDeleteButton} /> diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderCard.tsx index 6e6a5f43a..e3076326f 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderCard.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderCard.tsx @@ -4,7 +4,7 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { ApiObjectType } from "@eshg/employee-portal-api/inspection"; +import { ApiObjectType } from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderRow.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderRow.tsx index 346bcffdf..1281d82af 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderRow.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/header/ChecklistDefinitionHeaderRow.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUser } from "@eshg/employee-portal-api/inspection"; +import { ApiUser } from "@eshg/inspection-api"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/sections/ChecklistDefinitionSection.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/sections/ChecklistDefinitionSection.tsx index 4815c0a2c..b182ec949 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/sections/ChecklistDefinitionSection.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/editor/sections/ChecklistDefinitionSection.tsx @@ -8,7 +8,7 @@ import { ApiCLSectionContext, ApiCLSectionContextElementsInner, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd"; import { Add } from "@mui/icons-material"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/OptionalInputField.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/OptionalInputField.tsx index 725eb6515..c247f75d0 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/OptionalInputField.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/OptionalInputField.tsx @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useBaseField } from "@eshg/lib-portal/components/formFields/BaseField"; import { Add } from "@mui/icons-material"; import { Button } from "@mui/joy"; -import { useFormikContext } from "formik"; -import { useEffect, useState } from "react"; +import { FormikErrors } from "formik"; +import { memo, useState } from "react"; import { FlexInputFieldProps } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/FlexInputField"; import { InputWithDeleteButton } from "@/lib/businessModules/inspection/components/checklistDefinition/helpers/InputWithDeleteButton"; @@ -15,23 +16,37 @@ export interface OptionalInputFieldProps extends FlexInputFieldProps { addButtonLabel: string; } -export function OptionalInputField({ +export function OptionalInputField(props: Readonly<OptionalInputFieldProps>) { + const { meta, helpers } = useBaseField(props); + + return ( + <MemoizedOptionalInputField + {...props} + setValue={helpers.setValue} + hasInitialValue={!!meta.initialValue} + /> + ); +} + +interface InnerOptionalInputFieldProps extends OptionalInputFieldProps { + setValue: ( + value: string, + shouldValidate?: boolean, + ) => Promise<void | FormikErrors<string>>; + hasInitialValue: boolean; +} + +const MemoizedOptionalInputField = memo(function InnerOptionalInputField({ addButtonLabel, + hasInitialValue, + setValue, ...props -}: Readonly<OptionalInputFieldProps>) { - const { getFieldProps, setFieldValue } = useFormikContext(); - const [showInput, setShowInput] = useState(false); - - // show input if it has an initial value - useEffect(() => { - const { value } = getFieldProps<string>(props.name); - setShowInput(!!value); - // eslint-disable-next-line react-hooks/exhaustive-deps -- only run on mount - }, [props.name]); +}: Readonly<InnerOptionalInputFieldProps>) { + const [showInput, setShowInput] = useState(hasInitialValue); function handleDelete() { setShowInput(false); - void setFieldValue(props.name, ""); + void setValue(""); } if (showInput) { @@ -49,4 +64,4 @@ export function OptionalInputField({ {addButtonLabel} </Button> ); -} +}); diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts index bef9e97aa..fb7169e54 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/helpers/helpers.ts @@ -14,7 +14,7 @@ import { ApiCLSingleSelectContext, type ApiCLTextElementContext, ApiChecklistDefinitionVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ConfirmationDialogProps } from "@eshg/lib-portal/components/confirmationDialog/BaseConfirmationDialog"; import { v4 as uuidv4 } from "uuid"; 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 ae73692a5..011ed4054 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 @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiChecklistDefinition } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinition } from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Row } from "@tanstack/react-table"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx index 6e76b3c0f..26bb38226 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/overview/columns.tsx @@ -6,7 +6,7 @@ import { ApiChecklistDefinition, ApiChecklistDefinitionVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ConfirmationDialogProps } from "@eshg/lib-portal/components/confirmationDialog/BaseConfirmationDialog"; import { Snackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/CLDInfoCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/CLDInfoCard.tsx index 7022e431c..c7aaa99c8 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/CLDInfoCard.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/CLDInfoCard.tsx @@ -4,7 +4,7 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionVersion } from "@eshg/inspection-api"; import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { CopyAllOutlined, InfoOutlined } from "@mui/icons-material"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDContent.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDContent.tsx index d6855f2db..c7019b653 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDContent.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDContent.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionVersion } from "@eshg/inspection-api"; import { Sheet, Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDPage.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDPage.tsx index 1182b75f3..be420a422 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDPage.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDPage.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionVersion } from "@eshg/inspection-api"; import { InfoOutlined } from "@mui/icons-material"; import { Alert, Box } from "@mui/joy"; import { ReactNode } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDSection.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDSection.tsx index 717be100c..b2f8c293d 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDSection.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/ReadOnlyCLDSection.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSectionContext } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSectionContext } from "@eshg/inspection-api"; import { Stack, Typography } from "@mui/joy"; import { ReadOnlyCLDElement } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement.tsx index a9afcd8a4..4a6ac2252 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSectionContextElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSectionContextElementsInner } from "@eshg/inspection-api"; import { Divider } from "@mui/joy"; import { ReadOnlyCLDElementCheckbox } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementCheckbox"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementCheckbox.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementCheckbox.tsx index 677e56aff..92d278cde 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementCheckbox.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementCheckbox.tsx @@ -6,7 +6,7 @@ import { ApiCLCheckboxContext, ApiCLMultiSelectContext, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ReadOnlyCLDElementProps } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement"; import { ReadOnlyCLDElementMultiSelect } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementMultiSelect"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementFile.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementFile.tsx index 4a9af6644..ef5b149b6 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementFile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementFile.tsx @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiCLAudioContext, - ApiCLImageContext, -} from "@eshg/employee-portal-api/inspection"; +import { ApiCLAudioContext, ApiCLImageContext } from "@eshg/inspection-api"; import { UploadFileOutlined } from "@mui/icons-material"; import { Button } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementMultiSelect.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementMultiSelect.tsx index 704ef1ed7..62b91678f 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementMultiSelect.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementMultiSelect.tsx @@ -6,7 +6,7 @@ import { ApiCLFieldOptionContext, ApiCLMultiSelectContext, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Box, List, ListItem, Typography, styled } from "@mui/joy"; import { ReadOnlyCLDElementProps } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementText.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementText.tsx index 46c13bf6e..c60872839 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementText.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/inner/ReadOnlyCLDElementText.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLTextElementContext } from "@eshg/employee-portal-api/inspection"; +import { ApiCLTextElementContext } from "@eshg/inspection-api"; import { Card } from "@mui/joy"; import { ReadOnlyCLDElementProps } from "@/lib/businessModules/inspection/components/checklistDefinition/readOnly/elements/ReadOnlyCLDElement"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/UploadChecklistToRepoSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/UploadChecklistToRepoSidebar.tsx index c4365d706..8d112b3be 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/UploadChecklistToRepoSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/UploadChecklistToRepoSidebar.tsx @@ -6,7 +6,7 @@ import { ApiChecklistDefinition, ApiChecklistDefinitionCentralRepoRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx index 63cff01cd..ce4f052b6 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/ChecklistVersionsSidebar.tsx @@ -9,7 +9,7 @@ import { ApiUserRole } from "@eshg/base-api"; import { ApiChecklistDefinition, ApiChecklistDefinitionVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/VersionSheet.tsx b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/VersionSheet.tsx index 825276f2c..d03f82ea2 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/VersionSheet.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/checklistDefinition/sidebars/history/VersionSheet.tsx @@ -7,7 +7,7 @@ import { ApiUserRole } from "@eshg/base-api"; import { ApiChecklistDefinition, ApiChecklistDefinitionVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { ConfirmationDialogProps } from "@eshg/lib-portal/components/confirmationDialog/BaseConfirmationDialog"; import { 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 index 227cb6726..d8ad5e2a8 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/FacilityDuplicateTile.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiFacilityForDuplicateReview } from "@eshg/employee-portal-api/inspection"; +import { ApiFacilityForDuplicateReview } from "@eshg/inspection-api"; import { Radio, Sheet, Stack, Typography } from "@mui/joy"; import { DuplicateTileLine } from "@/lib/businessModules/inspection/components/facility/pending/DuplicateTileLine"; 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 index f6476a1bd..a19cc6899 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/InspectionDuplicateTile.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionForDuplicateReview } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionForDuplicateReview } from "@eshg/inspection-api"; import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime"; import { Sheet, Stack, Typography } from "@mui/joy"; 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 ec2eac3dd..977ffdbfa 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 @@ -9,7 +9,7 @@ import { ApiGetReferenceFacilityResponse } from "@eshg/base-api"; import { type ApiInspAddFacilityResponse, ApiProcedureStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Add } from "@mui/icons-material"; import { Button } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar.tsx index e460c227a..85b9b1d24 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/pending/PendingFacilitiesIncidentsSidebar.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionIncident } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionIncident } from "@eshg/inspection-api"; import { Sheet, Stack, Typography } from "@mui/joy"; import { useGetIncidents } from "@/lib/businessModules/inspection/api/queries/incidents"; 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 e59f75a28..caeec0115 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,9 +5,9 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; -import { ApiObjectType } from "@eshg/employee-portal-api/inspection"; +import { ApiObjectType } from "@eshg/inspection-api"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { addDays, formatISO } from "date-fns"; import { useMemo, useState } from "react"; 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 3a558839b..7e3bfcd52 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 @@ -6,7 +6,7 @@ import { type ApiInspPendingFacility, ApiProcedureStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { IconButton, Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchImportSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchImportSidebar.tsx index 0a1213b5b..3c9704c77 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchImportSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/FacilityWebSearchImportSidebar.tsx @@ -8,7 +8,7 @@ import { ApiInspAddFacilityResponse, ApiProcedureStatus, ApiWebSearchEntry, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Card, Chip, Typography } from "@mui/joy"; import { useRouter } from "next/navigation"; 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 7f7cfba6e..e56113b4f 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 @@ -8,7 +8,7 @@ import { type ApiWebSearchOverviewEntry, type ApiWebSearchStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { DeleteOutlined, Edit, PlayArrow } from "@mui/icons-material"; import { ColorPaletteProp } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/FacilityWebSearchResultsTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/FacilityWebSearchResultsTable.tsx index 99ce013af..48286530f 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/FacilityWebSearchResultsTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/FacilityWebSearchResultsTable.tsx @@ -10,7 +10,7 @@ import { ApiWebSearchEntriesResponse, ApiWebSearchEntry, ApiWebSearchQuery, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import AddIcon from "@mui/icons-material/Add"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx index 1fdd607a9..ec90f2fb4 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/facility/search/results/columns.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiWebSearchEntry } from "@eshg/employee-portal-api/inspection"; +import { ApiWebSearchEntry } from "@eshg/inspection-api"; import { Add } from "@mui/icons-material"; import { Checkbox, Chip, CircularProgress, Stack } from "@mui/joy"; import { Cell, ColumnHelper, createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inbox/InspectionInboxProcedureCreateSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inbox/InspectionInboxProcedureCreateSidebar.tsx index f461f74cc..9834c75ca 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inbox/InspectionInboxProcedureCreateSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inbox/InspectionInboxProcedureCreateSidebar.tsx @@ -8,7 +8,7 @@ import { ApiGetReferenceFacilityResponse, ApiInboxProcedure, ApiProcedureStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { assertNever } from "@eshg/lib-portal/helpers/assertions"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/EditFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/EditFacilitySidebar.tsx index d98c5c3c0..50e49bb97 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/EditFacilitySidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/EditFacilitySidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetFacilityFileStateResponse } from "@eshg/employee-portal-api/inspection"; +import { ApiGetFacilityFileStateResponse } from "@eshg/inspection-api"; import { DefaultFacilityFormValues, diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionLockInfo.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionLockInfo.tsx index fc13c1db6..ae47e4aa6 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionLockInfo.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionLockInfo.tsx @@ -4,7 +4,7 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { type ApiInspection } from "@eshg/employee-portal-api/inspection"; +import { type ApiInspection } from "@eshg/inspection-api"; import { Clear } from "@mui/icons-material"; import { Box, IconButton, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionPhaseSelect.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionPhaseSelect.tsx index 9df78c100..f00a63edb 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionPhaseSelect.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionPhaseSelect.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspection } from "@eshg/employee-portal-api/inspection"; +import { ApiInspection } from "@eshg/inspection-api"; import { Typography } from "@mui/joy"; import { translateInspectionPhase } from "@/lib/businessModules/inspection/shared/enums"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabHeader.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabHeader.tsx index be274d012..5a8403eb5 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabHeader.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/InspectionTabHeader.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { type ApiInspection } from "@eshg/employee-portal-api/inspection"; +import { type ApiInspection } from "@eshg/inspection-api"; import { InspectionLockInfo } from "@/lib/businessModules/inspection/components/inspection/InspectionLockInfo"; import { InspectionPhaseSelect } from "@/lib/businessModules/inspection/components/inspection/InspectionPhaseSelect"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/OfflineSwitch.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/OfflineSwitch.tsx index 819f268c2..634f53c78 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/OfflineSwitch.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/OfflineSwitch.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPhase } from "@eshg/inspection-api"; 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"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx index 3ec36bda7..132d5bbe2 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTabBasedata.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPhase } from "@eshg/inspection-api"; import { Grid } from "@mui/joy"; import { useSuspenseQueries } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx index ad2482a29..0bf5ba8f5 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/InspectionTypeCard.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiInspection } from "@eshg/employee-portal-api/inspection"; +import { ApiInspection } from "@eshg/inspection-api"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { SetFieldValueHelper } from "@eshg/lib-portal/types/form"; import { Divider, Grid, Stack } from "@mui/joy"; @@ -23,11 +23,11 @@ import { import { InspectionAssigneeSelection } from "@/lib/businessModules/inspection/components/inspection/assignee/InspectionAssigneeSelection"; import { translateInspectionType } from "@/lib/businessModules/inspection/shared/enums"; import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsSection, SimplifiedModalProps, } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { SidebarForm, @@ -66,18 +66,15 @@ export function InspectionTypeCard({ } > <Grid container direction="column" gap={2}> - <DetailsCell - name="type" + <DetailsItem label="Art" value={translateInspectionType(inspection.type)} /> - <DetailsCell - name="challenging" + <DetailsItem label="Besonderheiten" value={inspection.challenging ? "Schwierige Gegebenheit" : "keine"} /> - <DetailsCell - name="assignee" + <DetailsItem label="Zugewiesene:r Bearbeiter:in" value={formatPersonName({ firstName: inspection.assignee?.firstName, diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/contactperson/ContactPersonTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/contactperson/ContactPersonTile.tsx index ff3ac95ee..6783df603 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/contactperson/ContactPersonTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/basedata/contactperson/ContactPersonTile.tsx @@ -13,7 +13,7 @@ import { isNonNullish } from "remeda"; import { EmailSection } from "@/lib/businessModules/inspection/components/inspection/common/EmailSection"; import { PhoneNumberSection } from "@/lib/businessModules/inspection/components/inspection/common/PhoneNumberSection"; import { TileDivider } from "@/lib/businessModules/inspection/components/inspection/common/TileDivider"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { InfoTileAddButton } from "@/lib/shared/components/infoTile/InfoTileAddButton"; @@ -45,41 +45,24 @@ export function ContactPersonTile({ <Grid container direction="row" sx={{ gap: 3 }}> {isNonNullish(contactPerson?.salutation) && contactPerson.salutation !== "NOT_SPECIFIED" && ( - <DetailsCell - name="salutation" + <DetailsItem label="Anrede" value={SALUTATION_VALUES[contactPerson.salutation]} /> )} {isNonNullish(contactPerson?.title) && contactPerson.title !== "Keine Angabe" && ( - <DetailsCell - name="title" - label="Titel" - value={contactPerson.title} - /> + <DetailsItem label="Titel" value={contactPerson.title} /> )} </Grid> {isNonNullish(contactPerson?.role) && ( - <DetailsCell - name="role" - label="Role" - value={contactPerson.role} - /> + <DetailsItem label="Role" value={contactPerson.role} /> )} {isNonNullish(contactPerson?.firstName) && ( - <DetailsCell - name="name" - label="Vorname" - value={contactPerson.firstName} - /> + <DetailsItem label="Vorname" value={contactPerson.firstName} /> )} {isNonNullish(contactPerson?.lastName) && ( - <DetailsCell - name="surname" - label="Nachname" - value={contactPerson.lastName} - /> + <DetailsItem label="Nachname" value={contactPerson.lastName} /> )} </Grid> </Grid> diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar.tsx index 92e7ad81c..8f315e81e 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionAppointment } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionAppointment } from "@eshg/inspection-api"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { toDateString } from "@eshg/lib-portal/helpers/dateTime"; import { Grid } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils.ts b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils.ts index 527706872..4ea017676 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils.ts +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionAppointment } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionAppointment } from "@eshg/inspection-api"; import { formatDate, formatTime } from "@eshg/lib-portal/formatters/dateTime"; import { isNonNullish } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/facility/FacilityTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/facility/FacilityTile.tsx index 7d5910bd2..01869b232 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/common/facility/FacilityTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/common/facility/FacilityTile.tsx @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspFacility } from "@eshg/employee-portal-api/inspection"; +import { ApiInspFacility } from "@eshg/inspection-api"; import { CentralFileFacilityDetails } from "@/lib/shared/components/centralFile/display/CentralFileFacilityDetails"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; interface FacilityTileProps { @@ -27,11 +27,7 @@ export function FacilityTile({ onEdit={!readonly ? onEdit : undefined} > <CentralFileFacilityDetails facility={facility.baseFacility}> - <DetailsCell - name="Objekttyp" - label="Objekttyp" - value={facility.objectType?.name} - /> + <DetailsItem label="Objekttyp" value={facility.objectType?.name} /> </CentralFileFacilityDetails> </InfoTile> ); diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx index 9df913809..e334509d1 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/ExecutionSidePanel.tsx @@ -9,11 +9,11 @@ import { ApiChecklist, ApiInspection, ApiInspectionPhase, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { scrollToFirstFormError } from "@eshg/lib-portal/components/form/FormPlus"; import AddIcon from "@mui/icons-material/Add"; import EditIcon from "@mui/icons-material/EditOutlined"; -import { Button, Divider, IconButton, Stack } from "@mui/joy"; +import { Button, Divider, Grid, IconButton, Stack, Typography } from "@mui/joy"; import { useState } from "react"; import { AppointmentSidebar } from "@/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar"; @@ -26,7 +26,7 @@ import { SidePanelNavigationTab, } from "@/lib/businessModules/inspection/components/inspection/execution/SidePanelNavigation"; import { useChecklistValidateContext } from "@/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistValidateContext"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { SidePanel } from "@/lib/shared/components/sidePanel/SidePanel"; import { SidePanelTitle } from "@/lib/shared/components/sidePanel/SidePanelTitle"; import { UserLink } from "@/lib/shared/components/users/UserLink"; @@ -159,38 +159,55 @@ export function ExecutionSidePanel({ <Divider sx={{ marginY: 1 }} /> - <DetailsCell - name="user" + <DetailsItem label={"Durchführung durch"} value={ - inspection?.executedAppointment?.assignedTo && ( + (inspection?.executedAppointment?.assignedTo && ( <UserLink user={inspection?.executedAppointment?.assignedTo} /> + )) ?? ( + <Typography + data-testid={`user.value`} + component="i" + color="neutral" + level="title-md" + > + Keine Angaben + </Typography> ) } - showIfEmpty /> - <DetailsCell - name="date" - label="Geplanter Termin" - value={plannedDateAndTime} - /> - <DetailsCell - name="date" + <DetailsItem label="Geplanter Termin" value={plannedDateAndTime} /> + <DetailsItem label="Tatsächlicher Begehungstermin" - value={executedDateAndTime} - > - {!readOnly && ( - <IconButton - aria-label={`Termin ändern`} - color="primary" - onClick={() => setApprovalSidebarOpen(true)} - sx={{ p: 0, ml: 1, minHeight: 0 }} - > - <EditIcon /> - </IconButton> - )} - </DetailsCell> + value={ + <Grid container alignItems="center"> + <Typography + role={"paragraph"} + component={"p"} + data-testid={`date.value`} + level="title-md" + sx={{ + width: undefined, + textWrap: "pretty", + hyphens: "auto", + }} + > + {executedDateAndTime} + </Typography> + {!readOnly && ( + <IconButton + aria-label={`Termin ändern`} + color="primary" + onClick={() => setApprovalSidebarOpen(true)} + sx={{ p: 0, ml: 1, minHeight: 0 }} + > + <EditIcon /> + </IconButton> + )} + </Grid> + } + ></DetailsItem> {!readOnly && ( <> @@ -202,8 +219,7 @@ export function ExecutionSidePanel({ {inspection.phase === ApiInspectionPhase.Executed && ( <> <Divider sx={{ mb: 1, mt: 1 }} /> - <DetailsCell - name="phase-executed" + <DetailsItem label="Begehung abgeschlossen" value="Die Begehung ist abgeschlossen. Sie sind noch offline. Die eingegebenen Daten wurden gespeichert und werden übertragen, sobald Sie wieder online sind." /> 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 3b3769064..b39e07fde 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 @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionPhase } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPhase } from "@eshg/inspection-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { 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 16955318d..3f05d65e6 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 @@ -10,7 +10,7 @@ import { ApiInspection, ApiInspectionPhase, ApiUpdateInspectionRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { AutorenewOutlined, Checklist as ChecklistIcon, diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/Checklist.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/Checklist.tsx index 1bf4a1b69..9395c9ab9 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/Checklist.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/Checklist.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiChecklist } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklist } from "@eshg/inspection-api"; import { Box, Stack, Typography } from "@mui/joy"; import { Fragment } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistSection.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistSection.tsx index 6bfe8c95a..68f49daa9 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistSection.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistSection.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCLSection } from "@eshg/employee-portal-api/inspection"; +import { ApiCLSection } from "@eshg/inspection-api"; import { Divider, Stack, Typography } from "@mui/joy"; import { Fragment } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistValidateContext.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistValidateContext.tsx index 819423bf1..897b2492f 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistValidateContext.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/ChecklistValidateContext.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiChecklist } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklist } from "@eshg/inspection-api"; import { FormikErrors } from "formik"; import { ReactNode, 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 839376ab7..b783f63fc 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 @@ -5,7 +5,7 @@ "use client"; -import { ApiFileType } from "@eshg/employee-portal-api/inspection"; +import { ApiFileType } from "@eshg/inspection-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; import { FileLike } from "@eshg/lib-portal/components/formFields/file/validators"; 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 67e9c6bf3..1e53a4255 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 @@ -8,7 +8,7 @@ import { ApiUpdateChecklistElementsInner, ApiUpdateChecklistRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { Stack } from "@mui/joy"; import { useQueryClient } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/helpers.ts b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/helpers.ts index bb1e09632..1b9943820 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/helpers.ts +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/checklist/form/helpers.ts @@ -10,7 +10,7 @@ import { ApiCLSectionElementsInner, ApiChecklist, ApiUpdateChecklistElementsInner, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { isDefined } from "remeda"; export type FormCheckboxField = Omit<ApiCLCheckboxField, "checked"> & { diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentSidebar.tsx index c3cdf803d..437a2d146 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentSidebar.tsx @@ -6,9 +6,9 @@ import { ApiCreateInspectionIncidentRequest, ApiInspectionIncident, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; -import { Grid } from "@mui/joy"; +import { Grid, Typography } from "@mui/joy"; import { Formik } from "formik"; import { isEmpty, isNonNullish, isNullish } from "remeda"; import { v4 as uuidv4 } from "uuid"; @@ -18,7 +18,7 @@ import { useUpdateIncident, } from "@/lib/businessModules/inspection/api/mutations/incidents"; import { OverlayBoundary } from "@/lib/shared/components/boundaries/OverlayBoundary"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; @@ -99,11 +99,20 @@ function IncidentSidebarInner({ /> )} {isChecklistIncident && ( - <DetailsCell - name="title" + <DetailsItem label="Titel" - value={incident.title} - showIfEmpty + value={ + incident.title ?? ( + <Typography + data-testid={`title.value`} + component="i" + color="neutral" + level="title-md" + > + Keine Angaben + </Typography> + ) + } /> )} </Grid> diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsPanel.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsPanel.tsx index b7d63b628..9d02ac369 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsPanel.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsPanel.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionIncident } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionIncident } from "@eshg/inspection-api"; import { Box, Stack, Typography } from "@mui/joy"; import { useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsTable.tsx index e70225152..3af7e299b 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/execution/incident/IncidentsTable.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionIncident } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionIncident } from "@eshg/inspection-api"; import { DeleteOutlined, Edit } from "@mui/icons-material"; import { ColorPaletteProp } from "@mui/joy"; import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/history/InspectionHistoryTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/history/InspectionHistoryTable.tsx index 991f050c8..cb54efeab 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/history/InspectionHistoryTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/history/InspectionHistoryTable.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspPendingFacility } from "@eshg/employee-portal-api/inspection"; +import { ApiInspPendingFacility } from "@eshg/inspection-api"; import { Row } from "@tanstack/react-table"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/history/columns.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/history/columns.tsx index e72becc7b..f0f2ed1c4 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/history/columns.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/history/columns.tsx @@ -6,7 +6,7 @@ import { type ApiInspPendingFacility, ApiProcedureStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { OpenInNewOutlined } from "@mui/icons-material"; import { Chip, IconButton, Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx index f3ec84a73..bc08efcaa 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AddInspectionTiles.tsx @@ -6,10 +6,7 @@ "use client"; import { ApiUser } from "@eshg/base-api"; -import { - ApiInspection, - ApiObjectType, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiObjectType } from "@eshg/inspection-api"; import { Grid } from "@mui/joy"; import { useUpdateInspectionFacility } from "@/lib/businessModules/inspection/api/mutations/facility"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx index 15c964d8e..d92314ef8 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/new/AdditionalInfoTile.tsx @@ -11,7 +11,7 @@ import { ApiInspection, ApiInspectionType, ApiObjectType, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; 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 2760ad791..73659201d 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 @@ -7,7 +7,7 @@ import { ApiInspection, ApiInspectionAvailableCLDVersionsResponse, ApiInspectionPhase, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useWindowDimensions } from "@eshg/lib-portal/hooks/useWindowDimension"; import { Box, useTheme } from "@mui/joy"; import { useSuspenseQueries } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementSidebar.tsx index 11d021446..c3b20d134 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementSidebar.tsx @@ -6,7 +6,7 @@ import { ApiInspectionAnnouncement, ApiInspectionAnnouncementType, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { toDateString, toUtcDate } from "@eshg/lib-portal/helpers/dateTime"; import { Grid } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementTile.tsx index 3c30660f7..38081f1ae 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementTile.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionAnnouncement } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionAnnouncement } from "@eshg/inspection-api"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { Typography } from "@mui/joy"; import { useState } from "react"; @@ -13,7 +13,7 @@ import { isNonNullish, isNullish } from "remeda"; import { AnnouncementSidebar } from "@/lib/businessModules/inspection/components/inspection/planning/announcement/AnnouncementSidebar"; import { translateInspectionAnnouncement } from "@/lib/businessModules/inspection/shared/enums"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { InfoTileAddButton } from "@/lib/shared/components/infoTile/InfoTileAddButton"; @@ -70,9 +70,8 @@ export function AnnouncementTile({ </> } > - <DetailsCell name="date" label="Datum" value={date} /> - <DetailsCell - name="type" + <DetailsItem label="Datum" value={date} /> + <DetailsItem label="Kommunikationsmittel" value={ isNonNullish(announcement?.type) diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/appointment/AppointmentTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/appointment/AppointmentTile.tsx index 16d827bde..35bcee946 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/appointment/AppointmentTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/appointment/AppointmentTile.tsx @@ -5,17 +5,14 @@ "use client"; -import { - ApiInspection, - ApiInspectionAppointment, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiInspectionAppointment } from "@eshg/inspection-api"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { useState } from "react"; import { isNonNullish, isNullish } from "remeda"; import { AppointmentSidebar } from "@/lib/businessModules/inspection/components/inspection/common/appointment/AppointmentSidebar"; import { getFormattedAppointmentParts } from "@/lib/businessModules/inspection/components/inspection/common/appointment/appointmentUtils"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { InfoTileAddButton } from "@/lib/shared/components/infoTile/InfoTileAddButton"; @@ -79,8 +76,8 @@ export function AppointmentTile({ message="Termin muss ausgewählt sein, um eine Begehung durchzuführen." /> )} - <DetailsCell name="date" label="Datum" value={date} /> - <DetailsCell name="timeFrame" label="Zeitraum" value={fromTo} /> + <DetailsItem label="Datum" value={date} /> + <DetailsItem label="Zeitraum" value={fromTo} /> </InfoTile> ); } diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar.tsx index 0808570df..f3f4dfd3e 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionCLDVersion } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionCLDVersion } from "@eshg/inspection-api"; import { InfoOutlined } from "@mui/icons-material"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebarForm.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebarForm.tsx index 4c593daf8..b1b4cf332 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebarForm.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistSelectSidebarForm.tsx @@ -6,7 +6,7 @@ import { ApiInspectionAvailableCLDVersionsResponse, ApiInspectionCLDVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx index 8308f63c0..9fdd0adfe 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/checklist/ChecklistTile.tsx @@ -10,7 +10,7 @@ import { ApiInspection, ApiInspectionAvailableCLDVersionsResponse, ApiInspectionCLDVersion, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { DeleteOutlined } from "@mui/icons-material"; import { Chip, IconButton, Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventorySidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventorySidebar.tsx index 4724fa9a2..9db991340 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventorySidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventorySidebar.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiUpdateInspectionModifyInventoryRequest } from "@eshg/employee-portal-api/inspection"; +import { ApiUpdateInspectionModifyInventoryRequest } from "@eshg/inspection-api"; import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { validateRange } from "@eshg/lib-portal/helpers/validators"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTable.tsx index 5edc302c7..4bbf6c292 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTable.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionInventory } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionInventory } from "@eshg/inspection-api"; import { DeleteOutlined } from "@mui/icons-material"; import { Chip, IconButton } from "@mui/joy"; import { createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTile.tsx index 0d38cf356..77f8f6c5d 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/inventory/InventoryTile.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiInspectionInventory } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionInventory } from "@eshg/inspection-api"; import { useState } from "react"; import { InventorySidebar } from "@/lib/businessModules/inspection/components/inspection/planning/inventory/InventorySidebar"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/Packlist.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/Packlist.tsx index 77f47f459..3adf48911 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/Packlist.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/Packlist.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiPacklist } from "@eshg/employee-portal-api/inspection"; +import { ApiPacklist } from "@eshg/inspection-api"; import { DeleteOutlined } from "@mui/icons-material"; import { Accordion, diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebar.tsx index 101863040..df7b080e2 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionPLDRevision } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionPLDRevision } from "@eshg/inspection-api"; import { InfoOutlined } from "@mui/icons-material"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebarForm.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebarForm.tsx index ad8f5550f..c29740062 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebarForm.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistSelectSidebarForm.tsx @@ -6,7 +6,7 @@ import { ApiInspectionAvailablePLDRevisionsResponse, ApiInspectionPLDRevision, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistTile.tsx index 8f9a669aa..414f3f3dc 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/packlist/PacklistTile.tsx @@ -5,10 +5,7 @@ "use client"; -import { - ApiInspection, - ApiPacklist, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiPacklist } from "@eshg/inspection-api"; import { Stack, Textarea, Typography } from "@mui/joy"; import { useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceSidebar.tsx index 5ca0520e1..0f33e242a 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceSidebar.tsx @@ -10,7 +10,7 @@ import { ApiResourceType, ApiResourceTypeFromJSON, } from "@eshg/base-api"; -import type { ApiInspectionTravelTime } from "@eshg/employee-portal-api/inspection"; +import type { ApiInspectionTravelTime } from "@eshg/inspection-api"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceTile.tsx index 07aa0815e..18a62b36f 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourceTile.tsx @@ -9,7 +9,7 @@ import type { ApiInspectionAppointment, ApiInspectionResource, ApiInspectionTravelTime, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useState } from "react"; import { ResourceSidebar } from "@/lib/businessModules/inspection/components/inspection/planning/resource/ResourceSidebar"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourcesTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourcesTable.tsx index 4495f3440..f069b0782 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourcesTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/resource/ResourcesTable.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { ApiInspectionResource } from "@eshg/employee-portal-api/inspection"; -import { ApiResourceType } from "@eshg/employee-portal-api/inspection"; +import type { ApiInspectionResource } from "@eshg/inspection-api"; +import { ApiResourceType } from "@eshg/inspection-api"; import { CameraAltOutlined, DeleteOutlined, diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeSidebar.tsx index 9a2fcaead..1018a25cd 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeSidebar.tsx @@ -7,7 +7,7 @@ import type { ApiInspectionAppointment, ApiInspectionTravelTime, ApiObjectType, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; import { toDateTimeString } from "@eshg/lib-portal/helpers/dateTime"; import { isEmptyString } from "@eshg/lib-portal/helpers/guards"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeTile.tsx index 455a75188..3ca648d4c 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeTile.tsx @@ -10,7 +10,7 @@ import type { ApiDomesticAddress, ApiInspection, ApiInspectionTravelTime, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; @@ -24,7 +24,7 @@ import { useInspectionGeoApi } from "@/lib/businessModules/inspection/api/client import { useGetDepartment } from "@/lib/businessModules/inspection/api/queries/department"; import { getReverseGeoCode } from "@/lib/businessModules/inspection/api/queries/geo"; import { TravelTimeSidebar } from "@/lib/businessModules/inspection/components/inspection/planning/traveltime/TravelTimeSidebar"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { useCopy } from "@/lib/shared/hooks/useCopy"; import { useIsOffline } from "@/lib/shared/hooks/useIsOffline"; @@ -169,38 +169,22 @@ export function TravelTimeTile({ <Grid container columnSpacing={2} rowSpacing={3}> {showTravelStart && ( <Grid xs={6}> - <DetailsCell - name="startBuffer" - label="Anfahrtszeit in Minuten" - value={startBuffer} - /> + <DetailsItem label="Anfahrtszeit in Minuten" value={startBuffer} /> </Grid> )} {showTravelStart && ( <Grid xs={6}> - <DetailsCell - name="startTime" - label="Zeitpunkt der Abfahrt" - value={startTime} - /> + <DetailsItem label="Zeitpunkt der Abfahrt" value={startTime} /> </Grid> )} {showTravelEnd && ( <Grid xs={6}> - <DetailsCell - name="endBuffer" - label="Rückfahrzeit in Minuten" - value={endBuffer} - /> + <DetailsItem label="Rückfahrzeit in Minuten" value={endBuffer} /> </Grid> )} {showTravelEnd && ( <Grid xs={6}> - <DetailsCell - name="endTime" - label="Zeitpunkt der Rückkehr" - value={endTime} - /> + <DetailsItem label="Zeitpunkt der Rückkehr" value={endTime} /> </Grid> )} </Grid> diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidePanel.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidePanel.tsx index e375d10b8..9203416e3 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidePanel.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidePanel.tsx @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiInspection, - ApiInspectionPhase, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiInspectionPhase } from "@eshg/inspection-api"; import { InternalLink } from "@eshg/lib-portal/components/navigation/InternalLink"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import ChevronRight from "@mui/icons-material/ChevronRight"; @@ -22,7 +19,7 @@ import { translateInspectionResult, } from "@/lib/businessModules/inspection/shared/enums"; import { routes } from "@/lib/businessModules/inspection/shared/routes"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { useIsOffline } from "@/lib/shared/hooks/useIsOffline"; @@ -59,28 +56,24 @@ export function InspectionResultSidePanel({ > {hasResult && ( <> - <DetailsCell - name="resultName" + <DetailsItem label="Ergebnis" value={translateInspectionResult(inspection.result)} /> {followupInfo?.followupType && ( <> - <DetailsCell - name="followupType" + <DetailsItem label="Folgebegehung" value={translateFollowupType(followupInfo.followupType)} /> {followupInfo.followupDate && ( - <DetailsCell - name="followupDate" + <DetailsItem label="Datum der Nachprüfung" value={formatDate(followupInfo.followupDate)} /> )} {followupInfo.followupId && ( - <DetailsCell - name="followupId" + <DetailsItem label="" value={ <InternalLink diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidebar.tsx index 7312e0a33..b86a679ff 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/InspectionResultSidebar.tsx @@ -7,7 +7,7 @@ import { ApiFollowupType, ApiInspectionFollowupInfo, ApiInspectionResult, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/ReportApprovalButtons.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/ReportApprovalButtons.tsx index 75389c9dc..c0f88c5b5 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/ReportApprovalButtons.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/ReportApprovalButtons.tsx @@ -5,10 +5,7 @@ "use client"; -import { - ApiInspection, - ApiInspectionPhase, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiInspectionPhase } from "@eshg/inspection-api"; import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { CheckOutlined, EditRoadOutlined } from "@mui/icons-material"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx index e3124eb2a..ea069b652 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/editor/InspectionReportEditor.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiEditorBodyElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiEditorBodyElementsInner } from "@eshg/inspection-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { v4 as uuidv4 } from "uuid"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/reportutils.ts b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/reportutils.ts index 7a5bfc3e5..f414e1501 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/reportutils.ts +++ b/employee-portal/src/lib/businessModules/inspection/components/inspection/reportresult/reportutils.ts @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiInspection, - ApiInspectionResult, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspection, ApiInspectionResult } from "@eshg/inspection-api"; import { isNonNullish } from "remeda"; export function inspectionHasResult(inspection: ApiInspection) { diff --git a/employee-portal/src/lib/businessModules/inspection/components/objectType/EditObjectTypeSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/objectType/EditObjectTypeSidebar.tsx index 37e213cc9..733f51def 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/objectType/EditObjectTypeSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/objectType/EditObjectTypeSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiObjectType } from "@eshg/employee-portal-api/inspection"; +import { ApiObjectType } from "@eshg/inspection-api"; import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; import { validateIntegerAnd, diff --git a/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx index 9f557c77d..a78e3d160 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/objectType/ObjectTypesTable.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiObjectType } from "@eshg/employee-portal-api/inspection"; +import { ApiObjectType } from "@eshg/inspection-api"; import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; import { useGetObjectTypes } from "@/lib/businessModules/inspection/api/queries/objectTypes"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx index 079f6319a..bf30a7255 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/CreateOrEditPacklistDefinitionSidebar.tsx @@ -8,7 +8,7 @@ import { ApiObjectType, ApiPacklistDefinitionRevision, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Stack } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/PacklistDefinitionOverviewTable.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/PacklistDefinitionOverviewTable.tsx index 4bea82918..ff14feb44 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/PacklistDefinitionOverviewTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/PacklistDefinitionOverviewTable.tsx @@ -8,7 +8,7 @@ import { ApiPacklistDefinition, ApiPacklistDefinitionRevision, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; import { Add, Edit, History } from "@mui/icons-material"; import { Button, Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx index 84ef86e01..42d400e62 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/elements/PacklistDefinitionElement.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiPacklistDefinitionElement } from "@eshg/employee-portal-api/inspection"; +import { ApiPacklistDefinitionElement } from "@eshg/inspection-api"; import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd"; import { DeleteOutlined, DragIndicatorOutlined } from "@mui/icons-material"; import { Box, IconButton, Input, Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx index e8c1fab84..4fe5a33e1 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderCard.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiObjectType } from "@eshg/employee-portal-api/inspection"; +import { ApiObjectType } from "@eshg/inspection-api"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderRow.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderRow.tsx index ee8f863e3..4c1609a97 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderRow.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/header/PacklistDefinitionHeaderRow.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUser } from "@eshg/employee-portal-api/inspection"; +import { ApiUser } from "@eshg/inspection-api"; import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; import AddIcon from "@mui/icons-material/Add"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/sidebars/PacklistDefinitionRevisionTile.tsx b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/sidebars/PacklistDefinitionRevisionTile.tsx index 17a746b08..052f37eb3 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/sidebars/PacklistDefinitionRevisionTile.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/packlistDefinition/sidebars/PacklistDefinitionRevisionTile.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUser } from "@eshg/base-api"; -import { ApiPacklistDefinitionRevision } from "@eshg/employee-portal-api/inspection"; +import { ApiPacklistDefinitionRevision } from "@eshg/inspection-api"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { FactCheckOutlined } from "@mui/icons-material"; import CropFree from "@mui/icons-material/CropFree"; 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 066e1e052..01f9cfc5c 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/repository/ChecklistDefinitionRepoOverviewTable.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/inspection-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { diff --git a/employee-portal/src/lib/businessModules/inspection/components/repository/MetadataDetailsSidebar.tsx b/employee-portal/src/lib/businessModules/inspection/components/repository/MetadataDetailsSidebar.tsx index 743d7ba34..581cd6ee3 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/repository/MetadataDetailsSidebar.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/repository/MetadataDetailsSidebar.tsx @@ -3,12 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/inspection-api"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { Grid } from "@mui/joy"; import { isNonNullish } from "remeda"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { DrawerProps } from "@/lib/shared/components/drawer/drawerContext"; import { useSidebar } from "@/lib/shared/components/drawer/useSidebar"; import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; @@ -33,26 +33,10 @@ export function MetadataDetailsSidebar({ return ( <SidebarContent title="Details"> <Grid container direction="column" gap={2}> - <DetailsCell - name={"description"} - label={"Beschreibung"} - value={metadata?.description} - /> - <DetailsCell - name={"changeLog"} - label={"Änderungshinweis"} - value={metadata?.changeLog} - /> - <DetailsCell - name={"contact"} - label={"Kontakt"} - value={metadata?.contact} - /> - <DetailsCell - name={"createdAt"} - label={"Erstellt am"} - value={createdAt} - /> + <DetailsItem label={"Beschreibung"} value={metadata?.description} /> + <DetailsItem label={"Änderungshinweis"} value={metadata?.changeLog} /> + <DetailsItem label={"Kontakt"} value={metadata?.contact} /> + <DetailsItem label={"Erstellt am"} value={createdAt} /> </Grid> </SidebarContent> ); diff --git a/employee-portal/src/lib/businessModules/inspection/components/repository/RepoCLDInfoCard.tsx b/employee-portal/src/lib/businessModules/inspection/components/repository/RepoCLDInfoCard.tsx index bf71c0a4e..f45189c30 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/repository/RepoCLDInfoCard.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/repository/RepoCLDInfoCard.tsx @@ -4,7 +4,7 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { ApiChecklistDefinitionVersion } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionVersion } from "@eshg/inspection-api"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; diff --git a/employee-portal/src/lib/businessModules/inspection/components/repository/overviewTableColumns.tsx b/employee-portal/src/lib/businessModules/inspection/components/repository/overviewTableColumns.tsx index 4f39f0df0..ea33a7429 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/repository/overviewTableColumns.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/repository/overviewTableColumns.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/employee-portal-api/inspection"; +import { ApiChecklistDefinitionCentralRepoMetadata } from "@eshg/inspection-api"; import { Close, DeleteOutlined, 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 ea5af95b2..2f5665db8 100644 --- a/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx +++ b/employee-portal/src/lib/businessModules/inspection/components/textBlock/TextBlocksTable.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiTextBlock } from "@eshg/employee-portal-api/inspection"; +import { ApiTextBlock } from "@eshg/inspection-api"; import { Add, DeleteOutlined, Edit } from "@mui/icons-material"; import { Button } from "@mui/joy"; import { createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/inspection/shared/constants.ts b/employee-portal/src/lib/businessModules/inspection/shared/constants.ts index 5bfbe80e1..da80b282f 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/constants.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/constants.ts @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiProcedureType, - ApiTaskType, -} from "@eshg/employee-portal-api/inspection"; +import { ApiProcedureType, ApiTaskType } from "@eshg/inspection-api"; export const procedureTypes = [ApiProcedureType.Inspection]; diff --git a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts index 5471ef5f7..0ffca3299 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/enums.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/enums.ts @@ -11,7 +11,7 @@ import { ApiInspectionResult, ApiInspectionType, ApiWebSearchEntryStatus, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { ChipProps } from "@mui/joy"; export const webSearchStatusNames = { diff --git a/employee-portal/src/lib/businessModules/inspection/shared/isUnknownUser.ts b/employee-portal/src/lib/businessModules/inspection/shared/isUnknownUser.ts index bd6b7531c..631be7e6d 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/isUnknownUser.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/isUnknownUser.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUser } from "@eshg/employee-portal-api/inspection"; +import { ApiUser } from "@eshg/inspection-api"; export function isUnknownUser(user: ApiUser) { return ( diff --git a/employee-portal/src/lib/businessModules/inspection/shared/offline/useIsOfflineFeatureEnabled.ts b/employee-portal/src/lib/businessModules/inspection/shared/offline/useIsOfflineFeatureEnabled.ts index 7252bd8d6..0c1704be9 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/offline/useIsOfflineFeatureEnabled.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/useIsOfflineFeatureEnabled.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspectionFeature } from "@eshg/employee-portal-api/inspection"; +import { ApiInspectionFeature } from "@eshg/inspection-api"; import { useIsNewFeatureEnabledUnsuspended } from "@/lib/businessModules/inspection/api/queries/feature"; 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 640d92f69..95b41e0a0 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/offline/usePrecacheInspections.ts @@ -23,7 +23,7 @@ import { PacklistApi, ProcedureApi, ProgressEntryApi, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { queryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; import { QueryClient, useQueryClient } from "@tanstack/react-query"; import { useCallback } from "react"; diff --git a/employee-portal/src/lib/businessModules/inspection/shared/types.ts b/employee-portal/src/lib/businessModules/inspection/shared/types.ts index a87805ded..e5b792553 100644 --- a/employee-portal/src/lib/businessModules/inspection/shared/types.ts +++ b/employee-portal/src/lib/businessModules/inspection/shared/types.ts @@ -6,7 +6,7 @@ import { GetPendingFacilitiesRequest, SearchRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; export type FacilityWebSearchFilters = Partial<Omit<SearchRequest, "sort">> & { sortField?: string; diff --git a/employee-portal/src/lib/businessModules/measlesProtection/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/measlesProtection/api/queries/archiving.ts index bfa5b4387..a4fff1401 100644 --- a/employee-portal/src/lib/businessModules/measlesProtection/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/measlesProtection/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/measlesProtection/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/measlesProtection/api/queries/apiQueryKeys"; 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 bcee4566d..ca193d2be 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 @@ -6,9 +6,9 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; import { Row } from "@eshg/lib-portal/components/Row"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { ApiGetProcedure200Response } from "@eshg/measles-protection-api"; import { EditOutlined, Preview, ToggleOffOutlined } from "@mui/icons-material"; import { Chip } from "@mui/joy"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts index 306b535b1..90b9518dd 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/clients.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; import { ApprovalRequestApi, ArchivingApi, @@ -12,8 +13,7 @@ import { MedicalRegistryImportApi, ProcedureApi, ProgressEntryApi, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/medical-registry-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/import.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/import.ts index 85c271848..5cf5df8ad 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/import.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/import.ts @@ -7,7 +7,7 @@ import { ApiResponse } from "@eshg/base-api"; import { ApiImportStatistics, ApiImportStatisticsFromJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { useMutation } from "@tanstack/react-query"; import { useCallback, useRef } from "react"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts index 80b85e3f2..80563be59 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ConfirmProcedureRequest, - CreateProcedureRequest, -} from "@eshg/employee-portal-api/medicalRegistry"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { + ConfirmProcedureRequest, + CreateProcedureRequest, +} from "@eshg/medical-registry-api"; import { useRouter } from "next/navigation"; import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/archiving.ts index d53dc5431..1309eec95 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/medicalRegistry/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/medicalRegistry/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/draft.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/draft.ts index eee8189f2..d095f68ee 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/draft.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/draft.ts @@ -10,7 +10,7 @@ import { ApiMedicalRegistryEntrySearchResult, ApiMedicalRegistryEntrySearchResultFromJSON, ApiProcedureStatus, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; import { useQuery } from "@tanstack/react-query"; import { isDefined, mapValues } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts index 221182ecf..54b08c5b4 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries.ts @@ -5,11 +5,11 @@ "use client"; +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { GetProcedureOverviewRequest, MedicalRegistryApi, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/medical-registry-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/useGetMedicalProceduresTablePage.ts b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/useGetMedicalProceduresTablePage.ts new file mode 100644 index 000000000..29d634e18 --- /dev/null +++ b/employee-portal/src/lib/businessModules/medicalRegistry/api/queries/useGetMedicalProceduresTablePage.ts @@ -0,0 +1,48 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; +import { useQueries } from "@tanstack/react-query"; + +import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients"; +import { + getMedicalRegistryOverviewQuery, + getMedicalRegistrySearchQuery, +} from "@/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries"; +import { getMedicalRegistryEntryFilters } from "@/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings"; +import { useGetGdprValidationBannerQuery } from "@/lib/shared/api/queries/gdpr"; +import { FilterValue } from "@/lib/shared/components/filterSettings/models/FilterValue"; + +export function useGetMedicalProceduresTablePage( + isSearchPanel: boolean, + pageSize: number, + pageNumber: number, + activeValues: FilterValue[], + searchQuery: string, +) { + const medicalRegistryApi = useMedicalRegistryApi(); + + const gdprBannerQuery = useGetGdprValidationBannerQuery( + ApiBusinessModule.MedicalRegistry, + ); + + const proceduresQuery = isSearchPanel + ? getMedicalRegistrySearchQuery(medicalRegistryApi, searchQuery) + : getMedicalRegistryOverviewQuery(medicalRegistryApi, { + ...getMedicalRegistryEntryFilters(activeValues), + pageSize: pageSize, + pageNumber: pageNumber, + }); + + const [{ data: medicalHistoryData, isLoading }, gdprBanner] = useQueries({ + queries: [proceduresQuery, gdprBannerQuery], + }); + + return { + medicalHistoryData, + isLoading, + gdprBanner, + }; +} diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/MedicalRegistryProcedureChip.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/MedicalRegistryProcedureChip.tsx index f0f165a9f..07bb347b1 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/MedicalRegistryProcedureChip.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/MedicalRegistryProcedureChip.tsx @@ -8,7 +8,7 @@ import { ApiProcedureStatus, ApiProcedureType, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; import { Chip, ChipProps } from "@mui/joy"; import { diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/OccupationalInformationForm.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/OccupationalInformationForm.tsx index 13db5a6fc..44002de33 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/OccupationalInformationForm.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/OccupationalInformationForm.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiTypeOfChange } from "@eshg/employee-portal-api/medicalRegistry"; import { professionalTitleNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; import { MedicalRegistryCreateProcedureFormValues, @@ -22,6 +21,7 @@ import { validatePastOrTodayDate, } from "@eshg/lib-portal/helpers/validators"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Grid, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/PersonalInformationForm.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/PersonalInformationForm.tsx index 0efc847e4..46d11d347 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/PersonalInformationForm.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/PersonalInformationForm.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiTypeOfChange } from "@eshg/employee-portal-api/medicalRegistry"; import { MedicalRegistryCreateProcedureFormValues, PersonalInformationFormValues, @@ -16,10 +15,13 @@ import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField" import { GENDER_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { + validateDateOfBirth, validateLength, validatePastOrTodayDate, + validatePipe, } from "@eshg/lib-portal/helpers/validators"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Grid, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; @@ -162,7 +164,7 @@ export function PersonalInformationForm(props: NestedFormProps) { name={fieldName("birthDate")} label={"Geburtsdatum"} required={requiredFieldMessage} - validate={validatePastOrTodayDate} + validate={validatePipe(validatePastOrTodayDate, validateDateOfBirth)} /> </Grid> <Grid xxs={6} xxl={4}> diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/ProfessionalismInformationForm.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/ProfessionalismInformationForm.tsx index eb7152bf1..e17e3a102 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/ProfessionalismInformationForm.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/ProfessionalismInformationForm.tsx @@ -3,10 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiEmploymentStatus, - ApiEmploymentType, -} from "@eshg/employee-portal-api/medicalRegistry"; import { employmentStatusNames, employmentTypeNames, @@ -15,6 +11,10 @@ import { ProfessionalismInformationFormValues } from "@eshg/lib-portal/businessM import { RadioGroupField } from "@eshg/lib-portal/components/formFields/RadioGroupField"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; +import { + ApiEmploymentStatus, + ApiEmploymentType, +} from "@eshg/medical-registry-api"; import { Grid, Radio, Typography } from "@mui/joy"; import { requiredFieldMessage } from "@/lib/businessModules/medicalRegistry/components/procedures/create/MedicalRegistryCreateProcedureForm"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/RequiredDocumentsForm.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/RequiredDocumentsForm.tsx index fbbeccc84..48cdb4c24 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/RequiredDocumentsForm.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/create/RequiredDocumentsForm.tsx @@ -4,7 +4,6 @@ */ import { ApiCountryCode } from "@eshg/base-api"; -import { ApiTypeOfChange } from "@eshg/employee-portal-api/medicalRegistry"; import { MedicalRegistryCreateProcedureFormValues, RequiredDocumentsFormValues, @@ -13,6 +12,7 @@ import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { validateFile } from "@eshg/lib-portal/helpers/validators"; import { NestedFormProps } from "@eshg/lib-portal/types/form"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; import { Add, DeleteOutlined } from "@mui/icons-material"; import { Button, Grid, IconButton, Stack, Typography } from "@mui/joy"; import { FieldArray, useFormikContext } from "formik"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/MedicalRegistryProcedureDetails.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/MedicalRegistryProcedureDetails.tsx index a19a4e3bb..42951a4d6 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/MedicalRegistryProcedureDetails.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/MedicalRegistryProcedureDetails.tsx @@ -6,12 +6,12 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; +import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext"; import { ApiGetProcedure200Response, ApiGetProcedureDraftResponse, ApiMedicalRegistryEntryProcedureType, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext"; +} from "@eshg/medical-registry-api"; import { Button, Grid, Stack } from "@mui/joy"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/PracticesDetailsSection.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/PracticesDetailsSection.tsx index 149a9fdf1..70e122e27 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/PracticesDetailsSection.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/PracticesDetailsSection.tsx @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; import { ApiGetProcedure200Response, ApiPractice, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; +} from "@eshg/medical-registry-api"; import { Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/ProfessionalDetailsSection.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/ProfessionalDetailsSection.tsx index ba99ab39f..5fa072264 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/ProfessionalDetailsSection.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/ProfessionalDetailsSection.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedure200Response } from "@eshg/employee-portal-api/medicalRegistry"; import { employmentStatusNames, employmentTypeNames, @@ -12,6 +11,7 @@ import { import { PERSON_FIELD_NAME } from "@eshg/lib-portal/components/formFields/constants"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; +import { ApiGetProcedure200Response } from "@eshg/medical-registry-api"; import { Stack } from "@mui/joy"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/TypeOfChangeSection.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/TypeOfChangeSection.tsx index 625f30c5a..181386054 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/TypeOfChangeSection.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/TypeOfChangeSection.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedureDraftResponse } from "@eshg/employee-portal-api/medicalRegistry"; import { changeTypeNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; +import { ApiGetProcedureDraftResponse } from "@eshg/medical-registry-api"; import { Typography } from "@mui/joy"; import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/WrittenConfirmationSection.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/WrittenConfirmationSection.tsx index 1c4ce4da6..09cd761f3 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/WrittenConfirmationSection.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/details/WrittenConfirmationSection.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedure200Response } from "@eshg/employee-portal-api/medicalRegistry"; +import { ApiGetProcedure200Response } from "@eshg/medical-registry-api"; import { SvgIcon, Typography } from "@mui/joy"; import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FacilitySidebarContent.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FacilitySidebarContent.tsx index a77e0facb..c258a4d3f 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FacilitySidebarContent.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FacilitySidebarContent.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedureDraftResponse } from "@eshg/employee-portal-api/medicalRegistry"; import { RadioGroupField } from "@eshg/lib-portal/components/formFields/RadioGroupField"; +import { ApiGetProcedureDraftResponse } from "@eshg/medical-registry-api"; import { Stack, Typography } from "@mui/joy"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FinalizeDraftSidebar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FinalizeDraftSidebar.tsx index edaf8eef9..3c621dc76 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FinalizeDraftSidebar.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/FinalizeDraftSidebar.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedureDraftResponse } from "@eshg/employee-portal-api/medicalRegistry"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; +import { ApiGetProcedureDraftResponse } from "@eshg/medical-registry-api"; import { Button } from "@mui/joy"; import { Formik, FormikHelpers } from "formik"; import { useMemo, useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PartialEntryErrorSidebar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PartialEntryErrorSidebar.tsx index 0d0a1fae9..9c96c5f5e 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PartialEntryErrorSidebar.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PartialEntryErrorSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedureDraftResponse } from "@eshg/employee-portal-api/medicalRegistry"; +import { ApiGetProcedureDraftResponse } from "@eshg/medical-registry-api"; import { Button, Card, Stack, Typography } from "@mui/joy"; import { useId } from "react"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PersonSidebarContent.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PersonSidebarContent.tsx index 34a13e22d..116cff840 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PersonSidebarContent.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/PersonSidebarContent.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiGetProcedureDraftResponse } from "@eshg/employee-portal-api/medicalRegistry"; import { RadioGroupField } from "@eshg/lib-portal/components/formFields/RadioGroupField"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiGetProcedureDraftResponse } from "@eshg/medical-registry-api"; import { Stack, Typography } from "@mui/joy"; import { SearchDraftReferencesResponse } from "@/lib/businessModules/medicalRegistry/api/queries/draft"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/ProcedureSidebarContent.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/ProcedureSidebarContent.tsx index 2da8d87bd..acd4a7fa0 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/ProcedureSidebarContent.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/ProcedureSidebarContent.tsx @@ -4,7 +4,6 @@ */ import { ApiGetReferencePersonResponse } from "@eshg/base-api"; -import { ApiMedicalRegistryEntrySearchResult } from "@eshg/employee-portal-api/medicalRegistry"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { RadioGroupField } from "@eshg/lib-portal/components/formFields/RadioGroupField"; import { @@ -13,6 +12,7 @@ import { } from "@eshg/lib-portal/formatters/dateTime"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; +import { ApiMedicalRegistryEntrySearchResult } from "@eshg/medical-registry-api"; import { Stack, Typography } from "@mui/joy"; import { diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/helper.ts b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/helper.ts index 5023dd0b1..12565e510 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/helper.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/helper.ts @@ -6,7 +6,7 @@ import { ApiGetProcedureDraftResponse, ApiTypeOfChange, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; export function mapToOptionalPhoneNumbers(phoneNumbers: string[]) { if (phoneNumbers.length === 0) { diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/useConfirmDraftDialog.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/useConfirmDraftDialog.tsx index e57e14368..c9a62bd94 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/useConfirmDraftDialog.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/finalize/useConfirmDraftDialog.tsx @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ConfirmationDialogOptions } from "@eshg/lib-portal/components/confirmationDialog/ConfirmationDialogProvider"; import { ApiGetProcedureDraftResponse, ApiPracticeReferenceFacility, ApiProcedureReference, ApiProfessionalReferencePerson, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { ConfirmationDialogOptions } from "@eshg/lib-portal/components/confirmationDialog/ConfirmationDialogProvider"; +} from "@eshg/medical-registry-api"; import { useConfirmDraft } from "@/lib/businessModules/medicalRegistry/api/mutations/medicalRegistryEntries"; import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataFormSidebar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataFormSidebar.tsx index 4e614d840..d778ccbc8 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataFormSidebar.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataFormSidebar.tsx @@ -30,38 +30,33 @@ export function ImportDataFormSidebar({ onClose }: ImportDataFormSidebarProps) { <SidebarContent title="Daten importieren"> <Stack gap={3}> <Alert - color="primary" - title="Hinweis" + color="danger" + title="Duplikate vermeiden" message={ <List marker="disc" + size="md" sx={{ "&, && > *": { color: "inherit" }, paddingInlineStart: "2ch", "--List-padding": 0, - "--ListItem-minHeight": "2rem", + "--ListItem-minHeight": "1.5rem", + "--ListItem-paddingX": 0, + "--ListItem-paddingY": 0, }} > + <ListItem>Laden Sie Datensätze nicht mehrfach hoch.</ListItem> <ListItem> - Der Import kann nicht rückgängig gemacht werden. - </ListItem> - <ListItem>Alle Pflichtfelder müssen ausgefüllt sein.</ListItem> - <ListItem>Daten-Duplikate sind zu vermeiden.</ListItem> - <ListItem> - Die Datei darf maximal 4000 Einträge enthalten. + Vermeiden Sie Duplikate von Bestandsdaten sowie innerhalb der + XLSX-Datei. </ListItem> </List> } /> - <Alert - color="danger" - title="Mehrfachupload vermeiden" - message="Datensätze dürfen nicht mehrfach hochgeladen werden." - /> <Stack gap={2}> <FileField name="importFile" - label="Wählen Sie eine XLSX-Datei aus" + label="XLSX-Datei auswählen (Max. 4000 Einträge)" required="Bitte eine XLSX-Datei auswählen." accept={FileType.Xlsx} /> diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataSummarySidebar.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataSummarySidebar.tsx index 9b306964d..d0c70c544 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataSummarySidebar.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/import/ImportDataSummarySidebar.tsx @@ -5,9 +5,9 @@ "use client"; -import { ApiImportStatistics } from "@eshg/employee-portal-api/medicalRegistry"; import { downloadFileAndOpen } from "@eshg/lib-portal/api/files/download"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { ApiImportStatistics } from "@eshg/medical-registry-api"; import { ErrorOutlineOutlined, FileDownloadOutlined, diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryEntryOverviewControls.tsx b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryEntryOverviewControls.tsx index dca26fca2..bf0f5df6b 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryEntryOverviewControls.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryEntryOverviewControls.tsx @@ -5,65 +5,94 @@ "use client"; +import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; +import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { NavigationLink } from "@eshg/lib-portal/components/navigation/NavigationLink"; import { Add } from "@mui/icons-material"; +import SearchIcon from "@mui/icons-material/Search"; import { Button } from "@mui/joy"; +import { Formik } from "formik"; import { MedicalRegistryImportButton } from "@/lib/businessModules/medicalRegistry/components/procedures/import/MedicalRegistryImportButton"; import { routes } from "@/lib/businessModules/medicalRegistry/shared/routes"; import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; import { FilterButton } from "@/lib/shared/components/buttons/FilterButton"; import { UseFilterSettings } from "@/lib/shared/components/filterSettings/useFilterSettings"; -import { SearchFilter } from "@/lib/shared/components/tableFilters/SearchFilter"; -import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl"; interface MedicalRegistryEntryOverviewControlProps { filterSettings: UseFilterSettings; toggleActivePanel: (panelName: "filters" | "entrySearch") => void; activePanel: "filters" | "entrySearch" | undefined; + onNameSearch: (name: string) => void; } export function MedicalRegistryEntryOverviewControls( props: MedicalRegistryEntryOverviewControlProps, ) { - const tableControl = useTableControl({ - serverSideSorting: true, - sortFieldName: "sortKey", - }); + const isEntrySearch = props.activePanel === "entrySearch"; return ( <ButtonBar left={ - <> - <FilterButton - {...props.filterSettings.filterButtonProps} - isFilterVisible={props.activePanel === "filters"} - onClick={() => props.toggleActivePanel("filters")} - activeFilters={ - props.activePanel !== "entrySearch" - ? props.filterSettings.filterButtonProps.activeFilters - : 0 - } - /> - <Button onClick={() => props.toggleActivePanel("entrySearch")}> - Suche - </Button> - {props.activePanel === "entrySearch" && ( - <SearchFilter - tableControl={tableControl} - searchParamName="name" - label="Suche" - /> - )} - </> + <Formik + initialValues={{ + name: "", + }} + onSubmit={({ name }) => props.onNameSearch(name)} + > + <FormPlus + sx={{ + display: "flex", + direction: "row", + gap: 2, + }} + > + {isEntrySearch && ( + <InputField + label={null} + placeholder="Suche" + aria-label="Suche" + name="name" + type="text" + startDecorator={<SearchIcon />} + /> + )} + {!isEntrySearch && ( + <FilterButton + {...props.filterSettings.filterButtonProps} + isFilterVisible={props.activePanel === "filters"} + onClick={() => props.toggleActivePanel("filters")} + activeFilters={ + props.filterSettings.filterButtonProps.activeFilters + } + /> + )} + {!isEntrySearch && ( + <Button onClick={() => props.toggleActivePanel("entrySearch")}> + Suche + </Button> + )} + {isEntrySearch && <Button type="submit">Suchen</Button>} + {isEntrySearch && ( + <Button + variant="outlined" + onClick={() => props.toggleActivePanel("entrySearch")} + > + Abbrechen + </Button> + )} + </FormPlus> + </Formik> } right={ - <> - <MedicalRegistryImportButton /> - <NavigationLink href={routes.procedures.create} passHref> - <Button startDecorator={<Add />}>Eintrag erstellen</Button> - </NavigationLink> - </> + !isEntrySearch && ( + <> + <MedicalRegistryImportButton /> + <NavigationLink href={routes.procedures.create} passHref> + <Button startDecorator={<Add />}>Eintrag erstellen</Button> + </NavigationLink> + </> + ) } /> ); 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 index d4ea118c1..7b8d2b2ae 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx +++ b/employee-portal/src/lib/businessModules/medicalRegistry/components/procedures/proceduresTable/MedicalRegistryProceduresTable.tsx @@ -5,32 +5,24 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; -import { - ApiApplicantAddress, - ApiMedicalRegistryEntry, -} from "@eshg/employee-portal-api/medicalRegistry"; import { professionalTitleNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; -import { useSuspenseQueries } from "@tanstack/react-query"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; +import { + ApiApplicantAddress, + ApiMedicalRegistryEntry, +} from "@eshg/medical-registry-api"; +import { Box } from "@mui/joy"; import { createColumnHelper } from "@tanstack/react-table"; -import { useSearchParams } from "next/navigation"; -import { useReducer } from "react"; +import { useEffect, useReducer, useState } from "react"; import { isDefined } from "remeda"; -import { useMedicalRegistryApi } from "@/lib/businessModules/medicalRegistry/api/clients"; -import { - getMedicalRegistryOverviewQuery, - getMedicalRegistrySearchQuery, -} from "@/lib/businessModules/medicalRegistry/api/queries/medicalRegistryEntries"; +import { useGetMedicalProceduresTablePage } from "@/lib/businessModules/medicalRegistry/api/queries/useGetMedicalProceduresTablePage"; import { MedicalRegistryProcedureChip } from "@/lib/businessModules/medicalRegistry/components/procedures/MedicalRegistryProcedureChip"; -import { - getMedicalRegistryEntryFilters, - useMedicalRegistryFilterSettings, -} from "@/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings"; +import { useMedicalRegistryFilterSettings } from "@/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings"; import { routes } from "@/lib/businessModules/medicalRegistry/shared/routes"; -import { useGetGdprValidationBannerQuery } from "@/lib/shared/api/queries/gdpr"; +import { NoSearchResults } from "@/lib/shared/components/NoSearchResult"; import { FilterSettings } from "@/lib/shared/components/filterSettings/FilterSettings"; import { FilterSettingsSheet } from "@/lib/shared/components/filterSettings/FilterSettingsSheet"; import { useGdprValidationTasksAlert } from "@/lib/shared/components/gdpr/useGdprValidationTasksAlert"; @@ -38,7 +30,7 @@ import { Pagination } from "@/lib/shared/components/pagination/Pagination"; import { DataTable } from "@/lib/shared/components/table/DataTable"; import { TablePage } from "@/lib/shared/components/table/TablePage"; import { TableSheet } from "@/lib/shared/components/table/TableSheet"; -import { useTableControl } from "@/lib/shared/hooks/searchParams/useTableControl"; +import { usePagination } from "@/lib/shared/hooks/table/usePagination"; import { MedicalRegistryEntryOverviewControls } from "./MedicalRegistryEntryOverviewControls"; @@ -150,49 +142,37 @@ function getProceduresColumns() { ]; } +type PanelName = "filters" | "entrySearch"; + +function reduceActivePanel( + state: PanelName | undefined, + newState: PanelName, +): PanelName | undefined { + return newState === state ? undefined : newState; +} + export function MedicalRegistryProceduresTable() { - const tableControl = useTableControl(); + const { resetPageNumber, page, pageSize, getPaginationProps } = + usePagination(); const filterSettings = useMedicalRegistryFilterSettings(); - + const [searchQuery, setSearchQuery] = useState<string>(""); const [activePanel, toggleActivePanel] = useReducer( reduceActivePanel, undefined, ); - type PanelName = "filters" | "entrySearch"; - - function reduceActivePanel( - state: PanelName | undefined, - newState: PanelName, - ): PanelName | undefined { - return newState === state ? undefined : newState; - } - - const searchParams = useSearchParams(); - const searchQuery = searchParams.get("name") ?? ""; - - const medicalRegistryApi = useMedicalRegistryApi(); - - const proceduresQuery = - activePanel === "entrySearch" - ? getMedicalRegistrySearchQuery(medicalRegistryApi, searchQuery) - : getMedicalRegistryOverviewQuery(medicalRegistryApi, { - ...getMedicalRegistryEntryFilters(filterSettings.activeValues), - pageSize: tableControl.paginationProps.pageSize, - pageNumber: tableControl.paginationProps.pageNumber, - }); - - const gdprBannerQuery = useGetGdprValidationBannerQuery( - ApiBusinessModule.MedicalRegistry, - ); + useEffect(() => { + resetPageNumber(); + }, [activePanel, searchQuery, filterSettings.activeValues, resetPageNumber]); - const [ - { - data: { medicalRegistryEntries, totalElements }, - isLoading, - }, - gdprBanner, - ] = useSuspenseQueries({ queries: [proceduresQuery, gdprBannerQuery] }); + const { medicalHistoryData, isLoading, gdprBanner } = + useGetMedicalProceduresTablePage( + activePanel === "entrySearch", + pageSize, + page, + filterSettings.activeValues, + searchQuery, + ); useGdprValidationTasksAlert({ banner: gdprBanner.data, @@ -202,11 +182,13 @@ export function MedicalRegistryProceduresTable() { return ( <TablePage aria-label="Vorgänge" + fullHeight controls={ <MedicalRegistryEntryOverviewControls filterSettings={filterSettings} activePanel={activePanel} toggleActivePanel={toggleActivePanel} + onNameSearch={setSearchQuery} /> } filterSettings={ @@ -220,22 +202,33 @@ export function MedicalRegistryProceduresTable() { <TableSheet loading={isLoading} footer={ - activePanel !== "entrySearch" && ( + activePanel !== "entrySearch" && + medicalHistoryData && ( <Pagination - totalCount={totalElements} - {...tableControl.paginationProps} + {...getPaginationProps({ + totalCount: medicalHistoryData.totalElements, + })} /> ) } > <DataTable - data={medicalRegistryEntries} + data={medicalHistoryData?.medicalRegistryEntries ?? []} columns={getProceduresColumns()} rowNavigation={{ route: ({ original: { id: procedureId } }) => routes.procedures.byId(procedureId).details, focusColumnAccessorKey: "lastName", }} + noDataComponent={ + isLoading + ? undefined + : () => ( + <Box flex={1} alignContent="center"> + <NoSearchResults info="Keine Vorgänge vorhanden" /> + </Box> + ) + } /> </TableSheet> </TablePage> diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/constants.ts b/employee-portal/src/lib/businessModules/medicalRegistry/shared/constants.ts index ff59a2f00..0a82276c0 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/shared/constants.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/constants.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiProcedureType } from "@eshg/employee-portal-api/medicalRegistry"; +import { ApiProcedureType } from "@eshg/medical-registry-api"; export const archivableProcedureTypes = [ApiProcedureType.MedicalRegistryEntry]; diff --git a/employee-portal/src/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings.ts b/employee-portal/src/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings.ts index 22a0e1628..cddc92105 100644 --- a/employee-portal/src/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings.ts +++ b/employee-portal/src/lib/businessModules/medicalRegistry/shared/hooks/useMedicalRegistryFilterSettings.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { professionalTitleNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; import { ApiProcedureStatus, ApiProcedureType, ApiProfessionalTitle, -} from "@eshg/employee-portal-api/medicalRegistry"; -import { GetProcedureOverviewRequest } from "@eshg/employee-portal-api/medicalRegistry/apis"; -import { professionalTitleNames } from "@eshg/lib-portal/businessModules/medicalRegistry/constants"; + GetProcedureOverviewRequest, +} from "@eshg/medical-registry-api"; import { EntryStatus, @@ -87,7 +87,6 @@ export function useMedicalRegistryFilterSettings(): UseFilterSettings { } else { localStorage.removeItem(initialProfessionalTitles); } - // active values are synced via SearchParamStateProvider }, showSearch: false, }); diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/clients.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/clients.ts index a13a2be43..1d822f745 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/clients.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/clients.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; import { AppointmentBlockApi, AppointmentTypeApi, @@ -12,10 +13,12 @@ import { EmployeeOmsProcedureApi, FileApi, OmsAppointmentApi, + OmsDocumentApi, + OmsFileApi, ProcedureApi, ProgressEntryApi, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; + WaitingRoomApi, +} from "@eshg/official-medical-service-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( @@ -63,3 +66,18 @@ export function useOmsAppointmentApi() { const configuration = useConfiguration(); return new OmsAppointmentApi(configuration); } + +export function useOmsDocumentApi() { + const configuration = useConfiguration(); + return new OmsDocumentApi(configuration); +} + +export function useOmsFileApi() { + const configuration = useConfiguration(); + return new OmsFileApi(configuration); +} + +export function useWaitingRoomApi() { + const configuration = useConfiguration(); + return new WaitingRoomApi(configuration); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentBlockGroup.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentBlockGroup.ts index 6b18e9e1b..57d26a2f6 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentBlockGroup.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentBlockGroup.ts @@ -3,16 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiGetAppointmentBlock, - ApiGetAppointmentBlockGroup, -} from "@eshg/employee-portal-api/officialMedicalService"; import { BaseEntity, mapBaseEntity, } from "@eshg/lib-employee-portal/api/models/BaseEntity"; import { assertNonEmptyArray } from "@eshg/lib-portal/helpers/assertions"; +import { + ApiAppointmentType, + ApiGetAppointmentBlock, + ApiGetAppointmentBlockGroup, +} from "@eshg/official-medical-service-api"; import { first, last, sumBy } from "remeda"; export interface AppointmentBlockGroup extends AppointmentBlock { diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentTypeConfig.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentTypeConfig.ts index ffc2e5390..2c846dcbf 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentTypeConfig.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/models/AppointmentTypeConfig.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiAppointmentTypeConfig, -} from "@eshg/employee-portal-api/officialMedicalService"; import { BaseEntity, mapBaseEntity, } from "@eshg/lib-employee-portal/api/models/BaseEntity"; +import { + ApiAppointmentType, + ApiAppointmentTypeConfig, +} from "@eshg/official-medical-service-api"; export interface AppointmentTypeConfig extends BaseEntity { appointmentTypeDto: ApiAppointmentType; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentApi.ts index abd13a73a..556aa95cc 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentApi.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentApi.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiBookingInfo } from "@eshg/employee-portal-api/officialMedicalService"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiBookingInfo } from "@eshg/official-medical-service-api"; import { useOmsAppointmentApi } from "@/lib/businessModules/officialMedicalService/api/clients"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentBlocksApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentBlocksApi.ts index 52844b566..c25520989 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentBlocksApi.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/appointmentBlocksApi.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCreateDailyAppointmentBlockGroupRequest } from "@eshg/employee-portal-api/officialMedicalService"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiCreateDailyAppointmentBlockGroupRequest } from "@eshg/official-medical-service-api"; import { useAppointmentBlockApi } from "@/lib/businessModules/officialMedicalService/api/clients"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi.ts index 7896fc11d..603c1bbcf 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi.ts @@ -3,10 +3,13 @@ * 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 { AbortDraftProcedureRequest, AcceptDraftProcedureRequest, ApiConcern, + ApiPatchEmployeeOmsProcedureEmailNotificationsRequest, ApiPatchEmployeeOmsProcedureFacilityRequest, ApiPatchEmployeeOmsProcedurePhysicianRequest, ApiPostEmployeeOmsProcedureRequest, @@ -14,10 +17,11 @@ import { ApiSyncAffectedPersonRequest, ApiSyncFacilityRequest, CloseOpenProcedureRequest, + PatchMedicalOpinionStatusRequest, + PatchWaitingRoomRequest, + PostDocumentRequest, UpdateAffectedPersonRequest, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/official-medical-service-api"; import { useEmployeeOmsProcedureApi } from "@/lib/businessModules/officialMedicalService/api/clients"; import { DefaultFacilityFormValues } from "@/lib/shared/components/facilitySidebar/create/FacilityForm"; @@ -178,6 +182,25 @@ export function usePatchPhysician(procedureId: string) { }); } +export function usePatchEmailNotifications(procedureId: string) { + const snackbar = useSnackbar(); + const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); + + return useHandledMutation({ + mutationFn: ( + request: ApiPatchEmployeeOmsProcedureEmailNotificationsRequest, + ) => { + return employeeOmsProcedureApi.patchEmailNotifications( + procedureId, + request, + ); + }, + onSuccess: () => { + snackbar.confirmation("Die E-Mail-Einstellungen wurden gespeichert."); + }, + }); +} + export function usePostAppointment() { const snackbar = useSnackbar(); const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); @@ -193,3 +216,40 @@ export function usePostAppointment() { onSuccess: () => snackbar.confirmation("Der Termin wurde angelegt."), }); } + +export function usePostDocument() { + const snackbar = useSnackbar(); + const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); + + return useHandledMutation({ + mutationFn: (request: PostDocumentRequest) => + employeeOmsProcedureApi.postDocumentRaw(request), + onSuccess: () => snackbar.confirmation("Das Dokument wurde angelegt."), + }); +} + +export function usePatchMedicalOpinionStatus() { + const snackbar = useSnackbar(); + const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); + + return useHandledMutation({ + mutationFn: (request: PatchMedicalOpinionStatusRequest) => { + return employeeOmsProcedureApi.patchMedicalOpinionStatusRaw(request); + }, + onSuccess: () => { + snackbar.confirmation("Gutachtenstatus geändert"); + }, + }); +} + +export function usePatchWaitingRoom() { + const snackbar = useSnackbar(); + const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); + + return useHandledMutation({ + mutationFn: (request: PatchWaitingRoomRequest) => + employeeOmsProcedureApi.patchWaitingRoomRaw(request), + onSuccess: () => + snackbar.confirmation("Das Wartezimmer wurde aktualisiert."), + }); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi.ts new file mode 100644 index 000000000..a1ab5fc2c --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi.ts @@ -0,0 +1,75 @@ +/** + * Copyright 2025 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 { + PatchCompleteDocumentFileUploadRequest, + PatchDocumentInformationRequest, + PatchDocumentNoteRequest, + PatchDocumentReviewRequest, +} from "@eshg/official-medical-service-api"; + +import { useOmsDocumentApi } from "@/lib/businessModules/officialMedicalService/api/clients"; + +export function usePatchDocumentInformation() { + const snackbar = useSnackbar(); + const omsDocumentApi = useOmsDocumentApi(); + + return useHandledMutation({ + mutationFn: (request: PatchDocumentInformationRequest) => + omsDocumentApi.patchDocumentInformationRaw(request), + onSuccess: () => { + snackbar.confirmation("Das Dokument wurde aktualisiert."); + }, + }); +} + +export function usePatchCompleteDocumentFileUpload() { + const snackbar = useSnackbar(); + const omsDocumentApi = useOmsDocumentApi(); + + return useHandledMutation({ + mutationFn: (request: PatchCompleteDocumentFileUploadRequest) => + omsDocumentApi.patchCompleteDocumentFileUploadRaw(request), + onSuccess: () => { + snackbar.confirmation("Das Dokument wurde aktualisiert."); + }, + }); +} + +export function useDeleteDocument() { + const snackbar = useSnackbar(); + const omsDocumentApi = useOmsDocumentApi(); + + return useHandledMutation({ + mutationFn: (documentId: string) => + omsDocumentApi.deleteDocumentEmployee(documentId), + onSuccess: () => { + snackbar.confirmation("Das Dokument wurde gelöscht."); + }, + }); +} + +export function usePatchDocumentNote() { + const snackbar = useSnackbar(); + const omsDocumentApi = useOmsDocumentApi(); + + return useHandledMutation({ + mutationFn: (request: PatchDocumentNoteRequest) => + omsDocumentApi.patchDocumentNoteRaw(request), + onSuccess: () => { + snackbar.confirmation("Die Stichwörter wurden aktualisiert."); + }, + }); +} + +export function useReviewDocument() { + const omsDocumentApi = useOmsDocumentApi(); + return useHandledMutation({ + mutationFn: (request: PatchDocumentReviewRequest) => + omsDocumentApi.patchDocumentReviewRaw(request), + }); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/apiQueryKeys.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/apiQueryKeys.ts index 0d9c060f7..7d788c7db 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/apiQueryKeys.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/apiQueryKeys.ts @@ -30,3 +30,7 @@ export const employeeOmsProcedureApiQueryKey = queryKeyFactory( ); export const concernApiQueryKey = queryKeyFactory(apiQueryKey(["concernApi"])); + +export const waitingRoomApiQueryKey = queryKeyFactory( + apiQueryKey(["waitingRoomApi"]), +); diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/appointmentBlocksApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/appointmentBlocksApi.ts index 0b99cee4c..6d751c8fd 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/appointmentBlocksApi.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/appointmentBlocksApi.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { mapPaginatedList } from "@eshg/lib-employee-portal/api/models/PaginatedList"; +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { ApiAppointmentType, ApiCreateDailyAppointmentBlockGroupRequest, GetAppointmentBlockGroupsRequest, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { mapPaginatedList } from "@eshg/lib-employee-portal/api/models/PaginatedList"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/official-medical-service-api"; import { queryOptions, useQuery, diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/concerns.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/concerns.ts index f649ea109..8cd382005 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/concerns.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/concerns.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ConcernApi } from "@eshg/employee-portal-api/officialMedicalService"; +import { ConcernApi } from "@eshg/official-medical-service-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useConcernApi } from "@/lib/businessModules/officialMedicalService/api/clients"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi.ts index 7fc4f5272..30c8f9314 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { EmployeeOmsProcedureApi, GetAllEmployeeProceduresRequest, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/official-medical-service-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useEmployeeOmsProcedureApi } from "@/lib/businessModules/officialMedicalService/api/clients"; @@ -53,15 +53,6 @@ export function getProcedureHeaderQuery( export function useGetProcedureDetails(procedureId: string) { const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); - return useSuspenseQuery( - getProcedureDetailsQuery(employeeOmsProcedureApi, procedureId), - ); -} - -export function getProcedureDetailsQuery( - employeeOmsProcedureApi: EmployeeOmsProcedureApi, - procedureId: string, -) { return queryOptions({ queryKey: employeeOmsProcedureApiQueryKey([ "getEmployeeProcedureDetails", @@ -71,3 +62,12 @@ export function getProcedureDetailsQuery( employeeOmsProcedureApi.getEmployeeProcedureDetails(procedureId), }); } + +export function useGetAllDocuments(procedureId: string) { + const employeeOmsProcedureApi = useEmployeeOmsProcedureApi(); + return queryOptions({ + queryKey: employeeOmsProcedureApiQueryKey(["getAllDocuments", procedureId]), + queryFn: () => employeeOmsProcedureApi.getAllDocuments(procedureId), + select: (response) => response.documents, + }); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/waitingRoomApi.ts b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/waitingRoomApi.ts new file mode 100644 index 000000000..737bbf3d3 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/api/queries/waitingRoomApi.ts @@ -0,0 +1,24 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +import { GetWaitingRoomProceduresRequest } from "@eshg/official-medical-service-api"; +import { useSuspenseQuery } from "@tanstack/react-query"; + +import { useWaitingRoomApi } from "@/lib/businessModules/officialMedicalService/api/clients"; +import { waitingRoomApiQueryKey } from "@/lib/businessModules/officialMedicalService/api/queries/apiQueryKeys"; + +export function useGetWaitingRoomProcedures( + request: GetWaitingRoomProceduresRequest, +) { + const waitingRoomApi = useWaitingRoomApi(); + return useSuspenseQuery({ + queryKey: waitingRoomApiQueryKey(["getWaitingRoomProcedures", request]), + queryFn: () => + waitingRoomApi + .getWaitingRoomProceduresRaw(request) + .then(unwrapRawResponse), + }); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx index 105d45591..a9b92a2e9 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx @@ -23,7 +23,6 @@ import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBl import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { FormSheet } from "@/lib/shared/components/form/FormSheet"; -import { fullName } from "@/lib/shared/components/users/userFormatter"; import { validateFieldArray } from "@/lib/shared/helpers/validators"; const DEFAULT_PARALLEL_EXAMINATIONS = 1; @@ -88,8 +87,9 @@ export function AppointmentBlockGroupForm( ) { const snackbar = useSnackbar(); const physicianOptions = props.allPhysicians.map((option) => ({ - value: option.userId, - label: fullName(option), + userId: option.userId, + firstName: option.firstName, + lastName: option.lastName, })); const appointmentDurations = Object.fromEntries( props.allAppointmentTypes.map((currentType) => [ diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx index a0ee8bc9f..5adbc9b3f 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksGroupForm/CreateAppointmentBlockGroupForm.tsx @@ -5,14 +5,14 @@ "use client"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { mapRequiredValue } from "@eshg/lib-portal/helpers/form"; +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { ApiAppointmentType, ApiCreateDailyAppointmentBlock, ApiCreateDailyAppointmentBlockGroupRequest, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { mapRequiredValue } from "@eshg/lib-portal/helpers/form"; -import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +} from "@eshg/official-medical-service-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksTable/AppointmentBlockGroupTable.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksTable/AppointmentBlockGroupTable.tsx index 86decc820..1635f4679 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksTable/AppointmentBlockGroupTable.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/appointmentBlocksTable/AppointmentBlockGroupTable.tsx @@ -5,11 +5,11 @@ "use client"; +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { ApiAppointmentBlockSortKey, ApiAppointmentType, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/official-medical-service-api"; import { Chip } from "@mui/joy"; import { useSuspenseQueries } from "@tanstack/react-query"; import { ColumnSort, createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/constants.ts b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/constants.ts index 8ba90a245..6a3e57ade 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/constants.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/constants.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiAppointmentType } from "@eshg/employee-portal-api/officialMedicalService"; import { EnumMap } from "@eshg/lib-portal/types/helpers"; +import { ApiAppointmentType } from "@eshg/official-medical-service-api"; export const APPOINTMENT_TYPES: EnumMap<ApiAppointmentType> = { [ApiAppointmentType.RegularExamination]: "Regeluntersuchung", diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/options.ts b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/options.ts index 61be359ce..e9a5c1b45 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/options.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/appointmentBlocks/options.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiAppointmentType } from "@eshg/employee-portal-api/officialMedicalService"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; +import { ApiAppointmentType } from "@eshg/official-medical-service-api"; import { APPOINTMENT_TYPES } from "@/lib/businessModules/officialMedicalService/components/appointmentBlocks/constants"; +import { WAITING_STATUS_VALUES } from "@/lib/businessModules/officialMedicalService/shared/translations"; const SUPPORTED_APPOINTMENT_TYPES: string[] = [ ApiAppointmentType.OfficialMedicalService, @@ -15,3 +16,5 @@ const SUPPORTED_APPOINTMENT_TYPES: string[] = [ export const APPOINTMENT_TYPE_OPTIONS = buildEnumOptions( APPOINTMENT_TYPES, ).filter((option) => SUPPORTED_APPOINTMENT_TYPES.includes(option.value)); + +export const WAITING_STATUS_OPTIONS = buildEnumOptions(WAITING_STATUS_VALUES); diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AdditionalInfoPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AdditionalInfoPanel.tsx index 1fd594057..8f60dca36 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AdditionalInfoPanel.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AdditionalInfoPanel.tsx @@ -5,18 +5,19 @@ "use client"; +import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { ApiEmployeeOmsProcedureDetails, ApiProcedureStatus, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { formatPersonName } from "@eshg/lib-portal/formatters/person"; +} from "@eshg/official-medical-service-api"; import { InfoOutlined } from "@mui/icons-material"; import { Alert } from "@mui/joy"; import { isDefined } from "remeda"; import { useConcernSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/ConcernSidebar"; +import { useEmailNotificationSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationSidebar"; import { usePhysicianSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianSidebar"; -import { DetailsCellInlineEdit } from "@/lib/businessModules/officialMedicalService/shared/DetailsCellInlineEdit"; +import { DetailsItemInlineEdit } from "@/lib/businessModules/officialMedicalService/shared/DetailsItemInlineEdit"; import { isProcedureFinalized } from "@/lib/businessModules/officialMedicalService/shared/helpers"; import { EditButton } from "@/lib/shared/components/buttons/EditButton"; import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; @@ -29,6 +30,7 @@ export function AdditionalInfoPanel({ }>) { const concernSidebar = useConcernSidebar(); const physicianSidebar = usePhysicianSidebar(); + const emailNotificationSidebar = useEmailNotificationSidebar(); return ( <InfoTile @@ -70,8 +72,7 @@ export function AdditionalInfoPanel({ </Alert> )} {procedure.concern && ( - <DetailsCellInlineEdit - name="concern" + <DetailsItemInlineEdit label="Anliegen" value={procedure.concern.nameDe} renderEditButton={ @@ -86,7 +87,7 @@ export function AdditionalInfoPanel({ )} {procedure.physician && ( - <DetailsCellInlineEdit + <DetailsItemInlineEdit renderEditButton={ !isProcedureFinalized(procedure) && ( <EditButton @@ -95,11 +96,25 @@ export function AdditionalInfoPanel({ /> ) } - name="physician" label="Ärzt:In" value={formatPersonName(procedure.physician)} /> )} + + {!!procedure.affectedPerson.emailAddresses?.length && ( + <DetailsItemInlineEdit + label="E-Mail-Benachrichtigungen" + value={procedure.sendEmailNotifications ? "Aktiviert" : "Deaktiviert"} + renderEditButton={ + procedure.status === ApiProcedureStatus.Draft && ( + <EditButton + aria-label="E-Mail-Benachrichtigungen bearbeiten" + onClick={() => emailNotificationSidebar.open({ procedure })} + /> + ) + } + /> + )} </InfoTile> ); } diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AffectedPersonPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AffectedPersonPanel.tsx index 04113419e..6a7ddaf69 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AffectedPersonPanel.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AffectedPersonPanel.tsx @@ -8,11 +8,10 @@ import { ApiEmployeeOmsProcedureDetails, ApiProcedureStatus, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { Sheet } from "@mui/joy"; +} from "@eshg/official-medical-service-api"; import { SxProps } from "@mui/joy/styles/types"; -import { UpdateAffectedPersonSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar"; +import { useUpdateAffectedPersonSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar"; import { routes } from "@/lib/businessModules/officialMedicalService/shared/routes"; import { PersonDetails } from "@/lib/businessModules/schoolEntry/api/models/Person"; import { EditButton } from "@/lib/shared/components/buttons/EditButton"; @@ -21,8 +20,7 @@ import { SyncBarrier, useSyncBarrier, } from "@/lib/shared/components/centralFile/sync/SyncBarrier"; -import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; -import { useToggle } from "@/lib/shared/hooks/useToggle"; +import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; const COLUMN_STYLE: SxProps = { flexGrow: 1, @@ -34,10 +32,17 @@ export function AffectedPersonPanel({ }: Readonly<{ procedure: ApiEmployeeOmsProcedureDetails; }>) { - const [editing, toggleEditing] = useToggle(false); - const title = "Betroffene Person"; const person = procedure.affectedPerson; + const updateAffectedPersonSidebar = useUpdateAffectedPersonSidebar(); + + function openSidebar() { + updateAffectedPersonSidebar.open({ + affectedPerson: person, + procedureId: procedure.id, + }); + } + const syncRoute = person.affectedPersonSync !== undefined ? routes.procedures @@ -58,35 +63,31 @@ export function AffectedPersonPanel({ syncPersonParams as PersonDetails, ); + if (!person) { + return null; + } + return ( - person && ( - <Sheet data-testid="affected-person"> - <DetailsSection - title={title} - buttons={ - procedure.status !== ApiProcedureStatus.Closed && - person.affectedPersonSync !== undefined && ( - <SyncBarrier - outdated={person.affectedPersonSync.outdated} - syncHref={syncRoute} - > - <EditButton - aria-label="Person bearbeiten" - onClick={syncBarrier(toggleEditing)} - /> - </SyncBarrier> - ) - } - > - <CentralFilePersonDetails person={person} columnSx={COLUMN_STYLE} /> - </DetailsSection> - <UpdateAffectedPersonSidebar - affectedPerson={person} - onClose={toggleEditing} - open={editing} - procedureId={procedure.id} - /> - </Sheet> - ) + <InfoTile + data-testid="affected-person" + name="affectedPerson" + title="Betroffene Person" + controls={ + procedure.status !== ApiProcedureStatus.Closed && + person.affectedPersonSync !== undefined && ( + <SyncBarrier + outdated={person.affectedPersonSync.outdated} + syncHref={syncRoute} + > + <EditButton + aria-label="Person bearbeiten" + onClick={syncBarrier(openSidebar)} + /> + </SyncBarrier> + ) + } + > + <CentralFilePersonDetails person={person} columnSx={COLUMN_STYLE} /> + </InfoTile> ); } diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentSidebar.tsx index 7ffc1ef1c..2b2d44ae2 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentSidebar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentSidebar.tsx @@ -3,35 +3,40 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointment, - ApiAppointmentType, - ApiBookingType, - ApiOmsAppointment, - ApiUser, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { + AppointmentListForDate, + AppointmentListProps, +} from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentListForDate"; import { AppointmentPickerField, FIELD_LABELS_DE, } from "@eshg/lib-portal/components/formFields/appointmentPicker/AppointmentPickerField"; -import { assertNever } from "@eshg/lib-portal/helpers/assertions"; import { toDateTimeString } from "@eshg/lib-portal/helpers/dateTime"; import { validateIntegerAnd, validateRange, } from "@eshg/lib-portal/helpers/validators"; -import { Box, Button, Sheet, Stack, Typography } from "@mui/joy"; +import { + ApiAppointment, + ApiAppointmentType, + ApiBookingType, + ApiOmsAppointment, + ApiUser, +} from "@eshg/official-medical-service-api"; +import { Sheet, Stack, Typography } from "@mui/joy"; import { addMinutes, isEqual } from "date-fns"; -import { Formik, useFormikContext } from "formik"; -import { useMemo, useState } from "react"; -import { isEmpty, prop, sortBy } from "remeda"; +import { Formik, FormikHelpers, useFormikContext } from "formik"; +import { ReactNode, useMemo, useReducer, useState } from "react"; +import { clamp, isEmpty, prop, sortBy } from "remeda"; import { useBookAppointment } from "@/lib/businessModules/officialMedicalService/api/mutations/appointmentApi"; import { usePostAppointment } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; import { useGetFreeAppointmentsQuery } from "@/lib/businessModules/officialMedicalService/api/queries/appointmentBlocksApi"; -import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; +import { APPOINTMENT_TYPES } from "@/lib/businessModules/schoolEntry/features/procedures/translations"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField"; import { @@ -51,6 +56,7 @@ interface Appointment { } interface AppointmentFormValues { + appointmentType: ApiAppointmentType; bookingType: ApiBookingType | "SelfBooking"; appointment?: Appointment; start: string; @@ -69,7 +75,7 @@ export function useCreateAppointmentSidebar( await createAppointment({ procedureId, request: { - appointmentType: ApiAppointmentType.OfficialMedicalService, + appointmentType: values.appointmentType, bookingInfo: values.bookingType === "SelfBooking" ? undefined @@ -132,6 +138,60 @@ interface AppointmentSidebarProps extends SidebarWithFormRefProps { physician?: ApiUser; } +interface FieldsProps { + allowSelfBooking: boolean; + physician?: ApiUser; + appointments: ApiAppointment[]; + initialValues: AppointmentFormValues; +} + +interface SidebarStep { + title: string; + subTitle: string; + fields: (props: Readonly<FieldsProps>) => ReactNode; +} + +function getAppointmentTypeOptions() { + return [ + { + value: ApiAppointmentType.OfficialMedicalService, + label: APPOINTMENT_TYPES[ApiAppointmentType.OfficialMedicalService], + }, + ]; +} + +function getSteps(editingExistingAppointment: boolean): SidebarStep[] { + return editingExistingAppointment + ? [ + { + title: "Termin buchen", + subTitle: "", + fields: (props: Readonly<FieldsProps>) => <BookingForm {...props} />, + }, + ] + : [ + { + title: "Termin buchen", + subTitle: "Schritt 1 von 2", + fields: () => ( + <> + <SelectField + name="appointmentType" + label="Terminart" + required="Bitte eine Terminart auswählen" + options={getAppointmentTypeOptions()} + /> + </> + ), + }, + { + title: "Termin buchen", + subTitle: "Schritt 2 von 2", + fields: (props: Readonly<FieldsProps>) => <BookingForm {...props} />, + }, + ]; +} + function EmbeddedAppointmentSidebar({ formRef, onClose: handleClose, @@ -145,9 +205,20 @@ function EmbeddedAppointmentSidebar({ physician?.userId, ); + const steps = getSteps(!!appointment); + const lastStepIndex = steps.length - 1; + const [stepIndex, changeToStep] = useReducer( + (_index: number, newIndex: number) => + clamp(newIndex, { min: 0, max: lastStepIndex }), + 0, + ); + const step = steps[stepIndex]!; + const Fields = step.fields; + async function handleSubmit(values: AppointmentFormValues) { if (values.bookingType === ApiBookingType.AppointmentBlock) { values = { + appointmentType: values.appointmentType, bookingType: ApiBookingType.AppointmentBlock, start: toDateTimeString(values.appointment!.start), duration: Math.round( @@ -162,49 +233,39 @@ function EmbeddedAppointmentSidebar({ handleClose(true); } + async function handleNext( + newValues: AppointmentFormValues, + helpers: FormikHelpers<AppointmentFormValues>, + ) { + const isOnLastStep = stepIndex === lastStepIndex; + + if (isOnLastStep) { + await handleSubmit(newValues); + helpers.resetForm(); + changeToStep(0); + } else { + changeToStep(stepIndex + 1); + } + } + return ( - <Formik initialValues={initialValues} onSubmit={handleSubmit}> + <Formik initialValues={initialValues} onSubmit={handleNext}> {({ isSubmitting }) => ( <SidebarForm ref={formRef}> - <SidebarContent title="Termin buchen"> - <AssignedPhysician physician={physician} /> - <RadioAccordionGroupField - name="bookingType" - data-testid="booking-type-radio-control" - > - <RadioAccordionItem - value={ApiBookingType.AppointmentBlock} - label="Aus Terminblock" - > - {(isExpanded) => ( - <AppointmentBlockForm - isExpanded={isExpanded} - appointments={appointments} - initialMonth={initialValues.appointment?.start} - /> - )} - </RadioAccordionItem> - <RadioAccordionItem - value={ApiBookingType.UserDefined} - label="Individueller Termin" - > - {(isExpanded) => ( - <AppointmentUserDefinedForm isExpanded={isExpanded} /> - )} - </RadioAccordionItem> - {allowSelfBooking && ( - <RadioAccordionItem - sx={{ "& .MuiAccordionDetails-root": { height: 0 } }} - value={"SelfBooking"} - label="Selbstbuchung" - /> - )} - </RadioAccordionGroupField> + <SidebarContent title={step.title} subtitle={step.subTitle}> + <Fields + allowSelfBooking={allowSelfBooking} + appointments={appointments} + initialValues={initialValues} + physician={physician} + /> </SidebarContent> <AppointmentSidebarActions isSubmitting={isSubmitting} onClose={handleClose} - initialValues={initialValues} + stepIndex={stepIndex} + changeToStep={changeToStep} + lastStepIndex={lastStepIndex} /> </SidebarForm> )} @@ -215,61 +276,82 @@ function EmbeddedAppointmentSidebar({ function AppointmentSidebarActions({ isSubmitting, onClose, - initialValues, + stepIndex, + changeToStep, + lastStepIndex, }: { isSubmitting: boolean; onClose: (force?: boolean) => void; - initialValues: AppointmentFormValues; + stepIndex: number; + changeToStep: (newStep: number) => void; + lastStepIndex: number; }) { - const { values } = useFormikContext<AppointmentFormValues>(); - const dirty = !isAppointmentFormValuesEqual(initialValues, values); + const { dirty } = useFormikContext<AppointmentFormValues>(); + const isOnFirstStep = stepIndex === 0; + const isOnLastStep = stepIndex === lastStepIndex; + + let submitLabel; + if (isOnLastStep) { + submitLabel = dirty ? "Buchen" : "Schließen"; + } else { + submitLabel = "Weiter"; + } return ( <SidebarActions> - <ButtonBar - left={ - dirty && ( - <Button - variant="plain" - color="primary" - onClick={() => onClose(true)} - > - Abbrechen - </Button> - ) - } - right={ - dirty ? ( - <SubmitButton submitting={isSubmitting}>Buchen</SubmitButton> - ) : ( - <Button onClick={() => onClose(true)}>Schließen</Button> - ) - } + <MultiFormButtonBar + submitting={isSubmitting} + onCancel={() => onClose(true)} + onBack={isOnFirstStep ? undefined : () => changeToStep(stepIndex - 1)} + submitLabel={submitLabel} /> </SidebarActions> ); } -function isAppointmentFormValuesEqual( - v1: AppointmentFormValues, - v2: AppointmentFormValues, -) { - if (v1.bookingType !== v2.bookingType) return false; - switch (v1.bookingType) { - case ApiBookingType.AppointmentBlock: - if (!v1.appointment || !v2.appointment) - return v1.appointment == v2.appointment; - return ( - isEqual(v1.appointment.start, v2.appointment.start) && - isEqual(v1.appointment.end, v2.appointment.end) - ); - case ApiBookingType.UserDefined: - return v1.start === v2.start && v1.duration === v2.duration; - case "SelfBooking": - return true; - default: - assertNever(v1.bookingType); - } +function BookingForm({ + allowSelfBooking, + physician, + appointments, + initialValues, +}: Readonly<FieldsProps>) { + return ( + <> + <AssignedPhysician physician={physician} /> + <RadioAccordionGroupField + name="bookingType" + data-testid="booking-type-radio-control" + > + <RadioAccordionItem + value={ApiBookingType.AppointmentBlock} + label="Aus Terminblock" + > + {(isExpanded) => ( + <AppointmentBlockForm + isExpanded={isExpanded} + appointments={appointments} + initialMonth={initialValues.appointment?.start} + /> + )} + </RadioAccordionItem> + <RadioAccordionItem + value={ApiBookingType.UserDefined} + label="Individueller Termin" + > + {(isExpanded) => ( + <AppointmentUserDefinedForm isExpanded={isExpanded} /> + )} + </RadioAccordionItem> + {allowSelfBooking && ( + <RadioAccordionItem + sx={{ "& .MuiAccordionDetails-root": { height: 0 } }} + value={"SelfBooking"} + label="Selbstbuchung" + /> + )} + </RadioAccordionGroupField> + </> + ); } function useAppointments( @@ -316,6 +398,7 @@ function useAppointments( return { appointments, initialValues: { + appointmentType: ApiAppointmentType.OfficialMedicalService, bookingType: appointment?.bookingType ?? ApiBookingType.AppointmentBlock, appointment: blockAppointment, @@ -356,6 +439,7 @@ function AppointmentBlockForm({ isAppointmentEqual={(apt1: ApiAppointment, apt2: ApiAppointment) => isEqual(apt1.start, apt2.start) && isEqual(apt1.end, apt2.end) } + appointmentList={StyledAppointmentListForDate} /> )} </Sheet> @@ -365,14 +449,14 @@ function AppointmentBlockForm({ function AssignedPhysician({ physician }: { physician?: ApiUser }) { return ( physician && ( - <Box component="dl"> - <Typography component="dt" my={2} level="title-md"> - Zugewiesene:r Arzt/Ärztin - </Typography> - <Typography component="dd" my={2}> - {physician.firstName} {physician.lastName} - </Typography> - </Box> + <DetailsItem + label="Zugewiesene:r Arzt/Ärztin" + value={physician.firstName + " " + physician.lastName} + slotProps={{ + label: { level: "title-md" }, + value: { level: "body-sm" }, + }} + /> ) ); } @@ -398,3 +482,28 @@ function AppointmentUserDefinedForm({ </Stack> ); } + +// AppointmentListForDate but with equal width chips and time labels with no leading zeros +export function StyledAppointmentListForDate<T extends Appointment>( + props: AppointmentListProps<T>, +) { + return ( + <AppointmentListForDate + {...props} + slotProps={{ + chip: { + sx: { + minWidth: "4rem", + paddingX: 0, + }, + }, + }} + getLabel={(apt) => + apt.start.toLocaleTimeString("de-DE", { + hour: "numeric", + minute: "2-digit", + }) + } + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsPanel.tsx index df47b2159..891d97f2c 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsPanel.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsPanel.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEmployeeOmsProcedureDetails } from "@eshg/employee-portal-api/officialMedicalService"; +import { ApiEmployeeOmsProcedureDetails } from "@eshg/official-medical-service-api"; import { Button } from "@mui/joy"; import { useCreateAppointmentSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentSidebar"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsTable.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsTable.tsx index e8ec21f59..4bdcd78de 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsTable.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsTable.tsx @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { EnumMap } from "@eshg/lib-portal/types/helpers"; import { ApiAppointmentState, ApiBookingState, ApiEmployeeOmsProcedureDetails, ApiOmsAppointment, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; -import { EnumMap } from "@eshg/lib-portal/types/helpers"; +} from "@eshg/official-medical-service-api"; import { CheckCircle, Delete, @@ -71,13 +71,11 @@ function createAppointmentColumns({ openCancelAppointmentDialog, openCloseAppointmentDialog, openWithdrawAppointmentDialog, - closeAppointment, }: { openBookingSidebar?: (appointment: ApiOmsAppointment) => void; openCancelAppointmentDialog?: (appointment: ApiOmsAppointment) => void; openCloseAppointmentDialog?: (appointment: ApiOmsAppointment) => void; openWithdrawAppointmentDialog?: (appointment: ApiOmsAppointment) => void; - closeAppointment?: (appointment: ApiOmsAppointment) => Promise<void>; }) { return [ columnHelper.accessor("appointmentType", { @@ -189,12 +187,12 @@ function createAppointmentColumns({ if ( bookingState === ApiBookingState.Cancelled && appointmentState === ApiAppointmentState.Open && - closeAppointment + openCloseAppointmentDialog ) { items.push({ label: "Als abgeschlossen markieren", startDecorator: <CheckCircle />, - onClick: () => closeAppointment(ctx.row.original), + onClick: () => openCloseAppointmentDialog(ctx.row.original), }); } @@ -234,7 +232,7 @@ export function AppointmentsTable({ openConfirmationDialog({ title: "Termin absagen?", description: - "Der/die Bürger:in wird per E-Mail informiert. Ein neuer Termin kann gebucht werden.", + "Ein neuer Termin kann gebucht werden. Der/die Bürger:in wird per E-Mail informiert.", color: "danger", confirmLabel: "Absagen", onConfirm: () => cancelAppointment(appointment), @@ -244,8 +242,7 @@ export function AppointmentsTable({ function openCloseAppointmentDialog(appointment: ApiOmsAppointment) { openConfirmationDialog({ title: "Termin abschließen?", - description: - "Der/die Bürger:in wird per E-Mail informiert. Ein neuer Termin kann gebucht werden.", + description: "Der Termin kann nicht mehr editiert werden.", confirmLabel: "Abschließen", onConfirm: () => closeAppointment(appointment), }); @@ -255,7 +252,7 @@ export function AppointmentsTable({ openConfirmationDialog({ title: "Terminoption zurückziehen?", description: - "Der/die Bürger:in wird per E-Mail informiert. Eine neue Terminoption kann erstellt werden.", + "Es kan kein Termin über das Online Portal mehr gebucht werden. Der/die Bürger:in wird per E-Mail informiert.", color: "danger", confirmLabel: "Zurückziehen", onConfirm: () => withdrawAppointment(appointment), @@ -271,7 +268,6 @@ export function AppointmentsTable({ openCancelAppointmentDialog(appointment), openCloseAppointmentDialog, openWithdrawAppointmentDialog, - closeAppointment, }); return <DataTable data={procedure.appointments} columns={columns} />; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ConcernSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ConcernSidebar.tsx index 569551562..0c432c8d2 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ConcernSidebar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ConcernSidebar.tsx @@ -3,15 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { ApiConcern, ApiConcernCategoryConfig, ApiConcernConfig, ApiEmployeeOmsProcedureDetails, ApiGetConcernsResponse, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; -import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; +} from "@eshg/official-medical-service-api"; import { Grid } from "@mui/joy"; import { Formik, useFormikContext } from "formik"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationSidebar.tsx new file mode 100644 index 000000000..640140854 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationSidebar.tsx @@ -0,0 +1,57 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiEmployeeOmsProcedureDetails } from "@eshg/official-medical-service-api"; + +import { usePatchEmailNotifications } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; +import { + EmailNotificationsForm, + EmailNotificationsFormValues, +} from "@/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationsForm"; +import { + SidebarWithFormRefProps, + UseSidebarWithFormRefResult, + useSidebarWithFormRef, +} from "@/lib/shared/hooks/useSidebarWithFormRef"; + +export function useEmailNotificationSidebar(): UseSidebarWithFormRefResult<EmailNotificationSidebarProps> { + return useSidebarWithFormRef({ component: EmailNotificationSidebar }); +} + +interface EmailNotificationSidebarProps extends SidebarWithFormRefProps { + procedure: ApiEmployeeOmsProcedureDetails; +} + +function EmailNotificationSidebar( + props: Readonly<EmailNotificationSidebarProps>, +) { + const patchEmailNotifications = usePatchEmailNotifications( + props.procedure.id, + ); + + async function handleSubmit(values: EmailNotificationsFormValues) { + await patchEmailNotifications.mutateAsync( + { sendEmailNotifications: values.sendEmailNotifications }, + { + onSuccess: () => { + props.onClose(true); + }, + }, + ); + } + + return ( + <EmailNotificationsForm + title={"E-Mail-Benachrichtigungen"} + onSubmit={handleSubmit} + onCancel={props.onClose} + formRef={props.formRef} + initialValues={{ + sendEmailNotifications: props.procedure.sendEmailNotifications, + }} + submitLabel="Speichern" + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationsForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationsForm.tsx new file mode 100644 index 000000000..7a0885bdf --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/EmailNotificationsForm.tsx @@ -0,0 +1,62 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Stack } from "@mui/joy"; +import { Formik } from "formik"; +import { Ref } from "react"; + +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { SwitchField } from "@/lib/shared/components/formFields/SwitchField"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +export interface EmailNotificationsFormValues { + sendEmailNotifications: boolean; +} + +interface EmailNotificationsFormProps { + initialValues: EmailNotificationsFormValues; + formRef: Ref<SidebarFormHandle>; + onCancel: () => void; + onSubmit: (values: EmailNotificationsFormValues) => Promise<void>; + title: string; + submitLabel: string; +} + +export function EmailNotificationsForm( + props: Readonly<EmailNotificationsFormProps>, +) { + return ( + <Formik + initialValues={props.initialValues} + onSubmit={props.onSubmit} + enableReinitialize + > + {({ isSubmitting }) => ( + <SidebarForm ref={props.formRef}> + <SidebarContent title={props.title}> + <Stack gap={2} rowGap={2}> + <SwitchField + label="E-Mail-Benachrichtigungen an Bürger:in" + name="sendEmailNotifications" + /> + </Stack> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitLabel={props.submitLabel} + submitting={isSubmitting} + onCancel={props.onCancel} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/FacilityPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/FacilityPanel.tsx index 6d2f04bdd..7b9f2ae61 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/FacilityPanel.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/FacilityPanel.tsx @@ -7,8 +7,7 @@ import { ApiEmployeeOmsProcedureDetails, ApiFacilitySync, ApiProcedureStatus, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { Sheet } from "@mui/joy"; +} from "@eshg/official-medical-service-api"; import { SxProps } from "@mui/joy/styles/types"; import { isDefined } from "remeda"; @@ -21,7 +20,7 @@ import { SyncBarrier, useSyncBarrier, } from "@/lib/shared/components/centralFile/sync/SyncBarrier"; -import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; import { useSidebarWithFormRef } from "@/lib/shared/hooks/useSidebarWithFormRef"; const COLUMN_STYLE: SxProps = { @@ -38,6 +37,13 @@ export function FacilityPanel({ component: UpdateFacilitySidebar, }); + function openUpdateFacilitySidebar() { + updateFacilitySidebar.open({ + procedureId: procedure.id, + facility: procedure.facility!, + }); + } + const syncRoute = procedure.facility?.facilitySync !== undefined ? routes.procedures @@ -63,38 +69,33 @@ export function FacilityPanel({ } return ( - <Sheet data-testid="facility"> - <DetailsSection - title={"Auftraggeber"} - buttons={ - isDefined(procedure.facility) && - !procedureClosed() && ( - <SyncBarrier - outdated={procedure.facility?.facilitySync?.outdated ?? false} - syncHref={syncRoute} - > - <EditButton - aria-label="Auftraggeber bearbeiten" - onClick={syncBarrier(() => - updateFacilitySidebar.open({ - procedureId: procedure.id, - facility: procedure.facility!, - }), - )} - /> - </SyncBarrier> - ) - } - > - {procedureDraft() && !isDefined(procedure.facility) ? ( - <AddFacility id={procedure.id} /> - ) : ( - <CentralFileFacilityDetails - facility={{ ...procedure.facility! }} - columnSx={COLUMN_STYLE} - ></CentralFileFacilityDetails> - )} - </DetailsSection> - </Sheet> + <InfoTile + data-testid="facility" + name="facility" + title="Auftraggeber" + controls={ + isDefined(procedure.facility) && + !procedureClosed() && ( + <SyncBarrier + outdated={procedure.facility?.facilitySync?.outdated ?? false} + syncHref={syncRoute} + > + <EditButton + aria-label="Auftraggeber bearbeiten" + onClick={syncBarrier(openUpdateFacilitySidebar)} + /> + </SyncBarrier> + ) + } + > + {procedureDraft() && !isDefined(procedure.facility) ? ( + <AddFacility id={procedure.id} /> + ) : ( + <CentralFileFacilityDetails + facility={{ ...procedure.facility! }} + columnSx={COLUMN_STYLE} + /> + )} + </InfoTile> ); } diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/MedicalOpinionStatusPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/MedicalOpinionStatusPanel.tsx new file mode 100644 index 000000000..47ab2b307 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/MedicalOpinionStatusPanel.tsx @@ -0,0 +1,111 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; +import { + ApiEmployeeOmsProcedureDetails, + ApiMedicalOpinionStatus, +} from "@eshg/official-medical-service-api"; +import { Formik, FormikHelpers } from "formik"; + +import { usePatchMedicalOpinionStatus } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; +import { STATUS_NAMES_MEDICAL_OPINION_STATUS } from "@/lib/businessModules/officialMedicalService/shared/translations"; +import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { FormStack } from "@/lib/shared/components/form/FormStack"; +import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; +import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; + +export function MedicalOpinionStatusPanel({ + procedure, +}: Readonly<{ + procedure: ApiEmployeeOmsProcedureDetails; +}>) { + const { openConfirmationDialog } = useConfirmationDialog(); + const patchMedicalOpinionStatus = usePatchMedicalOpinionStatus(); + const snackbar = useSnackbar(); + + function isMedicalOpinionAccomplished(status: ApiMedicalOpinionStatus) { + return status === ApiMedicalOpinionStatus.Accomplished; + } + + function handleSubmit( + values: ApiEmployeeOmsProcedureDetails, + helpers: FormikHelpers<ApiEmployeeOmsProcedureDetails>, + ) { + if (procedure.medicalOpinionStatus !== values.medicalOpinionStatus) { + openConfirmationDialog({ + onConfirm: async () => { + await patchMedicalOpinionStatus.mutateAsync({ + id: procedure.id, + body: values.medicalOpinionStatus, + }); + }, + onCancel: () => helpers.resetForm(), + confirmLabel: "Bestätigen", + title: "Gutachtenstatus ändern?", + description: + "Der/Die Bürger:in wird per Mail über den neuen Status informiert. Die Statusänderung kann nicht rückgängig gemacht werden.", + }); + } else { + snackbar.notification("Gutachtenstatus wurde nicht verändert."); + helpers.resetForm(); + } + } + + return ( + <InfoTile + data-testid="medical-opinion-status" + name="medicalOpinionStatus" + title="Gutachtenstatus" + > + <Formik + initialValues={procedure} + onSubmit={(values, helpers) => handleSubmit(values, helpers)} + enableReinitialize + > + {({ isSubmitting, handleSubmit, initialValues }) => { + const opinionUnacomplished = !isMedicalOpinionAccomplished( + initialValues.medicalOpinionStatus, + ); + return ( + <FormStack onSubmit={handleSubmit}> + {opinionUnacomplished ? ( + <SelectField + label="Status" + name="medicalOpinionStatus" + options={buildEnumOptions( + STATUS_NAMES_MEDICAL_OPINION_STATUS, + )} + /> + ) : ( + <DetailsItem + label="Status" + value={ + STATUS_NAMES_MEDICAL_OPINION_STATUS[ + initialValues.medicalOpinionStatus + ] + } + ></DetailsItem> + )} + {opinionUnacomplished && ( + <ButtonBar + right={ + <SubmitButton submitting={isSubmitting}> + Speichern + </SubmitButton> + } + /> + )} + </FormStack> + ); + }} + </Formik> + </InfoTile> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianForm.tsx index 327962800..17aacf27f 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianForm.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianForm.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUser } from "@eshg/employee-portal-api/officialMedicalService"; import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; +import { ApiUser } from "@eshg/official-medical-service-api"; import { Stack } from "@mui/joy"; import { Formik } from "formik"; import { Ref } from "react"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianSidebar.tsx index 6a29ac9de..3cfe2fe11 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianSidebar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/PhysicianSidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEmployeeOmsProcedureDetails } from "@eshg/employee-portal-api/officialMedicalService"; +import { ApiEmployeeOmsProcedureDetails } from "@eshg/official-medical-service-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureActionsPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureActionsPanel.tsx index b4bdbb60f..16579f4a6 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureActionsPanel.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureActionsPanel.tsx @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiEmployeeOmsProcedureDetails, ApiProcedureStatus, -} from "@eshg/employee-portal-api/officialMedicalService"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/official-medical-service-api"; import { Button } from "@mui/joy"; import { useRouter } from "next/navigation"; import { ReactNode } from "react"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTab.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTab.tsx index 5cb04e834..5388130ff 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTab.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTab.tsx @@ -6,6 +6,7 @@ "use client"; import { Grid, Stack } from "@mui/joy"; +import { useSuspenseQueries } from "@tanstack/react-query"; import { useGetProcedureDetails } from "@/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi"; import { AdditionalInfoPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/AdditionalInfoPanel"; @@ -13,7 +14,10 @@ import { AffectedPersonPanel } from "@/lib/businessModules/officialMedicalServic import { AppointmentsPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/AppointmentsPanel"; import { DetailsGrid } from "@/lib/businessModules/officialMedicalService/components/procedures/details/DetailsGrid"; import { FacilityPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/FacilityPanel"; +import { MedicalOpinionStatusPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/MedicalOpinionStatusPanel"; import { ProcedureActionsPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureActionsPanel"; +import { WaitingRoomPanel } from "@/lib/businessModules/officialMedicalService/components/procedures/details/WaitingRoomPanel"; +import { isProcedureOpenOrInProgress } from "@/lib/businessModules/officialMedicalService/shared/helpers"; const SPACING = { xxs: 2, sm: 3, md: 3, xxl: 3 }; @@ -24,7 +28,9 @@ interface ProcedureDetailsTabProps { export function ProcedureDetailsTab({ procedureId, }: Readonly<ProcedureDetailsTabProps>) { - const { data: procedure } = useGetProcedureDetails(procedureId); + const [{ data: procedure }] = useSuspenseQueries({ + queries: [useGetProcedureDetails(procedureId)], + }); return ( <DetailsGrid data-testid="procedure-detail-page"> @@ -38,6 +44,10 @@ export function ProcedureDetailsTab({ <Grid xs={3}> <Stack spacing={SPACING}> <AdditionalInfoPanel procedure={procedure} /> + <MedicalOpinionStatusPanel procedure={procedure} /> + {isProcedureOpenOrInProgress(procedure) && ( + <WaitingRoomPanel procedure={procedure} /> + )} <ProcedureActionsPanel procedure={procedure} dataTestid="procedure-actions" diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTabHeader.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTabHeader.tsx index 92126eefe..ffe519aea 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTabHeader.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProcedureDetailsTabHeader.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEmployeeOmsProcedureHeader } from "@eshg/employee-portal-api/officialMedicalService"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiEmployeeOmsProcedureHeader } from "@eshg/official-medical-service-api"; import { TabNavigationHeader, diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProceduresDetailsToolbar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProceduresDetailsToolbar.tsx index 13c14905d..4c7e0fbf7 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProceduresDetailsToolbar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/ProceduresDetailsToolbar.tsx @@ -6,7 +6,11 @@ "use client"; import { ApiUserRole } from "@eshg/base-api"; -import { TextSnippetOutlined, TimelineOutlined } from "@mui/icons-material"; +import { + DescriptionOutlined, + TextSnippetOutlined, + TimelineOutlined, +} from "@mui/icons-material"; import { Chip } from "@mui/joy"; import { procedureStatusNames } from "@/lib/baseModule/api/procedures/enums"; @@ -56,6 +60,11 @@ function buildTabItems(id: string): TabNavigationItem[] { href: routes.procedures.byId(id).details, decorator: <TextSnippetOutlined />, }, + { + tabButtonName: "Dokumente", + href: routes.procedures.byId(id).documents, + decorator: <DescriptionOutlined />, + }, { tabButtonName: "Verlaufseinträge", href: routes.procedures.byId(id).progressEntries, diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar.tsx index 241173c56..07e39ebc2 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateAffectedPersonSidebar.tsx @@ -3,38 +3,41 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiAffectedPerson } from "@eshg/employee-portal-api/officialMedicalService"; import { mapOptional } from "@eshg/lib-employee-portal/api/models/utils"; import { toDateString } from "@eshg/lib-portal/helpers/dateTime"; +import { ApiAffectedPerson } from "@eshg/official-medical-service-api"; import { usePatchAffectedPerson } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; import { mapToPatchAffectedPersonRequest } from "@/lib/businessModules/officialMedicalService/shared/helpers"; import { mapApiAddressToForm } from "@/lib/shared/components/form/address/helpers"; -import { PersonEditSidebar } from "@/lib/shared/components/personSidebar/PersonEditSidebar"; import { DefaultPersonForm, DefaultPersonFormValues, } from "@/lib/shared/components/personSidebar/form/DefaultPersonForm"; +import { PersonSidebarForm } from "@/lib/shared/components/personSidebar/form/PersonSidebarForm"; import { normalizeListInputs } from "@/lib/shared/components/personSidebar/helpers"; -import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm"; +import { + SidebarWithFormRefProps, + useSidebarWithFormRef, +} from "@/lib/shared/hooks/useSidebarWithFormRef"; + +export function useUpdateAffectedPersonSidebar() { + return useSidebarWithFormRef({ + component: UpdateAffectedPersonSidebar, + }); +} -interface UpdateAffectedPersonSidebarProps { +interface UpdateAffectedPersonSidebarProps extends SidebarWithFormRefProps { affectedPerson: ApiAffectedPerson; procedureId: string; - open: boolean; - onClose: () => void; } -export function UpdateAffectedPersonSidebar({ +function UpdateAffectedPersonSidebar({ affectedPerson, procedureId, - open, + formRef, onClose, }: UpdateAffectedPersonSidebarProps) { - const { closeSidebar, handleClose, sidebarFormRef } = useSidebarForm({ - onClose, - }); - const updateAffectedPerson = usePatchAffectedPerson(); const version = affectedPerson.version; @@ -49,18 +52,18 @@ export function UpdateAffectedPersonSidebar({ ), }, { - onSuccess: closeSidebar, + onSuccess: () => onClose(true), }, ); } return ( - <PersonEditSidebar - open={open} + <PersonSidebarForm + mode={"edit"} title={"Betroffene Person bearbeiten"} - onCancel={handleClose} + onCancel={onClose} onSubmit={handleSubmit} - sidebarFormRef={sidebarFormRef} + sidebarFormRef={formRef} initialValues={mapPersonDetailsToForm(affectedPerson)} component={DefaultPersonForm} addressRequired diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateFacilitySidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateFacilitySidebar.tsx index 99f90a53d..98e9a3307 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateFacilitySidebar.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/UpdateFacilitySidebar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiFacility } from "@eshg/employee-portal-api/officialMedicalService"; +import { ApiFacility } from "@eshg/official-medical-service-api"; import { usePatchFacility } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; import { diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/WaitingRoomPanel.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/WaitingRoomPanel.tsx new file mode 100644 index 000000000..f577f95a9 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/WaitingRoomPanel.tsx @@ -0,0 +1,102 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; +import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { + mapOptionalValue, + parseOptionalValue, +} from "@eshg/lib-portal/helpers/form"; +import { + OptionalFieldValue, + SetFieldValueHelper, +} from "@eshg/lib-portal/types/form"; +import { + ApiEmployeeOmsProcedureDetails, + ApiWaitingStatus, +} from "@eshg/official-medical-service-api"; +import { Button } from "@mui/joy"; +import { Formik } from "formik"; + +import { usePatchWaitingRoom } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; +import { WAITING_STATUS_OPTIONS } from "@/lib/businessModules/officialMedicalService/components/appointmentBlocks/options"; +import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; +import { FormStack } from "@/lib/shared/components/form/FormStack"; +import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; +import { InfoTile } from "@/lib/shared/components/infoTile/InfoTile"; + +interface WaitingRoomValues { + info: OptionalFieldValue<string>; + status: OptionalFieldValue<ApiWaitingStatus>; +} + +export function WaitingRoomPanel({ + procedure, +}: Readonly<{ + procedure: ApiEmployeeOmsProcedureDetails; +}>) { + const patchWaitingRoom = usePatchWaitingRoom(); + + async function handleSubmit(values: WaitingRoomValues) { + await patchWaitingRoom.mutateAsync({ + id: procedure.id, + apiWaitingRoom: { + info: mapOptionalValue(values.info), + status: mapOptionalValue(values.status), + }, + }); + } + + async function handleReset(setFieldValue: SetFieldValueHelper) { + void setFieldValue("info", ""); + void setFieldValue("status", ""); + + await handleSubmit({ info: "", status: "" }); + } + + return ( + <InfoTile data-testid="waiting-room" name="waitingRoom" title="Wartezimmer"> + <Formik + initialValues={{ + info: parseOptionalValue(procedure.waitingRoom.info), + status: parseOptionalValue(procedure.waitingRoom.status), + }} + onSubmit={handleSubmit} + > + {({ isSubmitting, handleSubmit, setFieldValue }) => { + return ( + <FormStack onSubmit={handleSubmit}> + <TextareaField + name="info" + label="Zusätzliche Info" + sxTextarea={{ maxHeight: "37px" }} + /> + <SelectField + label="Status" + name="status" + options={WAITING_STATUS_OPTIONS} + /> + <ButtonBar + right={ + <> + <Button + variant="outlined" + onClick={() => handleReset(setFieldValue)} + > + Zurücksetzen + </Button> + <SubmitButton submitting={isSubmitting}> + Speichern + </SubmitButton> + </> + } + /> + </FormStack> + ); + }} + </Formik> + </InfoTile> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm.tsx new file mode 100644 index 000000000..e171f8af3 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm.tsx @@ -0,0 +1,215 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { FormAddMoreButton } from "@eshg/lib-portal/components/form/FormAddMoreButton"; +import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { RadioGroupField } from "@eshg/lib-portal/components/formFields/RadioGroupField"; +import Delete from "@mui/icons-material/Delete"; +import { Divider, IconButton, Radio, Stack, Typography } from "@mui/joy"; +import { Formik, FormikErrors } from "formik"; +import { Ref } from "react"; +import { isDefined } from "remeda"; + +import { theme } from "@/lib/baseModule/theme/theme"; +import { FilesSection } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection"; +import { SwitchField } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField"; +import { HorizontalFieldLabelEnd } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/HorizontalFieldLabelEnd"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +export interface AddDocumentFormValues { + documentTypeDe: string; + documentTypeEn?: string; + helpTextDe?: string; + helpTextEn?: string; + mandatoryDocument: boolean; + uploadInCitizenPortal: boolean; + files?: File[]; + note?: string; + upload?: string; +} + +interface AddDocumentFormProps { + initialValues: AddDocumentFormValues; + formRef: Ref<SidebarFormHandle>; + onCancel: () => void; + onSubmit: (values: AddDocumentFormValues) => Promise<void>; + title: string; + submitLabel: string; +} + +export function AddDocumentForm(props: Readonly<AddDocumentFormProps>) { + async function handleChange( + values: AddDocumentFormValues, + newType: string, + setFieldValue: ( + field: string, + value: boolean | File[] | string, + ) => Promise<void | FormikErrors<AddDocumentFormValues>>, + setFieldTouched: ( + field: string, + isTouched: boolean, + shouldValidate: boolean, + ) => Promise<void | FormikErrors<AddDocumentFormValues>>, + ) { + await setFieldTouched("uploadInCitizenPortal", true, false); + if (isDefined(values.files) && values.files.length >= 1) { + await setFieldValue("files", []); + } + if (newType === "citizen") { + await setFieldValue("uploadInCitizenPortal", true); + await setFieldValue("note", ""); + return; + } + if (newType === "later") { + await setFieldValue("uploadInCitizenPortal", false); + await setFieldValue("note", ""); + return; + } + if (newType === "now") { + await setFieldValue("uploadInCitizenPortal", false); + } + } + + return ( + <Formik + initialValues={props.initialValues} + onSubmit={props.onSubmit} + enableReinitialize + > + {({ isSubmitting, values, setFieldValue, setFieldTouched }) => ( + <SidebarForm ref={props.formRef}> + <SidebarContent title={props.title}> + <Stack gap={2} rowGap={4}> + <Typography level={"title-md"}>Angaben zum Dokument</Typography> + <Stack direction="column" gap={1}> + <InputField + name="documentTypeDe" + label="Dokumentenart" + required="Bitte geben Sie eine Dokumentenart an" + /> + {isDefined(values.documentTypeEn) ? ( + <InputField + name="documentTypeEn" + label="Dokumentenart (EN)" + endDecorator={ + <IconButton + color="danger" + aria-label="Dokumentenart (EN) entfernen" + onClick={async () => { + await setFieldValue("documentTypeEn", undefined); + }} + > + <Delete /> + </IconButton> + } + /> + ) : ( + <FormAddMoreButton + onClick={() => setFieldValue("documentTypeEn", "", false)} + aria-label="Dokument Übersetzen" + > + Übersetzung ergänzen + </FormAddMoreButton> + )} + </Stack> + <Stack direction="column" gap={1}> + <InputField name="helpTextDe" label="Hilfstext" /> + {isDefined(values.helpTextEn) ? ( + <InputField + name="helpTextEn" + label="Hilfstext (EN)" + endDecorator={ + <IconButton + color="danger" + aria-label="Hilfstext (EN) entfernen" + onClick={async () => { + await setFieldValue("helpTextEn", undefined); + }} + > + <Delete /> + </IconButton> + } + /> + ) : ( + <FormAddMoreButton + onClick={() => setFieldValue("helpTextEn", "", false)} + aria-label="Hilfstext Übersetzen" + > + Übersetzung ergänzen + </FormAddMoreButton> + )} + </Stack> + <SwitchField + name="mandatoryDocument" + label="Pflichtdokument" + sx={{ + ".MuiFormLabel-root": { + fontSize: theme.typography["body-md"].fontSize, + fontWeight: theme.typography["body-md"].fontWeight, + }, + }} + component={HorizontalFieldLabelEnd} + /> + <Divider orientation="horizontal" /> + <Stack direction="column" gap={2}> + <RadioGroupField + name="upload" + label="Dateien" + sx={{ + ".MuiFormLabel-root": { + fontSize: theme.typography["title-md"].fontSize, + fontWeight: theme.typography["title-md"].fontWeight, + pb: theme.spacing(2), + }, + }} + onChange={async (newType) => + await handleChange( + values, + newType, + setFieldValue, + setFieldTouched, + ) + } + > + <Stack direction="column" gap={4}> + <Radio value="citizen" label="Upload durch Bürger:in" /> + <Radio value="later" label="Dateien später hochladen" /> + <Radio value="now" label="Dateien jetzt hochladen" /> + </Stack> + </RadioGroupField> + {values.upload === "now" && ( + <Stack gap={2}> + <FilesSection + name="files" + canAdd={true} + withInitialField={true} + addLabel="Weitere Datei hochladen" + /> + <InputField name="note" label="Stichwörter" /> + </Stack> + )} + </Stack> + </Stack> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitLabel={props.submitLabel} + submitting={isSubmitting} + onCancel={props.onCancel} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentSidebar.tsx new file mode 100644 index 000000000..dcd9131f2 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentSidebar.tsx @@ -0,0 +1,73 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PostDocumentRequest } from "@eshg/official-medical-service-api"; + +import { usePostDocument } from "@/lib/businessModules/officialMedicalService/api/mutations/employeeOmsProcedureApi"; +import { + AddDocumentForm, + AddDocumentFormValues, +} from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm"; +import { + SidebarWithFormRefProps, + UseSidebarWithFormRefResult, + useSidebarWithFormRef, +} from "@/lib/shared/hooks/useSidebarWithFormRef"; + +export function useAddDocumentSidebar(): UseSidebarWithFormRefResult<AddDocumentSidebarProps> { + return useSidebarWithFormRef({ component: AddDocumentSidebar }); +} + +interface AddDocumentSidebarProps extends SidebarWithFormRefProps { + procedureId: string; +} + +const INITIAL_VALUES: AddDocumentFormValues = { + documentTypeDe: "", + documentTypeEn: undefined, + helpTextDe: "", + helpTextEn: undefined, + mandatoryDocument: false, + uploadInCitizenPortal: false, + files: [], + note: "", + upload: "later", +}; + +function AddDocumentSidebar(props: Readonly<AddDocumentSidebarProps>) { + const postDocument = usePostDocument(); + + async function handleSubmit(values: AddDocumentFormValues) { + const request: PostDocumentRequest = { + id: props.procedureId, + postDocumentRequest: { + documentTypeDe: values.documentTypeDe, + documentTypeEn: values.documentTypeEn, + helpTextDe: values.helpTextDe, + helpTextEn: values.helpTextEn, + mandatoryDocument: values.mandatoryDocument, + uploadInCitizenPortal: values.uploadInCitizenPortal, + }, + files: values.files as Blob[], + note: values.note, + }; + await postDocument.mutateAsync(request, { + onSuccess: () => { + props.onClose(true); + }, + }); + } + + return ( + <AddDocumentForm + title="Dokument anlegen" + onSubmit={handleSubmit} + onCancel={props.onClose} + formRef={props.formRef} + initialValues={INITIAL_VALUES} + submitLabel="Anlegen" + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/Columns.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/Columns.tsx new file mode 100644 index 000000000..f10a3a390 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/Columns.tsx @@ -0,0 +1,166 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { + ApiDocument, + ApiDocumentStatus, +} from "@eshg/official-medical-service-api"; +import { Delete, ModeEditOutlineOutlined } from "@mui/icons-material"; +import { Chip } from "@mui/joy"; +import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; + +import { statusColorsDocumentStatus } from "@/lib/businessModules/officialMedicalService/shared/constants"; +import { STATUS_NAMES_DOCUMENT_STATUS } from "@/lib/businessModules/officialMedicalService/shared/translations"; +import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu"; +import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; + +const columnHelper: ColumnHelper<ApiDocument> = + createColumnHelper<ApiDocument>(); + +interface ColumnsProps { + onEdit: (document: ApiDocument) => void; + onDelete: (document: ApiDocument) => Promise<void>; + isProcedureFinalized: () => boolean; +} + +export function Columns({ + onEdit, + onDelete, + isProcedureFinalized, +}: Readonly<ColumnsProps>) { + const { openConfirmationDialog } = useConfirmationDialog(); + + return [ + columnHelper.accessor("documentTypeDe", { + header: "Dokumentenart", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("helpTextDe", { + header: "Hilfstext", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("documentStatus", { + header: "Status", + cell: (props) => { + const status: ApiDocumentStatus = props.getValue(); + return ( + <Chip color={statusColorsDocumentStatus[status]} size="md"> + {STATUS_NAMES_DOCUMENT_STATUS[status]} + </Chip> + ); + }, + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + // ToDo: missing in R4D, + // columnHelper.accessor("uploadInCitizenPortal", { + // header: "Upload-Option", + // cell: (props) => { + // return ( + // <Chip color="primary" size="md"> + // {props.getValue() ? "Mitarbeiter:in" : "Bürger:in"} + // </Chip> + // ); + // }, + // enableSorting: true, + // }), + // ToDo: missing attribute in BE "Hochgeladen von"; for now fixed value is displayed + // columnHelper.accessor("??", { + // header: "Hochgeladen von", + // cell: (props) => { + // return ( + // <Chip color={props.getValue() ? "warning" : "primary"} size="md"> + // {props.getValue() ? "Extern" : "Intern"} + // </Chip> + // ); + // }, + // enableSorting: true, + // }), + columnHelper.display({ + header: "Hochgeladen von", + cell: () => { + return ( + <Chip color="primary" size="md"> + Intern + </Chip> + ); + }, + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("mandatoryDocument", { + header: "Pflicht", + cell: (props) => { + return ( + <Chip color={props.getValue() ? "danger" : "primary"} size="md"> + {props.getValue() ? "Ja" : "Nein"} + </Chip> + ); + }, + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("lastDocumentUpload", { + header: "Letzte Aktualisierung", + cell: (props) => formatDateTime(props.getValue()), + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("files", { + header: "Dateien", + cell: (props) => props.getValue()?.length, + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.accessor("note", { + header: "Stichwörter", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { canNavigate: { parentRow: true } }, + }), + columnHelper.display({ + header: "Aktionen", + cell: (props) => ( + <ActionsMenu + actionItems={[ + { + label: "Bearbeiten", + onClick: () => onEdit(props.row.original), + startDecorator: <ModeEditOutlineOutlined />, + }, + ...(isProcedureFinalized() + ? [] + : [ + { + label: "Löschen", + onClick: () => { + openConfirmationDialog({ + title: "Dokument löschen?", + description: + "Möchten Sie das Dokument wirklich löschen? Die Aktion lässt sich nicht widerrufen.", + confirmLabel: "Löschen", + onConfirm: async () => { + await onDelete(props.row.original); + }, + color: "danger", + }); + }, + startDecorator: <Delete color="danger" />, + }, + ]), + ]} + /> + ), + meta: { + width: 96, + }, + }), + ]; +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm.tsx new file mode 100644 index 000000000..6a7218c73 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm.tsx @@ -0,0 +1,156 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiDocument, + ApiDocumentStatus, +} from "@eshg/official-medical-service-api"; +import { Formik } from "formik"; +import { Ref } from "react"; + +import { useReviewDocument } from "@/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; + +import { DocumentFormContent } from "./DocumentFormContent"; + +export interface DocumentFormValues { + documentTypeDe: string; + documentTypeEn?: string; + helpTextDe?: string; + helpTextEn?: string; + mandatoryDocument: boolean; + uploadInCitizenPortal: boolean; + files?: File[]; + note?: string; +} + +interface DocumentFormProps { + initialValues: DocumentFormValues; + document: ApiDocument; + formRef: Ref<SidebarFormHandle>; + onCancel: () => void; + onSubmit: (values: DocumentFormValues) => Promise<void>; + title: string; + submitLabel: string; + onEditInformation?: () => void; + onEditNote?: () => void; + onReject?: () => void; + onClose: () => void; + isProcedureFinalized: boolean; +} + +export function DocumentForm(props: Readonly<DocumentFormProps>) { + const { openConfirmationDialog } = useConfirmationDialog(); + const { mutateAsync: reviewDocument } = useReviewDocument(); + + return ( + <Formik + initialValues={props.initialValues} + onSubmit={async (values) => { + if (isCompletableWithConfirmation({ ...props, ...values })) { + openConfirmationDialog({ + onConfirm: () => props.onSubmit(values), + title: "Datei-Upload abschließen?", + confirmLabel: "Abschließen", + description: + 'Wenn Sie fortfahren, wird der Status auf "Akzeptiert" gesetzt und es können keine weiteren Dateien hinzugefügt werden.', + }); + } else if (isCompletableWithoutConfirmation({ ...props, ...values })) { + await props.onSubmit(values); + props.onClose(); + } else if (needsReview(props)) { + openConfirmationDialog({ + onConfirm: async () => { + await reviewDocument({ + id: props.document.id, + apiPatchDocumentReviewRequest: { + result: ApiDocumentStatus.Accepted, + }, + }); + props.onClose(); + }, + title: "Dokument akzeptieren?", + confirmLabel: "Akzeptieren", + description: + 'Wenn Sie fortfahren, wird der Status auf "Akzeptiert" gesetzt und es können keine weiteren Dateien hinzugefügt werden.', + }); + } else { + props.onClose(); + } + }} + enableReinitialize + > + {({ isSubmitting, values }) => { + const isCompletable = + isCompletableWithConfirmation({ ...props, ...values }) || + isCompletableWithoutConfirmation({ ...props, ...values }); + return ( + <SidebarForm ref={props.formRef}> + <DocumentFormContent {...props} /> + <SidebarActions> + {needsReview(props) ? ( + <MultiFormButtonBar + onCancel={props.onClose} + onReject={props.onReject} + submitLabel="Akzeptieren" + submitting={isSubmitting} + /> + ) : isCompletable ? ( + <MultiFormButtonBar + submitLabel="Abschließen" + onCancel={props.onCancel} + submitting={isSubmitting} + /> + ) : ( + <MultiFormButtonBar + submitLabel={props.submitLabel} + submitting={isSubmitting} + /> + )} + </SidebarActions> + </SidebarForm> + ); + }} + </Formik> + ); +} + +function needsReview({ document }: { document: ApiDocument }): boolean { + return document.documentStatus === ApiDocumentStatus.Submitted; +} + +function isCompletableWithConfirmation({ + document, + files, +}: { + document: ApiDocument; + files?: File[]; +}): boolean { + return ( + document.documentStatus !== ApiDocumentStatus.Accepted && + !!files && + files.length > 0 + ); +} + +function isCompletableWithoutConfirmation({ + document, + files, +}: { + document: ApiDocument; + files?: File[]; +}): boolean { + return ( + document.documentStatus === ApiDocumentStatus.Rejected && + !!files && + files.length > 0 + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentFormContent.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentFormContent.tsx new file mode 100644 index 000000000..e927662c9 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentFormContent.tsx @@ -0,0 +1,192 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiDocument, + ApiDocumentStatus, +} from "@eshg/official-medical-service-api"; +import { WarningAmber } from "@mui/icons-material"; +import { + Alert, + Box, + Chip, + ChipProps, + Divider, + Stack, + Typography, +} from "@mui/joy"; +import { ReactNode } from "react"; +import { isEmpty } from "remeda"; + +import { FilesSection } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection"; +import { statusColorsDocumentStatus } from "@/lib/businessModules/officialMedicalService/shared/constants"; +import { STATUS_NAMES_DOCUMENT_STATUS } from "@/lib/businessModules/officialMedicalService/shared/translations"; +import { EditButton } from "@/lib/shared/components/buttons/EditButton"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +export function DocumentFormContent(props: { + title: string; + document: ApiDocument; + onEditInformation?: () => void; + onEditNote?: () => void; + isProcedureFinalized: boolean; +}) { + return ( + <SidebarContent title={props.title}> + <Stack rowGap={3}> + <Stack + gap={3} + direction="row" + flexWrap="wrap" + width={"90%"} + data-testid="core-data" + > + <ChipItem + label="Status" + color={statusColorsDocumentStatus[props.document.documentStatus]} + value={STATUS_NAMES_DOCUMENT_STATUS[props.document.documentStatus]} + /> + <ChipItem + label="Pflichtdokument" + color={props.document.mandatoryDocument ? "danger" : "neutral"} + value={props.document.mandatoryDocument ? "Ja" : "Nein"} + /> + {props.document.documentStatus !== ApiDocumentStatus.Missing && ( + <ChipItem + label="Hochgeladen von" + color={ + props.document.uploadInCitizenPortal ? "warning" : "primary" + } + value={props.document.uploadInCitizenPortal ? "Extern" : "Intern"} + /> + )} + <ChipItem + label={"Upload-Option"} + color={props.document.uploadInCitizenPortal ? "warning" : "neutral"} + value={ + props.document.uploadInCitizenPortal + ? "Intern und Extern" + : "Intern" + } + /> + </Stack> + <Divider orientation="horizontal" /> + {props.document.documentStatus !== ApiDocumentStatus.Submitted && + props.document.reasonForRejection && ( + <> + <DetailsItem + label="Ablehnungsgrund" + value={props.document.reasonForRejection} + slotProps={{ + label: { level: "title-md", textColor: "text.primary" }, + value: { level: "body-md" }, + }} + /> + <Divider orientation="horizontal" /> + </> + )} + <Stack gap={2}> + <Typography level={"title-md"}>Dateien</Typography> + {props.document.documentStatus === ApiDocumentStatus.Submitted && + props.document.reasonForRejection && ( + <> + <Alert color={"warning"} startDecorator={<WarningAmber />}> + Neu hochgeladene Dateien nach Ablehnung + </Alert> + <DetailsItem + label="Ablehnungsgrund" + value={props.document.reasonForRejection} + /> + </> + )} + <FilesSection + name="files" + canAdd={ + (props.document.documentStatus === ApiDocumentStatus.Missing || + props.document.documentStatus === ApiDocumentStatus.Rejected) && + !props.isProcedureFinalized + } + withInitialField={false} + addLabel="Datei hinzufügen" + files={props.document.files} + /> + <Stack + direction="row" + gap={2} + justifyContent="space-between" + alignItems="start" + data-testid="noteSection" + > + <DetailsItem + label="Stichwörter" + value={!isEmpty(props.document.note) ? props.document.note : "-"} + slotProps={{ value: { pt: 1 } }} + /> + {!isEmpty(props.document.files) && !props.isProcedureFinalized && ( + <EditButton + aria-label="Stichwörter bearbeiten" + onClick={props.onEditNote} + /> + )} + </Stack> + </Stack> + + <Divider orientation="horizontal" /> + <Stack direction="column" gap={2} data-testid="additional-info"> + <Stack direction={"row"} gap={2} justifyContent={"space-between"}> + <Typography level="title-md">Dokument-Angaben</Typography> + {props.document.documentStatus === ApiDocumentStatus.Missing && + !props.isProcedureFinalized && ( + <EditButton + aria-label={"Dokument-Angaben bearbeiten"} + onClick={props.onEditInformation} + /> + )} + </Stack> + <DetailsItem + label="Dokumentenart (EN)" + value={props.document.documentTypeEn ?? "-"} + /> + <DetailsItem + label="Hilfstext" + value={ + !isEmpty(props.document.helpTextDe) + ? props.document.helpTextDe + : "-" + } + /> + <DetailsItem + label="Hilfstext (EN)" + value={props.document.helpTextEn ?? "-"} + /> + </Stack> + </Stack> + </SidebarContent> + ); +} + +function ChipItem({ + label, + color, + value, +}: { + label: string; + color: ChipProps["color"]; + value: ReactNode; +}) { + return ( + <DetailsItem + label={label} + value={ + <Chip color={color} size="md"> + {value} + </Chip> + } + slots={{ value: Box }} + slotProps={{ value: { sx: { pt: 1 } } }} + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentSidebar.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentSidebar.tsx new file mode 100644 index 000000000..bba40c5c6 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentSidebar.tsx @@ -0,0 +1,194 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + PatchCompleteDocumentFileUploadRequest, + PatchDocumentInformationRequest, + PatchDocumentNoteRequest, +} from "@eshg/official-medical-service-api"; +import { useSuspenseQueries } from "@tanstack/react-query"; +import { useState } from "react"; + +import { + usePatchCompleteDocumentFileUpload, + usePatchDocumentInformation, + usePatchDocumentNote, +} from "@/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi"; +import { useGetAllDocuments } from "@/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi"; +import { + DocumentForm, + DocumentFormValues, +} from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm"; +import { EditDocumentInformationForm } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentInformationForm"; +import { EditDocumentNoteForm } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentNoteForm"; +import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; +import { + SidebarWithFormRefProps, + UseSidebarWithFormRefResult, + useSidebarWithFormRef, +} from "@/lib/shared/hooks/useSidebarWithFormRef"; + +import { RejectDocumentForm } from "./RejectDocumentForm"; + +export function useDocumentSidebar(): UseSidebarWithFormRefResult<DocumentSidebarProps> { + return useSidebarWithFormRef({ component: DocumentSidebar }); +} + +interface DocumentSidebarProps extends SidebarWithFormRefProps { + procedureId: string; + documentId: string; + isProcedureFinalized: boolean; +} + +export enum DocumentSidebarMode { + default, + editInformation, + editNote, + reject, +} + +function DocumentSidebar({ + documentId, + procedureId, + isProcedureFinalized, + ...props +}: Readonly<DocumentSidebarProps>) { + const patchCompleteDocumentFileUpload = usePatchCompleteDocumentFileUpload(); + const patchDocumentInformation = usePatchDocumentInformation(); + const patchDocumentNote = usePatchDocumentNote(); + const { openCancelDialog } = useConfirmationDialog(); + + const [{ data: allDocuments }] = useSuspenseQueries({ + queries: [useGetAllDocuments(procedureId)], + }); + const document = allDocuments.find((doc) => doc.id === documentId)!; + + const [mode, setMode] = useState<DocumentSidebarMode>( + DocumentSidebarMode.default, + ); + + async function handleDocumentSubmit(values: DocumentFormValues) { + const request: PatchCompleteDocumentFileUploadRequest = { + id: document.id, + files: values.files as Blob[], + }; + + await patchCompleteDocumentFileUpload.mutateAsync(request, { + onSuccess: () => { + props.onClose(true); + }, + }); + } + + async function handleEditInformation(values: DocumentFormValues) { + const request: PatchDocumentInformationRequest = { + id: document.id, + apiPatchDocumentInformationRequest: { + documentTypeDe: values.documentTypeDe, + documentTypeEn: values.documentTypeEn, + helpTextDe: values.helpTextDe, + helpTextEn: values.helpTextEn, + mandatoryDocument: values.mandatoryDocument, + uploadInCitizenPortal: values.uploadInCitizenPortal, + }, + }; + + await patchDocumentInformation.mutateAsync(request, { + onSuccess: () => { + setMode(DocumentSidebarMode.default); + }, + }); + } + + async function handleEditNote(values: DocumentFormValues) { + const request: PatchDocumentNoteRequest = { + id: document.id, + apiPatchDocumentNoteRequest: { + note: values.note, + }, + }; + + await patchDocumentNote.mutateAsync(request, { + onSuccess: () => { + setMode(DocumentSidebarMode.default); + }, + }); + } + + function handleCancel() { + openCancelDialog({ + onConfirm: () => { + props.onClose(true); + }, + confirmLabel: "Verwerfen", + title: "Hochgeladene Dateien verwerfen?", + description: + "Wenn Sie fortfahren, werden die von Ihnen hochgeladenen Dateien nicht gespeichert.", + }); + } + + const INITIAL_VALUES: DocumentFormValues = { + documentTypeDe: document.documentTypeDe, + documentTypeEn: document.documentTypeEn ?? "", + helpTextDe: document.helpTextDe ?? "", + helpTextEn: document.helpTextEn ?? "", + mandatoryDocument: document.mandatoryDocument, + uploadInCitizenPortal: document.uploadInCitizenPortal, + files: [], + note: document.note ?? "", + }; + + return ( + <> + {mode === DocumentSidebarMode.default && ( + <DocumentForm + title={document.documentTypeDe} + onSubmit={handleDocumentSubmit} + onCancel={handleCancel} + onClose={props.onClose} + onEditInformation={() => setMode(DocumentSidebarMode.editInformation)} + onEditNote={() => setMode(DocumentSidebarMode.editNote)} + onReject={() => setMode(DocumentSidebarMode.reject)} + formRef={props.formRef} + initialValues={INITIAL_VALUES} + document={document} + submitLabel="Schließen" + isProcedureFinalized={isProcedureFinalized} + /> + )} + {mode === DocumentSidebarMode.editInformation && ( + <EditDocumentInformationForm + title="Angaben bearbeiten" + onSubmit={handleEditInformation} + onCancel={() => { + setMode(DocumentSidebarMode.default); + }} + formRef={props.formRef} + initialValues={INITIAL_VALUES} + submitLabel="Speichern" + /> + )} + {mode === DocumentSidebarMode.editNote && ( + <EditDocumentNoteForm + title="Stichwörter bearbeiten" + onSubmit={handleEditNote} + onCancel={() => { + setMode(DocumentSidebarMode.default); + }} + formRef={props.formRef} + initialValues={INITIAL_VALUES} + submitLabel="Speichern" + /> + )} + {mode === DocumentSidebarMode.reject && ( + <RejectDocumentForm + onClose={props.onClose} + formRef={props.formRef} + document={document} + /> + )} + </> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentsTable.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentsTable.tsx new file mode 100644 index 000000000..9d780fd74 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentsTable.tsx @@ -0,0 +1,136 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { ApiDocument } from "@eshg/official-medical-service-api"; +import { AddOutlined, DescriptionOutlined } from "@mui/icons-material"; +import { Button, Stack, Typography } from "@mui/joy"; +import { useSuspenseQueries } from "@tanstack/react-query"; + +import { useDeleteDocument } from "@/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi"; +import { + useGetAllDocuments, + useGetProcedureDetails, +} from "@/lib/businessModules/officialMedicalService/api/queries/employeeOmsProcedureApi"; +import { useAddDocumentSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentSidebar"; +import { Columns } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/Columns"; +import { useDocumentSidebar } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentSidebar"; +import { isProcedureFinalized } from "@/lib/businessModules/officialMedicalService/shared/helpers"; +import { TableTitle } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TableTitle"; +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"; + +interface DocumentsTableProps { + procedureId: string; +} + +export function DocumentsTable({ procedureId }: Readonly<DocumentsTableProps>) { + const addDocumentSidebar = useAddDocumentSidebar(); + const documentSidebar = useDocumentSidebar(); + const [{ data: allDocuments }, { data: procedureDetails }] = + useSuspenseQueries({ + queries: [ + useGetAllDocuments(procedureId), + useGetProcedureDetails(procedureId), + ], + }); + const deleteDocument = useDeleteDocument(); + + return ( + <TablePage + fullHeight + controls={ + !isProcedureFinalized(procedureDetails) && ( + <ButtonBar + right={ + <Button + endDecorator={<AddOutlined />} + onClick={() => addDocumentSidebar.open({ procedureId })} + aria-label="Dokument hinzufügen" + > + Dokument hinzufügen + </Button> + } + alignItems="flex-end" + /> + ) + } + > + <TableSheet title={<TableTitle title="Dokumente" />}> + <DataTable + data={allDocuments} + columns={Columns({ + onEdit: (document: ApiDocument) => { + documentSidebar.open({ + documentId: document.id, + procedureId: procedureId, + isProcedureFinalized: isProcedureFinalized(procedureDetails), + }); + }, + onDelete: async (document: ApiDocument) => { + await deleteDocument.mutateAsync(document.id); + }, + isProcedureFinalized: () => { + return isProcedureFinalized(procedureDetails); + }, + })} + rowNavigation={{ + onClick: (row) => () => { + documentSidebar.open({ + documentId: row.original.id, + procedureId: procedureId, + isProcedureFinalized: isProcedureFinalized(procedureDetails), + }); + }, + focusColumnAccessorKey: "documentTypeDe", + }} + noDataComponent={() => ( + <NoDocumentsAvailable + onAdd={() => addDocumentSidebar.open({ procedureId })} + isProcedureFinalized={isProcedureFinalized(procedureDetails)} + /> + )} + /> + </TableSheet> + </TablePage> + ); +} + +interface NoDocumentsAvailableProps { + onAdd?: () => void; + isProcedureFinalized: boolean; +} + +function NoDocumentsAvailable({ + onAdd, + isProcedureFinalized, +}: Readonly<NoDocumentsAvailableProps>) { + return ( + <Stack + sx={{ + alignItems: "center", + justifyContent: "center", + flex: 1, + }} + > + <DescriptionOutlined sx={{ height: "40px", width: "40px" }} /> + <Typography sx={{ mt: 2, mb: 3 }}> + Noch keine Dokumente hinzugefügt + </Typography> + {!isProcedureFinalized && ( + <Button + endDecorator={<AddOutlined />} + onClick={onAdd} + aria-label="Dokument hinzufügen" + > + Dokument hinzufügen + </Button> + )} + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentInformationForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentInformationForm.tsx new file mode 100644 index 000000000..b70658293 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentInformationForm.tsx @@ -0,0 +1,88 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { Stack } from "@mui/joy"; +import { Formik } from "formik"; +import { Ref } from "react"; + +import { theme } from "@/lib/baseModule/theme/theme"; +import { DocumentFormValues } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm"; +import { SwitchField } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField"; +import { HorizontalFieldLabelEnd } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/HorizontalFieldLabelEnd"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +interface EditDocumentInformationFormProps { + initialValues: DocumentFormValues; + formRef: Ref<SidebarFormHandle>; + onCancel: () => void; + onSubmit: (values: DocumentFormValues) => Promise<void>; + title: string; + submitLabel: string; +} + +export function EditDocumentInformationForm( + props: Readonly<EditDocumentInformationFormProps>, +) { + return ( + <Formik + initialValues={props.initialValues} + onSubmit={props.onSubmit} + enableReinitialize + > + {({ isSubmitting }) => ( + <SidebarForm ref={props.formRef}> + <SidebarContent title={props.title}> + <Stack gap={2} rowGap={2}> + <SwitchField + name="mandatoryDocument" + label="Pflichtdokument" + sx={{ + ".MuiFormLabel-root": { + fontSize: theme.typography["body-md"].fontSize, + fontWeight: theme.typography["body-md"].fontWeight, + }, + }} + component={HorizontalFieldLabelEnd} + /> + <SwitchField + name="uploadInCitizenPortal" + label="Upload durch Bürger:in" + sx={{ + ".MuiFormLabel-root": { + fontSize: theme.typography["body-md"].fontSize, + fontWeight: theme.typography["body-md"].fontWeight, + }, + }} + component={HorizontalFieldLabelEnd} + /> + <InputField + name="documentTypeDe" + label="Dokumentenart" + required="Bitte geben Sie eine Dokumentenart an" + /> + <InputField name="documentTypeEn" label="Dokumentenart (EN)" /> + <InputField name="helpTextDe" label="Hilfstext" /> + <InputField name="helpTextEn" label="Hilfstext (EN)" /> + </Stack> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitLabel={props.submitLabel} + submitting={isSubmitting} + onCancel={props.onCancel} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentNoteForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentNoteForm.tsx new file mode 100644 index 000000000..096873112 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/EditDocumentNoteForm.tsx @@ -0,0 +1,55 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { Stack } from "@mui/joy"; +import { Formik } from "formik"; +import { Ref } from "react"; + +import { DocumentFormValues } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +interface EditDocumentNoteFormProps { + initialValues: DocumentFormValues; + formRef: Ref<SidebarFormHandle>; + onCancel: () => void; + onSubmit: (values: DocumentFormValues) => Promise<void>; + title: string; + submitLabel: string; +} +export function EditDocumentNoteForm( + props: Readonly<EditDocumentNoteFormProps>, +) { + return ( + <Formik + initialValues={props.initialValues} + onSubmit={props.onSubmit} + enableReinitialize + > + {({ isSubmitting }) => ( + <SidebarForm ref={props.formRef}> + <SidebarContent title={props.title}> + <Stack gap={2} rowGap={2}> + <InputField name="note" label="Stichwörter" /> + </Stack> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitLabel={props.submitLabel} + submitting={isSubmitting} + onCancel={props.onCancel} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection.tsx new file mode 100644 index 000000000..f56d35281 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/FilesSection.tsx @@ -0,0 +1,143 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +import { FormAddMoreButton } from "@eshg/lib-portal/components/form/FormAddMoreButton"; +import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; +import { ApiFileType } from "@eshg/lib-procedures-api"; +import { ApiOmsFile } from "@eshg/official-medical-service-api"; +import { Delete, FileDownloadOutlined } from "@mui/icons-material"; +import { Stack } from "@mui/joy"; +import { useFormikContext } from "formik"; +import { isDefined } from "remeda"; + +import { useOmsFileApi } from "@/lib/businessModules/officialMedicalService/api/clients"; +import { AddDocumentFormValues } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/AddDocumentForm"; +import { DocumentFormValues } from "@/lib/businessModules/officialMedicalService/components/procedures/details/documents/DocumentForm"; +import { FileCard } from "@/lib/shared/components/FileCard"; +import { FileField } from "@/lib/shared/components/formFields/file/FileField"; +import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; +import { useToggle } from "@/lib/shared/hooks/useToggle"; + +interface FilesSectionProps { + name: string; + canAdd: boolean; + withInitialField: boolean; + addLabel?: string; + files?: ApiOmsFile[]; +} + +export function FilesSection(props: Readonly<FilesSectionProps>) { + const [active, toggleActive] = useToggle(props.withInitialField); + const { openCancelDialog } = useConfirmationDialog(); + + const { setFieldValue, setFieldTouched, values } = useFormikContext< + AddDocumentFormValues | DocumentFormValues + >(); + + const omsFileApi = useOmsFileApi(); + + const { download } = useFileDownload((fileId: string) => + omsFileApi.getDownloadFileRaw({ fileId }), + ); + + return ( + <Stack gap={2} data-testid="files"> + <Stack gap={1}> + {props.canAdd && + isDefined(values.files) && + values.files.length >= 1 && + values.files.map((file, index) => ( + <FileCard + key={`${file.name}.${index}`} + name={file.name} + type={file.type.split("/").pop()?.toUpperCase() as ApiFileType} + creationDate={new Date(file.lastModified)} + size={file.size} + actions={[ + { + onClick: () => { + openCancelDialog({ + onConfirm: async () => { + const newArr = isDefined(values.files) + ? values.files.filter((i) => i !== file) + : []; + if (newArr.length === 0) { + await setFieldTouched("files", false, false); + toggleActive(); + } + await setFieldValue("files", newArr, false); + }, + confirmLabel: "Löschen", + title: "Datei wirklich löschen?", + description: + "Möchten Sie die Datei wirklich löschen? Die Aktion kann nicht rückgängig gemacht werden.", + }); + }, + indicator: <Delete color="danger" />, + color: "primary", + name: "Löschen", + }, + ]} + /> + ))} + {isDefined(props.files) && + props.files.map((file) => ( + <FileCard + key={file.id} + name={file.name} + type={file.fileType as ApiFileType} + creationDate={file.creationDate} + size={file.size} + actions={[ + { + onClick: () => download(file.id), + indicator: <FileDownloadOutlined />, + color: "primary", + name: "Herunterladen", + }, + // ToDo: @saschl what about preview? + // { + // onClick: () => preview(file.id), + // indicator: <DeleteOutlined />, + // color: "warning", + // name: "Preview", + // },); + ]} + /> + ))} + </Stack> + {props.canAdd && ( + <> + {active && ( + // ToDo: Bug in FileField validation? filetype validation runs via drag and drop only + <FileField + label="Datei hochladen (PDF, JPG oder PNG)" + name="files" + placeholder="Auswählen" + accept={[FileType.Pdf, FileType.Jpeg, FileType.Png]} + onChange={async (value) => { + await setFieldTouched("files", true, false); + await setFieldValue( + props.name, + [...values.files!, value], + false, + ); + toggleActive(); + }} + /> + )} + <FormAddMoreButton + onClick={() => { + toggleActive(); + }} + > + {props.addLabel} + </FormAddMoreButton> + </> + )} + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/RejectDocumentForm.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/RejectDocumentForm.tsx new file mode 100644 index 000000000..646a3cad1 --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/RejectDocumentForm.tsx @@ -0,0 +1,78 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiDocument, + ApiReviewResult, +} from "@eshg/official-medical-service-api"; +import { WarningAmber } from "@mui/icons-material"; +import { Alert, Stack } from "@mui/joy"; +import { Formik } from "formik"; +import { Ref } from "react"; + +import { useReviewDocument } from "@/lib/businessModules/officialMedicalService/api/mutations/omsDocumentApi"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { + SidebarForm, + SidebarFormHandle, +} from "@/lib/shared/components/form/SidebarForm"; +import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +interface RejectDocumentFormProps { + document: ApiDocument; + formRef: Ref<SidebarFormHandle>; + onClose: (force?: boolean) => void; +} + +export function RejectDocumentForm({ + document, + formRef, + onClose, +}: Readonly<RejectDocumentFormProps>) { + const { mutateAsync: reviewDocument } = useReviewDocument(); + + return ( + <Formik + initialValues={{ reasonForRejection: document.reasonForRejection }} + onSubmit={async (values) => { + await reviewDocument({ + id: document.id, + apiPatchDocumentReviewRequest: { + result: ApiReviewResult.Rejected, + reasonForRejection: values.reasonForRejection, + }, + }); + onClose(true); + }} + enableReinitialize + > + {({ isSubmitting }) => ( + <SidebarForm ref={formRef}> + <SidebarContent title="Dokument ablehnen"> + <Stack gap={2} rowGap={2}> + <Alert color={"warning"} startDecorator={<WarningAmber />}> + Bei Ablehnung werden die Dateien dauerhaft gelöscht. + </Alert> + <TextareaField + name="reasonForRejection" + label="Ablehnungsgrund" + required="Bitte geben Sie einen Ablehnungsgrund an" + /> + </Stack> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitLabel="Ablehnen" + submitting={isSubmitting} + onCancel={onClose} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField.tsx new file mode 100644 index 000000000..542887abe --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/details/documents/SwitchField.tsx @@ -0,0 +1,49 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + BaseField, + FieldComponentProps, + useBaseField, +} from "@eshg/lib-portal/components/formFields/BaseField"; +import { FieldProps } from "@eshg/lib-portal/types/form"; +import { Switch } from "@mui/joy"; +import { SxProps } from "@mui/joy/styles/types"; + +// ToDo: @saschl replace old field or rename +interface SwitchFieldProps extends FieldProps<boolean>, FieldComponentProps { + sx?: SxProps; +} + +export function SwitchField(props: SwitchFieldProps) { + const FieldComponent = props.component ?? BaseField; + const field = useBaseField<boolean>(props); + + return ( + <FieldComponent + label={props.label} + helperText={field.helperText} + required={field.required} + error={field.error} + sx={{ + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + ...props.sx, + }} + > + <Switch + checked={field.input.value} + onChange={(checked) => field.helpers.setValue(checked.target.checked)} + sx={{ + "--Switch-trackRadius": "16px", + "--Switch-trackHeight": "24px", + "--Switch-trackWidth": "49px", + "--Switch-thumbSize": "16px", + }} + /> + </FieldComponent> + ); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/CreateProcedure.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/CreateProcedure.tsx index 8781a097a..d947e0a5e 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/CreateProcedure.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/CreateProcedure.tsx @@ -6,7 +6,7 @@ "use client"; import { ApiGetReferencePersonResponse } from "@eshg/base-api"; -import { ApiPostEmployeeOmsProcedureRequest } from "@eshg/employee-portal-api/officialMedicalService"; +import { ApiPostEmployeeOmsProcedureRequest } from "@eshg/official-medical-service-api"; import { Add } from "@mui/icons-material"; import { Button } from "@mui/joy"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/ProceduresOverviewTable.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/ProceduresOverviewTable.tsx index 6712e03e8..cfde95692 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/ProceduresOverviewTable.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/ProceduresOverviewTable.tsx @@ -5,10 +5,10 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; -import { GetAllEmployeeProceduresRequest } from "@eshg/employee-portal-api/officialMedicalService"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { useToggleableState } from "@eshg/lib-portal/hooks/useToggleableState"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; +import { GetAllEmployeeProceduresRequest } from "@eshg/official-medical-service-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { ColumnSort } from "@tanstack/react-table"; import { ReactNode, useMemo, useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/procedureOverviewColumns.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/procedureOverviewColumns.tsx index e4fdb8e6f..b5403e3f9 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/procedureOverviewColumns.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/procedures/overview/procedureOverviewColumns.tsx @@ -3,13 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEmployeeOmsProcedureOverview } from "@eshg/employee-portal-api/officialMedicalService"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiEmployeeOmsProcedureOverview } from "@eshg/official-medical-service-api"; import { WarningAmberOutlined } from "@mui/icons-material"; import { Chip, Tooltip } from "@mui/joy"; import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; +import { statusColorsMedicalOpinionStatus } from "@/lib/businessModules/officialMedicalService/shared/constants"; +import { STATUS_NAMES_MEDICAL_OPINION_STATUS } from "@/lib/businessModules/officialMedicalService/shared/translations"; import { procedureStatusNames, statusColors, @@ -110,6 +112,24 @@ export function procedureOverviewTableColumns() { }, }, }), + columnHelper.accessor("medicalOpinionStatus", { + header: "Gutachten Status", + cell: (props) => ( + <Chip + color={statusColorsMedicalOpinionStatus[props.getValue()]} + size="md" + > + {STATUS_NAMES_MEDICAL_OPINION_STATUS[props.getValue()]} + </Chip> + ), + enableSorting: true, + meta: { + width: 120, + canNavigate: { + parentRow: true, + }, + }, + }), columnHelper.accessor("nextAppointment", { header: "Nächster Termin", cell: (props) => formatDateTime(props.getValue()), diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/WaitingRoomTable.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/WaitingRoomTable.tsx new file mode 100644 index 000000000..260fb779e --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/WaitingRoomTable.tsx @@ -0,0 +1,92 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { ApiWaitingRoomSortKey } from "@eshg/official-medical-service-api"; +import { ColumnSort } from "@tanstack/react-table"; + +import { useGetWaitingRoomProcedures } from "@/lib/businessModules/officialMedicalService/api/queries/waitingRoomApi"; +import { waitingRoomColumns } from "@/lib/businessModules/officialMedicalService/components/waitingRoom/waitingRoomColumns"; +import { routes } from "@/lib/businessModules/officialMedicalService/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 { getSortDirection } from "@/lib/shared/components/table/sorting"; +import { + CustomSortingProps, + useTableControl, +} from "@/lib/shared/hooks/searchParams/useTableControl"; + +const initialSorting: ColumnSort = { + id: "modifiedAt", + desc: true, +}; + +export function WaitingRoomTable() { + const tableControl = useTableControl({ + serverSideSorting: true, + sortFieldName: "sortKey", + sortDirectionName: "sortDirection", + initialSorting: initialSorting, + }); + + const procedures = useGetWaitingRoomProcedures({ + pageNumber: tableControl.paginationProps.pageNumber, + pageSize: tableControl.paginationProps.pageSize, + sortKey: getSortKey(tableControl.tableSorting), + sortDirection: getSortDirection(tableControl.tableSorting), + }); + + return ( + <TablePage fullHeight data-testid="waiting-room-table"> + <TableSheet + loading={procedures.isFetching} + footer={ + <Pagination + totalCount={procedures.data.totalNumberOfElements} + {...tableControl.paginationProps} + /> + } + > + <DataTable + data={procedures.data.elements} + columns={waitingRoomColumns()} + sorting={tableControl.tableSorting} + enableSortingRemoval={false} + rowNavigation={{ + route: (row) => routes.procedures.byId(row.original.id).details, + focusColumnAccessorKey: "lastName", + }} + minWidth={1200} + ></DataTable> + </TableSheet> + </TablePage> + ); +} + +const SORT_KEY_MAPPING: Record<string, ApiWaitingRoomSortKey> = { + firstName: ApiWaitingRoomSortKey.Firstname, + lastName: ApiWaitingRoomSortKey.Lastname, + dateOfBirth: ApiWaitingRoomSortKey.DateOfBirth, + facilityName: ApiWaitingRoomSortKey.Facility, + physicianName: ApiWaitingRoomSortKey.Physician, + waitingRoom_info: ApiWaitingRoomSortKey.Info, + waitingRoom_status: ApiWaitingRoomSortKey.Status, + modifiedAt: ApiWaitingRoomSortKey.ModifiedAt, +}; + +function getSortKey( + sortingProps: CustomSortingProps, +): ApiWaitingRoomSortKey | undefined { + const sorting = sortingProps.manualSorting + ? sortingProps.sortingState + : sortingProps.initialSorting; + if (sorting?.[0] === undefined) return undefined; + + const columnId = sorting[0].id; + return SORT_KEY_MAPPING[columnId]; +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/waitingRoomColumns.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/waitingRoomColumns.tsx new file mode 100644 index 000000000..57bfd368c --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/components/waitingRoom/waitingRoomColumns.tsx @@ -0,0 +1,113 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiWaitingRoomProcedure } from "@eshg/official-medical-service-api"; +import { Chip } from "@mui/joy"; +import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; + +import { statusColorsWaitingStatus } from "@/lib/businessModules/officialMedicalService/shared/constants"; +import { WAITING_STATUS_VALUES } from "@/lib/businessModules/officialMedicalService/shared/translations"; +import { formatDateTimeRangeToNow } from "@/lib/shared/helpers/dateTime"; + +const columnHelper: ColumnHelper<ApiWaitingRoomProcedure> = + createColumnHelper<ApiWaitingRoomProcedure>(); + +export function waitingRoomColumns() { + return [ + columnHelper.accessor("firstName", { + header: "Vorname", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { + width: 180, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("lastName", { + header: "Nachname", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { + width: 180, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("dateOfBirth", { + header: "Geburtsdatum", + cell: (props) => formatDate(props.getValue()), + enableSorting: true, + meta: { + width: 160, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("facilityName", { + header: "Auftraggeber", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { + width: 250, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("physicianName", { + header: "Ärzt:in", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { + width: 180, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("waitingRoom.status", { + header: "Wartezimmer Status", + cell: (props) => ( + <Chip color={statusColorsWaitingStatus[props.getValue()!]} size="md"> + {WAITING_STATUS_VALUES[props.getValue()!]} + </Chip> + ), + enableSorting: true, + meta: { + width: 200, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("waitingRoom.info", { + header: "Info", + cell: (props) => props.getValue(), + enableSorting: true, + meta: { + width: 200, + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("modifiedAt", { + header: "Seit", + cell: (props) => formatDateTimeRangeToNow(props.getValue()), + enableSorting: true, + meta: { + width: 120, + canNavigate: { + parentRow: true, + }, + }, + }), + ]; +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsCellInlineEdit.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsItemInlineEdit.tsx similarity index 50% rename from employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsCellInlineEdit.tsx rename to employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsItemInlineEdit.tsx index 50ed529e4..04fa02b71 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsCellInlineEdit.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/DetailsItemInlineEdit.tsx @@ -7,15 +7,16 @@ import { Stack } from "@mui/joy"; import { ReactNode } from "react"; import { - DetailsCell, - DetailsCellProps, -} from "@/lib/shared/components/detailsSection/DetailsCell"; + DetailsItem, + DetailsItemProps, +} from "@/lib/shared/components/detailsSection/items/DetailsItem"; -interface DetailsCellInlineEditProps extends DetailsCellProps { +interface DetailsItemInlineEditProps<TLabelProps, TValueProps> + extends DetailsItemProps<TLabelProps, TValueProps> { renderEditButton?: ReactNode; } -export function DetailsCellInlineEdit( - props: Readonly<DetailsCellInlineEditProps>, +export function DetailsItemInlineEdit<TLabelProps, TValueProps>( + props: Readonly<DetailsItemInlineEditProps<TLabelProps, TValueProps>>, ) { return ( <Stack @@ -27,7 +28,7 @@ export function DetailsCellInlineEdit( }} width={"100%"} > - <DetailsCell name={props.name} label={props.label} value={props.value} /> + <DetailsItem label={props.label} value={props.value} /> {props.renderEditButton} </Stack> ); diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/constants.ts b/employee-portal/src/lib/businessModules/officialMedicalService/shared/constants.ts index 7bb25e0a5..8f6c7c000 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/shared/constants.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/constants.ts @@ -3,10 +3,51 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { + ApiDocumentStatus, + ApiMedicalOpinionStatus, + ApiWaitingStatus, +} from "@eshg/official-medical-service-api"; +import { ChipProps } from "@mui/joy"; + export const systemProgressEntryTypeTitles: Record<string, string> = { UPDATE_AFFECTED_PERSON: "Geänderte Personendaten übernommen", SYNC_AFFECTED_PERSON: "Geänderte Personendaten synchronisiert", SYNC_FACILITY: "Geänderte Auftraggeberdaten übernommen", PHYSICIAN_CHANGED: "Geänderte(r) Ärzt:in übernommen", PROCEDURE_STARTED: "Vorgang gestartet", + DOCUMENT_DELETED: "Dokument gelöscht", + APPOINTMENT_FOR_SELF_BOOKING_ADDED: "Selbstbucheroption hinzugefügt", + APPOINTMENT_ADDED_WITH_BOOKING: "Termin mit Buchung hinzugefügt", + APPOINTMENT_BOOKED: "Termin gebucht", + APPOINTMENT_REBOOKED: "Termin geändert", + APPOINTMENT_CANCELED: "Termin abgesagt", + APPOINTMENT_OPTION_WITHDRAWN: "Terminoption zurückgezogen", + APPOINTMENT_CLOSED: "Termin wurde geschlossen", + DOCUMENT_MISSING_BY_CITIZEN: + "Dokument hinzugefügt mit Upload durch Bürger:In Option", + DOCUMENT_MISSING_BY_EMPLOYEE: "Dokument hinzugefügt mit Upload später Option", + DOCUMENT_ACCEPTED: "Dokument hinzugefügt mit Dateien", + DOCUMENT_INFORMATION_CHANGED: + "Dokument bearbeitet mit Änderung Dokumentenart und/oder Hilfstext", + DOCUMENT_STATUS_CHANGE_ACCEPTED: + "Dokument von “Fehlt†nach “Akzeptiert†(Upload durch MA)", }; + +export const statusColorsDocumentStatus = { + [ApiDocumentStatus.Accepted]: "success", + [ApiDocumentStatus.Missing]: "danger", + [ApiDocumentStatus.Rejected]: "danger", + [ApiDocumentStatus.Submitted]: "warning", +} satisfies Record<ApiDocumentStatus, ChipProps["color"]>; + +export const statusColorsMedicalOpinionStatus = { + [ApiMedicalOpinionStatus.InProgress]: "neutral", + [ApiMedicalOpinionStatus.Accomplished]: "success", +} satisfies Record<ApiMedicalOpinionStatus, ChipProps["color"]>; + +export const statusColorsWaitingStatus = { + [ApiWaitingStatus.WaitingForConsultation]: "warning", + [ApiWaitingStatus.InConsultation]: "primary", + [ApiWaitingStatus.Done]: "success", +} satisfies Record<ApiWaitingStatus, ChipProps["color"]>; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/helpers.ts b/employee-portal/src/lib/businessModules/officialMedicalService/shared/helpers.ts index 3da15bb72..4a56e3e42 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/shared/helpers.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/helpers.ts @@ -14,7 +14,7 @@ import { ApiPatchEmployeeOmsProcedureFacilityRequest, ApiPostEmployeeOmsProcedureRequest, ApiProcedureStatus, -} from "@eshg/employee-portal-api/officialMedicalService"; +} from "@eshg/official-medical-service-api"; import { DefaultFacilityFormValues } from "@/lib/shared/components/facilitySidebar/create/FacilityForm"; import { @@ -120,3 +120,13 @@ export function isProcedureFinalized(procedure: { ]; return finalizedStates.includes(procedure.status); } + +export function isProcedureOpenOrInProgress(procedure: { + status: ApiProcedureStatus; +}): boolean { + const openOrInProgressStates: ApiProcedureStatus[] = [ + ApiProcedureStatus.Open, + ApiProcedureStatus.InProgress, + ]; + return openOrInProgressStates.includes(procedure.status); +} diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/routes.ts b/employee-portal/src/lib/businessModules/officialMedicalService/shared/routes.ts index f3e8532b4..d4aeb832f 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/shared/routes.ts +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/routes.ts @@ -11,6 +11,7 @@ export const routes = defineRoutes("/official-medical-service", (omsPath) => ({ byId: (procedureId: string) => defineRoutes(proceduresPath(`/${procedureId}`), (procedurePath) => ({ details: procedurePath("/details"), + documents: procedurePath("/documents"), progressEntries: procedurePath("/progress-entries"), syncAffectedPerson: (fileStateId: string, personVersion: number) => procedurePath( @@ -27,4 +28,5 @@ export const routes = defineRoutes("/official-medical-service", (omsPath) => ({ new: appointmentBlockGroupsPath("/new"), }), ), + waitingRoom: omsPath("/waiting-room"), })); diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/officialMedicalService/shared/sideNavigationItem.tsx index 88ce47043..98746ba2d 100644 --- a/employee-portal/src/lib/businessModules/officialMedicalService/shared/sideNavigationItem.tsx +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/sideNavigationItem.tsx @@ -29,6 +29,11 @@ const NAVIGATION_ITEMS: SideNavigationItem[] = [ href: routes.appointmentBlockGroups.index, accessCheck: hasUserRole(ApiUserRole.OfficialMedicalServiceAdmin), }, + { + name: "Wartezimmer", + href: routes.waitingRoom, + accessCheck: hasUserRole(ApiUserRole.OfficialMedicalServiceAdmin), + }, ].filter(isPlainObject), }, ]; diff --git a/employee-portal/src/lib/businessModules/officialMedicalService/shared/translations.ts b/employee-portal/src/lib/businessModules/officialMedicalService/shared/translations.ts new file mode 100644 index 000000000..44ff5fead --- /dev/null +++ b/employee-portal/src/lib/businessModules/officialMedicalService/shared/translations.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { EnumMap } from "@eshg/lib-portal/types/helpers"; +import { + ApiDocumentStatus, + ApiMedicalOpinionStatus, + ApiWaitingStatus, +} from "@eshg/official-medical-service-api"; + +export const STATUS_NAMES_DOCUMENT_STATUS: EnumMap<ApiDocumentStatus> = { + [ApiDocumentStatus.Accepted]: "Akzeptiert", + [ApiDocumentStatus.Missing]: "Fehlt", + [ApiDocumentStatus.Rejected]: "Nachreichen", + [ApiDocumentStatus.Submitted]: "Zu prüfen", +}; + +export const STATUS_NAMES_MEDICAL_OPINION_STATUS: EnumMap<ApiMedicalOpinionStatus> = + { + [ApiMedicalOpinionStatus.InProgress]: "In Arbeit", + [ApiMedicalOpinionStatus.Accomplished]: "Fertig", + }; + +export const WAITING_STATUS_VALUES: EnumMap<ApiWaitingStatus> = { + [ApiWaitingStatus.WaitingForConsultation]: "Wartet auf Termin", + [ApiWaitingStatus.InConsultation]: "Im Gespräch", + [ApiWaitingStatus.Done]: "Fertig", +}; diff --git a/employee-portal/src/lib/businessModules/schoolEntry/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/schoolEntry/api/queries/archiving.ts index 9db276a6b..756474d5c 100644 --- a/employee-portal/src/lib/businessModules/schoolEntry/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/schoolEntry/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/schoolEntry/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/schoolEntry/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx index e7607af7d..1d3a610d3 100644 --- a/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/schoolEntry/features/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx @@ -21,7 +21,6 @@ import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBl import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { FormSheet } from "@/lib/shared/components/form/FormSheet"; -import { fullName } from "@/lib/shared/components/users/userFormatter"; import { validateFieldArray } from "@/lib/shared/helpers/validators"; const DEFAULT_PARALLEL_EXAMINATIONS = 1; @@ -77,13 +76,15 @@ export function AppointmentBlockGroupForm( props: AppointmentBlockGroupFormProps, ) { const physicianOptions = props.allPhysicians.map((option) => ({ - value: option.userId, - label: fullName(option), + userId: option.userId, + firstName: option.firstName, + lastName: option.lastName, })); const medicalAssistantsOptions = props.allMfas.map((option) => ({ - value: option.userId, - label: fullName(option), + userId: option.userId, + firstName: option.firstName, + lastName: option.lastName, })); const appointmentDurations = Object.fromEntries( props.allAppointmentTypes.map((currentType) => [ 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 4001b4936..eb08d93ea 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 @@ -201,12 +201,14 @@ function UpdateProcedureSidebar(props: UpdateProcedureSidebarProps) { <SelectContactField name="school" label="Schule" + placeholder="Schule suchen" categories={new Set([ApiContactCategory.School])} /> {isHealthDepartmentSelectionMode(locationSelectionMode) && ( <SelectContactField name="location" label="Gesundheitsamt" + placeholder="Gesundheitsamt suchen" categories={new Set([ApiContactCategory.HealthDepartment])} /> )} diff --git a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx index ddf2a7e25..da5b52375 100644 --- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx +++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProcedureFilterSettings.tsx @@ -192,6 +192,7 @@ export function ProcedureFilterSettings(props: ProcedureFilterSettingsProps) { onChange={(schoolId) => props.setFilterFormValue("schoolIdFilter", schoolId) } + placeholder="Schule suchen" /> </FormControl> <FormControl> 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 6904a6c94..6ba918bcd 100644 --- a/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx +++ b/employee-portal/src/lib/businessModules/schoolEntry/features/procedures/proceduresTable/ProceduresTable.tsx @@ -5,12 +5,12 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; import { formatDate, formatDateTime, } from "@eshg/lib-portal/formatters/dateTime"; import { useToggleableState } from "@eshg/lib-portal/hooks/useToggleableState"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { ApiSchoolEntryProcedureSortKey } from "@eshg/school-entry-api"; import { Chip, Stack } from "@mui/joy"; import { useSuspenseQueries } from "@tanstack/react-query"; diff --git a/employee-portal/src/lib/businessModules/statistics/api/mapper/getActiveFilterLabels.ts b/employee-portal/src/lib/businessModules/statistics/api/mapper/getActiveFilterLabels.ts index 13458b0df..2f42d64f5 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mapper/getActiveFilterLabels.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mapper/getActiveFilterLabels.ts @@ -8,7 +8,7 @@ import { isDefined } from "remeda"; import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; import { getDefinitionByValue } from "@/lib/shared/components/filterSettings/models/getDefinitionByValue"; -import { SuppertedEvaluationFilterValues } from "./suppertedEvaluationFilterValues"; +import { SupportedEvaluationFilterValues } from "./supportedEvaluationFilterValues"; const includeNullString = "leere Felder"; @@ -29,7 +29,7 @@ function createNumberFilterLabels( } export function getActiveFilterLabels( - filterValues: SuppertedEvaluationFilterValues[] | undefined, + filterValues: SupportedEvaluationFilterValues[] | undefined, filterDefinitions: FilterDefinition[], ) { if (isDefined(filterValues) && filterValues.length > 0) { @@ -37,7 +37,10 @@ export function getActiveFilterLabels( if (filterValue.type === "Enum") { const definition = getDefinitionByValue(filterDefinitions, filterValue); return `${definition.name}: ${filterValue.selectedValues.map((value) => definition.options.find((option) => option.value === value)!.label).join(", ")}`; - } else { + } else if (filterValue.type === "Text") { + const definition = getDefinitionByValue(filterDefinitions, filterValue); + return `${definition.name}: ${filterValue.value}`; + } else if (filterValue.type === "Number") { const includeNull = filterValue.comparison.nullInclusion === "INCLUDE_NULL"; const definition = getDefinitionByValue(filterDefinitions, filterValue); diff --git a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapAttributesToFilterDefinitions.ts b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapAttributesToFilterDefinitions.ts index d479b7711..9d239e3ff 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapAttributesToFilterDefinitions.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapAttributesToFilterDefinitions.ts @@ -13,6 +13,7 @@ import { import { EnumFilterDefinition } from "@/lib/shared/components/filterSettings/models/EnumFilter"; import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; import { NumberFilterDefinition } from "@/lib/shared/components/filterSettings/models/NumberFilter"; +import { TextFilterDefinition } from "@/lib/shared/components/filterSettings/models/TextFilter"; export function mapAttributesToFilterDefinitions( attributes: FlatAttribute[], @@ -44,6 +45,13 @@ export function mapAttributesToFilterDefinitions( maxValue: attribute.maxValue, unit: attribute.unit, } satisfies NumberFilterDefinition; + case "TextAttribute": + return { + type: "Text", + key: attribute.key, + name: attribute.name, + inAccordion: true, + } satisfies TextFilterDefinition; } }) .filter(isNonNullish); diff --git a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapEvaluationFilterToFilterValue.ts b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapEvaluationFilterToFilterValue.ts index 66a2661ce..41b1b862f 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapEvaluationFilterToFilterValue.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapEvaluationFilterToFilterValue.ts @@ -11,11 +11,11 @@ import { ENUM_TRUE_VALUE, } from "@/lib/businessModules/statistics/components/evaluations/details/filter/enumFilterMappings"; -import { SuppertedEvaluationFilterValues } from "./suppertedEvaluationFilterValues"; +import { SupportedEvaluationFilterValues } from "./supportedEvaluationFilterValues"; export function mapEvaluationFilterToFilterValue( filter: EvaluationFilter, -): SuppertedEvaluationFilterValues { +): SupportedEvaluationFilterValues { switch (filter.type) { case "ValueOptionFilterParameter": { const selectedValues = filter.searchValues; @@ -83,6 +83,12 @@ export function mapEvaluationFilterToFilterValue( : "EXCLUDE_NULL", }, }; + case "TextFilterParameter": + return { + type: "Text", + key: mapAttributeSelectionToKey(filter.attribute), + value: filter.text, + }; default: throw new Error("Not Implemented!"); } diff --git a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapFilterValuesToEvaluationFilters.ts b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapFilterValuesToEvaluationFilters.ts index 4bbada063..f8392011f 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mapper/mapFilterValuesToEvaluationFilters.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mapper/mapFilterValuesToEvaluationFilters.ts @@ -93,6 +93,13 @@ export function mapFilterValuesToEvaluationFilters( withNullValues: withNullValues, } satisfies EvaluationFilter; } + case "TextAttribute": + assertFilterType(filterValue, "Text"); + return { + type: "TextFilterParameter", + text: filterValue.value, + attribute: attributeSelection, + } satisfies EvaluationFilter; default: throw new Error(`Attribute of type ${attribute.type} not expected`); } diff --git a/employee-portal/src/lib/businessModules/statistics/api/mapper/suppertedEvaluationFilterValues.ts b/employee-portal/src/lib/businessModules/statistics/api/mapper/supportedEvaluationFilterValues.ts similarity index 64% rename from employee-portal/src/lib/businessModules/statistics/api/mapper/suppertedEvaluationFilterValues.ts rename to employee-portal/src/lib/businessModules/statistics/api/mapper/supportedEvaluationFilterValues.ts index 0ce31fb09..bd2673ac1 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mapper/suppertedEvaluationFilterValues.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mapper/supportedEvaluationFilterValues.ts @@ -6,8 +6,9 @@ import { EnumFilterValue } from "@/lib/shared/components/filterSettings/models/EnumFilter"; import { FilterValue } from "@/lib/shared/components/filterSettings/models/FilterValue"; import { NumberFilterValue } from "@/lib/shared/components/filterSettings/models/NumberFilter"; +import { TextFilterValue } from "@/lib/shared/components/filterSettings/models/TextFilter"; -export type SuppertedEvaluationFilterValues = Extract< +export type SupportedEvaluationFilterValues = Extract< FilterValue, - EnumFilterValue | NumberFilterValue + EnumFilterValue | NumberFilterValue | TextFilterValue >; diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsTableView.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsTableView.ts index 2aa45e078..9d57a1c85 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsTableView.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsTableView.ts @@ -27,6 +27,8 @@ export interface EvaluationDetailsTableView { tableData: EvaluationDetailsTableRow[]; procedureReferences: ProcedureReferences | undefined; totalNumberOfElements: number; + sortAttribute: ApiGetEvaluationResponse["sortAttribute"]; + sortDirection: ApiGetEvaluationResponse["sortDirection"]; } export function mapEvaluationToTableView( @@ -47,5 +49,7 @@ export function mapEvaluationToTableView( tableData, procedureReferences: mapProcedureReferences({ tableData, attributes }), totalNumberOfElements: evaluation.totalNumberOfElements, + sortAttribute: evaluation.sortAttribute, + sortDirection: evaluation.sortDirection, }; } diff --git a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes.ts b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes.ts index 36f391cc1..25d3510e7 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes.ts @@ -154,6 +154,8 @@ export interface AnalysisHistogramDiagramConfiguration { grouping?: DiagramGrouping; binning: DiagramBinning; bins?: number; + minBin?: number; + maxBin?: number; } export interface AnalysisLineDiagramConfiguration { diff --git a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAnalysis.ts b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAnalysis.ts index a5231931b..2d14dfaef 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAnalysis.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/mutations/useAddAnalysis.ts @@ -9,7 +9,7 @@ import { ApiAddAnalysisRequest, ApiAddAnalysisRequestChartConfiguration, } from "@eshg/statistics-api"; -import { isNonNullish } from "remeda"; +import { isNonNullish, isNumber } from "remeda"; import { useAnalysisApi } from "@/lib/businessModules/statistics/api/clients"; import { mapKeyToAttributeSelection } from "@/lib/businessModules/statistics/api/mapper/mapAttributeSelectionKey"; @@ -68,6 +68,7 @@ export function mapModelToChartConfiguration({ const hasSecondaryAttribute = mapSelectionKeyToBoolean( chartConfigurationModel.secondaryAttribute, ); + const manualBinning = chartConfigurationModel.binning === "MANUAL"; return { type: "HistogramChartConfiguration", primaryAttribute: mapKeyToAttributeSelection( @@ -85,9 +86,14 @@ export function mapModelToChartConfiguration({ ? chartConfigurationModel.grouping : undefined, binningMode: chartConfigurationModel.binning, - numberOfBins: - chartConfigurationModel.binning === "MANUAL" - ? chartConfigurationModel.bins + numberOfBins: manualBinning ? chartConfigurationModel.bins : undefined, + minBin: + manualBinning && isNumber(chartConfigurationModel.minBin) + ? chartConfigurationModel.minBin + : undefined, + maxBin: + manualBinning && isNumber(chartConfigurationModel.maxBin) + ? chartConfigurationModel.maxBin : undefined, }; } 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 1d0e645e2..d7beefebe 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetDetailPageInformation.ts @@ -10,9 +10,8 @@ import { ApiGetDetailPageInformationResponse, EvaluationApi, } from "@eshg/statistics-api"; -import { useSuspenseQuery } from "@tanstack/react-query"; +import { queryOptions } from "@tanstack/react-query"; -import { useEvaluationApi } from "@/lib/businessModules/statistics/api/clients"; import { mapAttributeSelectionToKey } from "@/lib/businessModules/statistics/api/mapper/mapAttributeSelectionKey"; import { mapTimeRangeEndApiToFrontend } from "@/lib/businessModules/statistics/api/mapper/mapTimeRangeEnd"; import { mapToApiBusinessModule } from "@/lib/businessModules/statistics/api/mapper/mapToApiBusinessModule"; @@ -65,6 +64,8 @@ function mapConfiguration( scaling: diagramConfiguration.scaling, binning: diagramConfiguration.binningMode, bins: diagramConfiguration.numberOfBins, + minBin: diagramConfiguration.minBin, + maxBin: diagramConfiguration.maxBin, primaryAttribute: getApiAttribute( diagramConfiguration.primaryAttribute, )!, @@ -165,17 +166,9 @@ export function createQueryGetDetailPageInformation( evaluationApi: EvaluationApi, evaluationId: string, ) { - return { + return queryOptions({ queryKey: evaluationApiQueryKey(["getDetailPageInformation", evaluationId]), queryFn: () => evaluationApi.getDetailPageInformation(evaluationId), select: mapToEvaluationDetailsView, - }; -} - -export function useGetDetailPageInformation(evaluationId: string) { - const evaluationApi = useEvaluationApi(); - const queryResult = useSuspenseQuery( - createQueryGetDetailPageInformation(evaluationApi, evaluationId), - ); - return queryResult.data; + }); } diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluation.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluation.ts index ae0b4a8f7..1aa786c6d 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluation.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluation.ts @@ -5,6 +5,7 @@ import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { EvaluationApi, GetEvaluationRequest } from "@eshg/statistics-api"; +import { queryOptions } from "@tanstack/react-query"; import { mapEvaluationToTableView } from "@/lib/businessModules/statistics/api/models/evaluationDetailsTableView"; @@ -14,10 +15,10 @@ export function createQueryGetEvaluation( evaluationApi: EvaluationApi, evaluationRequest: GetEvaluationRequest, ) { - return { + return queryOptions({ queryKey: evaluationApiQueryKey(["getEvaluation", evaluationRequest]), queryFn: () => evaluationApi.getEvaluationRaw(evaluationRequest).then(unwrapRawResponse), select: mapEvaluationToTableView, - }; + }); } diff --git a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetailsTablePage.ts b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetailsTablePage.ts index 065b5c108..12b1d4db0 100644 --- a/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetailsTablePage.ts +++ b/employee-portal/src/lib/businessModules/statistics/api/queries/useGetEvaluationDetailsTablePage.ts @@ -3,8 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { GetEvaluationRequest } from "@eshg/statistics-api"; +import { + ApiAttributeSelection, + ApiSortDirection, + GetEvaluationRequest, +} from "@eshg/statistics-api"; import { useSuspenseQueries } from "@tanstack/react-query"; +import { useEffect, useRef } from "react"; +import { isDeepEqual, isNullish } from "remeda"; import { useEvaluationApi, @@ -19,11 +25,43 @@ export function useGetEvaluationDetailsTablePage( ) { const evaluationApi = useEvaluationApi(); const filterTemplateApi = useFilterTemplateApi(); + + const defaultSortAttributeRef = useRef<ApiAttributeSelection>(); + const defaultSortDirectionRef = useRef<ApiSortDirection>(); + const [{ data: evaluation }, { data: filterTemplates }] = useSuspenseQueries({ queries: [ - createQueryGetEvaluation(evaluationApi, evaluationRequest), + createQueryGetEvaluation(evaluationApi, { + ...evaluationRequest, + apiGetEvaluationRequest: { + ...evaluationRequest.apiGetEvaluationRequest, + sortAttribute: !isDeepEqual( + defaultSortAttributeRef.current, + evaluationRequest.apiGetEvaluationRequest.sortAttribute, + ) + ? evaluationRequest.apiGetEvaluationRequest.sortAttribute + : undefined, + sortDirection: !isDeepEqual( + defaultSortDirectionRef.current, + evaluationRequest.apiGetEvaluationRequest.sortDirection, + ) + ? evaluationRequest.apiGetEvaluationRequest.sortDirection + : undefined, + }, + }), createQueryGetFilterTemplates(filterTemplateApi, evaluationId), ], }); - return { evaluation, filterTemplates }; + + useEffect(() => { + if (isNullish(defaultSortAttributeRef.current)) { + defaultSortAttributeRef.current = evaluation.sortAttribute; + defaultSortDirectionRef.current = evaluation.sortDirection; + } + }, [evaluation.sortAttribute, evaluation.sortDirection]); + + return { + evaluation, + filterTemplates, + }; } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx index a3e9c497d..f4c8846f1 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/ChooseAttributesStep.tsx @@ -3,20 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Stack } from "@mui/joy"; +import { Checkbox, Stack } from "@mui/joy"; +import { useField } from "formik"; +import { useMemo, useState } from "react"; import { groupBy } from "remeda"; -import { ChooseAttributeStepOrChooseEvaluationStepFormModel } from "@/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/createEvaluationFromScratchFormModel"; +import { ChooseAttributesStepFormModel } from "@/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/chooseAttributesStepFormModel"; import { SearchableGroup, SearchableGroupItem, SearchableGroups, } from "@/lib/shared/components/SearchableGroups"; import { SidebarStepContentProps } from "@/lib/shared/components/SidebarStepper/sidebarStep"; -import { - CheckboxField, - CheckboxFieldProps, -} from "@/lib/shared/components/formFields/CheckboxField"; +import { CheckboxFieldProps } from "@/lib/shared/components/formFields/CheckboxField"; type SearchableCheckboxGroupItem = SearchableGroupItem & { checkboxFieldProps: CheckboxFieldProps; @@ -31,44 +30,76 @@ export interface CategorizedFlatAttribute { } export interface ChooseAttributesStepProps - extends SidebarStepContentProps<ChooseAttributeStepOrChooseEvaluationStepFormModel> { + extends SidebarStepContentProps<ChooseAttributesStepFormModel> { attributes: CategorizedFlatAttribute[]; dataSourceName: string; } -export function ChooseAttributesStep(props: ChooseAttributesStepProps) { - const groupedAttributesWithoutReference = groupBy( - props.attributes.filter( - (attribute) => attribute.code !== "PROCEDURE_REFERENCE", - ), - (attribute) => attribute.category, +export function ChooseAttributesStep({ + fieldName, + dataSourceName, + attributes, +}: ChooseAttributesStepProps) { + const selectedAttributeKeysFieldName = fieldName("selectedAttributeKeys"); + const [input, , helper] = useField<Set<string>>( + selectedAttributeKeysFieldName, ); - const searchableCheckboxGroups: SearchableGroup<SearchableCheckboxGroupItem>[] = - Object.entries(groupedAttributesWithoutReference).map( - ([category, attributes]) => ({ - name: category, - inAccordion: true, - items: attributes.flatMap((attribute) => - mapToCheckboxGroupItem( - attribute, - props.fieldName("selectedAttributeKeys"), - ), - ), - }), + // Make sure, that this expensive component is not re-rendered on value changed + return useMemo(() => { + const groupedAttributesWithoutReference = groupBy( + attributes.filter( + (attribute) => attribute.code !== "PROCEDURE_REFERENCE", + ), + (attribute) => attribute.category, ); - return ( - <Stack> - <SearchableGroups - groups={searchableCheckboxGroups} - label={props.dataSourceName} - placeholder="Attribut suchen" - startExpanded={searchableCheckboxGroups.length === 1} - renderItem={(item) => <CheckboxField {...item.checkboxFieldProps} />} - /> - </Stack> - ); + const searchableCheckboxGroups: SearchableGroup<SearchableCheckboxGroupItem>[] = + Object.entries(groupedAttributesWithoutReference).map( + ([category, attributes]) => ({ + name: category, + inAccordion: true, + items: attributes.flatMap((attribute) => + mapToCheckboxGroupItem(attribute, selectedAttributeKeysFieldName), + ), + }), + ); + + function onChange(value: string, checked: boolean) { + // Avoid triggering validation of all checkboxes + // And avoid rerendering by changing it inline + if (checked) { + input.value.add(value); + } else { + input.value.delete(value); + } + void helper.setValue(input.value, true); + } + + return ( + <Stack> + <SearchableGroups + groups={searchableCheckboxGroups} + label={dataSourceName} + placeholder="Attribut suchen" + startExpanded={searchableCheckboxGroups.length === 1} + renderItem={(item) => ( + <CheckboxItem + item={item} + onChange={onChange} + isChecked={(key: string) => input.value.has(key)} + /> + )} + /> + </Stack> + ); + }, [ + dataSourceName, + attributes, + selectedAttributeKeysFieldName, + input.value, + helper, + ]); } function mapToCheckboxGroupItem( @@ -85,3 +116,31 @@ function mapToCheckboxGroupItem( }, }; } + +function CheckboxItem({ + item, + onChange, + isChecked, +}: { + item: SearchableCheckboxGroupItem; + onChange: (value: string, checked: boolean) => void; + isChecked: (key: string) => boolean; +}) { + const [checked, setChecked] = useState( + isChecked(item.checkboxFieldProps.representingValue!), + ); + return ( + <Checkbox + label={item.checkboxFieldProps.label} + value={item.checkboxFieldProps.representingValue} + checked={checked} + onChange={(changeEvent) => { + setChecked(changeEvent.currentTarget.checked); + onChange( + item.checkboxFieldProps.representingValue!, + changeEvent.currentTarget.checked, + ); + }} + /> + ); +} diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/chooseAttributesStepFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/chooseAttributesStepFormModel.ts index c92d071eb..c06f0955d 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/chooseAttributesStepFormModel.ts +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/chooseAttributesStepFormModel.ts @@ -6,5 +6,5 @@ import { FormikValues } from "formik"; export interface ChooseAttributesStepFormModel extends FormikValues { - selectedAttributeKeys?: string[]; + selectedAttributeKeys?: Set<string>; } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts index c83d340ae..3cef06ef3 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/ChooseAttributesStep/validateChooseAttributeStep.ts @@ -12,7 +12,7 @@ export function validateChooseAttributeStep( ) { if ( isNullish(model.selectedAttributeKeys) || - (model.selectedAttributeKeys.length ?? 0) === 0 + (model.selectedAttributeKeys?.size ?? 0) === 0 ) { return { noAttribute: "Bitte Attribut wählen.", diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/CreateEvaluationFromScratchSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/CreateEvaluationFromScratchSidebar.tsx index f5233b4dc..9d2cbb1d3 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/CreateEvaluationFromScratchSidebar.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/CreateEvaluationSidebar/CreateEvaluationFromScratchSidebar.tsx @@ -107,7 +107,7 @@ export function CreateEvaluationFromScratchSidebar({ const attributeGroups = groupBy( getAttributesFromKeys( [ - ...model[1].selectedAttributeKeys!, + ...model[1].selectedAttributeKeys!.values(), attributeProcedureReference, ].filter(isDefined), dataSource, @@ -187,7 +187,7 @@ export function CreateEvaluationFromScratchSidebar({ }, }), initialValues: { - selectedAttributeKeys: [], + selectedAttributeKeys: new Set<string>(), }, validator: validateChooseAttributeStep, }; @@ -242,7 +242,7 @@ export function CreateEvaluationFromScratchSidebar({ isDefined(prevStepsValues[1].selectedAttributeKeys) && isDefined(dataSource) ? getAttributesFromKeys( - prevStepsValues[1].selectedAttributeKeys, + [...prevStepsValues[1].selectedAttributeKeys.values()], dataSource, ) : undefined, diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview.tsx new file mode 100644 index 000000000..bb7457a87 --- /dev/null +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview.tsx @@ -0,0 +1,281 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Sheet, Stack, Typography } from "@mui/joy"; +import { ReactNode } from "react"; + +export function ChartsSamplePreview({ chart }: { chart: ReactNode }) { + return ( + <Stack gap={3}> + <Typography level="h3" component="h2"> + Vorschau + </Typography> + <Sheet + variant="soft" + color="neutral" + sx={{ + display: "flex", + justifyContent: "center", + alignItems: "center", + borderRadius: (theme) => theme.radius.sm, + padding: 2, + }} + > + {chart} + </Sheet> + </Stack> + ); +} + +export const barChartSimpleSampleData = [ + { + label: "A", + attributes: [ + { + label: "A", + value: 8, + }, + ], + }, + { + label: "B", + attributes: [ + { + label: "B", + value: 7, + }, + ], + }, + { + label: "C", + attributes: [ + { + label: "C", + value: 10, + }, + ], + }, + { + label: "D", + attributes: [ + { + label: "D", + value: 5, + }, + ], + }, +]; + +export const barChartGroupedSampleData = [ + { + label: "A", + attributes: [ + { + label: "a", + value: 7, + }, + { + label: "b", + value: 8, + }, + ], + }, + { + label: "B", + attributes: [ + { + label: "a", + value: 5, + }, + { + label: "b", + value: 7, + }, + ], + }, + { + label: "C", + attributes: [ + { + label: "a", + value: 3, + }, + { + label: "b", + value: 10, + }, + ], + }, + { + label: "D", + attributes: [ + { + label: "a", + value: 1, + }, + { + label: "b", + value: 5, + }, + ], + }, +]; + +export function getHistogramSimpleSampleData(bins: number) { + const histogramSimple = []; + for (let i = 0; i < bins; i++) { + histogramSimple.push({ + min: i, + max: i + 1, + attributes: [ + { + label: "A", + value: 5 + i, + }, + ], + }); + } + return histogramSimple; +} + +export function getHistogramGroupedSampleData(bins: number) { + const histogramGrouped = []; + for (let i = 0; i < bins; i++) { + histogramGrouped.push({ + min: i, + max: i + 1, + attributes: [ + { + label: "A", + value: 5 + i, + }, + { + label: "B", + value: 8, + }, + { + label: "C", + value: 3, + }, + { + label: "D", + value: 15 - 0.5 * i, + }, + ], + }); + } + return histogramGrouped; +} + +export const pieChartSampleData = [ + { + label: "A", + value: 5, + }, + { + label: "B", + value: 8, + }, + { + label: "C", + value: 3, + }, + { + label: "D", + value: 6, + }, + { + label: "E", + value: 1, + }, +]; + +export const lineChartSimpleSampleData = [ + { + label: "Gruppe 1", + dataPoints: [ + { x: 11, y: 11 }, + { x: 22, y: 9.5 }, + { x: 34, y: 12 }, + { x: 46, y: 11 }, + { x: 58, y: 14 }, + ], + }, +]; + +export const lineChartGroupedSampleData = [ + ...lineChartSimpleSampleData, + { + label: "Gruppe 2", + dataPoints: [ + { x: 12.5, y: 8 }, + { x: 20, y: 12 }, + { x: 30, y: 13 }, + { x: 45, y: 18 }, + ], + }, +]; + +export function getScatterChartSimpleSampleData(hasTrendline: boolean) { + return [ + { + label: "Gruppe 1", + dataPoints: [ + { x: 14.2, y: 215 }, + { x: 16.4, y: 325 }, + { x: 11.9, y: 185 }, + { x: 15.2, y: 332 }, + { x: 18.5, y: 406 }, + { x: 22.1, y: 522 }, + { x: 19.4, y: 412 }, + { x: 25.1, y: 614 }, + { x: 23.4, y: 544 }, + { x: 18.1, y: 421 }, + { x: 22.6, y: 445 }, + { x: 17.2, y: 408 }, + ], + trendline: hasTrendline + ? { + offset: -216, + slope: 33, + } + : undefined, + }, + ]; +} + +export function getScatterChartGroupedSampleData(hasTrendline: boolean) { + return [ + ...getScatterChartSimpleSampleData(hasTrendline), + { + label: "Gruppe 2", + dataPoints: [ + { x: 10, y: 500 }, + { x: 15, y: 250 }, + { x: 20, y: 400 }, + { x: 25, y: 250 }, + { x: 30, y: 300 }, + { x: 35, y: 300 }, + ], + trendline: hasTrendline + ? { + offset: 461.9, + slope: -5.714, + } + : undefined, + }, + ]; +} + +export const chartSampleConfiguration = { + xAttribute: { + name: "A", + unit: "m", + }, + yAttribute: { + name: "B", + unit: "kg", + }, +}; diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureBarChartStep/ConfigureBarChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureBarChartStep/ConfigureBarChartStep.tsx index a9ee9aedf..aea1ff12a 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureBarChartStep/ConfigureBarChartStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureBarChartStep/ConfigureBarChartStep.tsx @@ -9,8 +9,14 @@ import { Stack } from "@mui/joy"; import { isNonNullish } from "remeda"; import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; +import { + ChartsSamplePreview, + barChartGroupedSampleData, + barChartSimpleSampleData, +} from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview"; import { ConfigureChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/createAnalysisFormModel"; import { mapAttributeToAutocompleteSelectionOption } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/mapAttribute"; +import { BarChart } from "@/lib/businessModules/statistics/components/shared/charts/BarChart"; import { groupingValueNames, isCategorical, @@ -45,41 +51,62 @@ export function ConfigureBarChartStep({ const scaling = buildEnumOptions(scalingValueNames); return ( - <Stack gap={3}> - <Stack gap={2}> - <SingleAutocompleteField - options={autocompleteSelectOptions} - name={fieldName("primaryAttribute")} - placeholder="Bitte wählen" - label="Primäres Attribut" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={autocompleteSelectOptions} - name={fieldName("secondaryAttribute")} - placeholder="Optional" - label="Sekundäres Attribut" + <Stack gap={4}> + <Stack gap={3}> + <Stack gap={2}> + <SingleAutocompleteField + options={autocompleteSelectOptions} + name={fieldName("primaryAttribute")} + placeholder="Bitte wählen" + label="Primäres Attribut" + required="Bitte wählen Sie ein Attribut aus." + /> + <SingleAutocompleteField + options={autocompleteSelectOptions} + name={fieldName("secondaryAttribute")} + placeholder="Optional" + label="Sekundäres Attribut" + /> + </Stack> + <ToggleButtonGroupField + options={orientations} + name={fieldName("orientation")} + label="Ausrichtung" /> + {showGroupedConfigurations && ( + <> + <ToggleButtonGroupField + options={grouping} + name={fieldName("grouping")} + label="Anordnung" + /> + <ToggleButtonGroupField + options={scaling} + name={fieldName("scaling")} + label="Verhältnisse" + /> + </> + )} </Stack> - <ToggleButtonGroupField - options={orientations} - name={fieldName("orientation")} - label="Ausrichtung" + <ChartsSamplePreview + chart={ + showGroupedConfigurations ? ( + <BarChart + key={"groupedBarChart"} + diagramData={barChartGroupedSampleData} + orientation={values.orientation} + grouping={values.grouping} + scaling={values.scaling} + /> + ) : ( + <BarChart + key={"simpleBarChart"} + diagramData={barChartSimpleSampleData} + orientation={values.orientation} + /> + ) + } /> - {showGroupedConfigurations && ( - <> - <ToggleButtonGroupField - options={grouping} - name={fieldName("grouping")} - label="Anordnung" - /> - <ToggleButtonGroupField - options={scaling} - name={fieldName("scaling")} - label="Verhältnisse" - /> - </> - )} </Stack> ); } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx index fcda202ed..9edf43137 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep.tsx @@ -3,14 +3,21 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { Stack } from "@mui/joy"; import { isDefined, isNonNullish } from "remeda"; import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; +import { + ChartsSamplePreview, + getHistogramGroupedSampleData, + getHistogramSimpleSampleData, +} from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview"; import { ConfigureChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/createAnalysisFormModel"; import { mapAttributeToAutocompleteSelectionOption } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/mapAttribute"; +import { Histogram } from "@/lib/businessModules/statistics/components/shared/charts/Histogram"; import { binningValueNames, groupingValueNames, @@ -54,51 +61,82 @@ export function ConfigureHistogramChartStep({ const binning = buildEnumOptions(binningValueNames); return ( - <Stack gap={3}> - <Stack gap={2}> - <SingleAutocompleteField - options={primaryAutocompleteSelectOptions} - name={fieldName("primaryAttribute")} - placeholder="Bitte wählen" - label="Primäres Attribut" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={secondaryAutocompleteSelectOptions} - name={fieldName("secondaryAttribute")} - placeholder="Optional" - label="Sekundäres Attribut" - /> - </Stack> - {showGroupedConfigurations && ( - <> - <ToggleButtonGroupField - options={grouping} - name={fieldName("grouping")} - label="Anordnung" + <Stack gap={4}> + <Stack gap={3}> + <Stack gap={2}> + <SingleAutocompleteField + options={primaryAutocompleteSelectOptions} + name={fieldName("primaryAttribute")} + placeholder="Bitte wählen" + label="Primäres Attribut" + required="Bitte wählen Sie ein Attribut aus." /> - <ToggleButtonGroupField - options={scaling} - name={fieldName("scaling")} - label="Verhältnisse" - /> - </> - )} - <Stack gap={1}> - <ToggleButtonGroupField - options={binning} - name={fieldName("binning")} - label="Bins" - /> - {showBins && ( - <SliderField - min={1} - max={50} - name={fieldName("bins")} - ariaLabel="Anzahl Bins" + <SingleAutocompleteField + options={secondaryAutocompleteSelectOptions} + name={fieldName("secondaryAttribute")} + placeholder="Optional" + label="Sekundäres Attribut" /> + </Stack> + {showGroupedConfigurations && ( + <> + <ToggleButtonGroupField + options={grouping} + name={fieldName("grouping")} + label="Anordnung" + /> + <ToggleButtonGroupField + options={scaling} + name={fieldName("scaling")} + label="Verhältnisse" + /> + </> )} + <Stack gap={1}> + <ToggleButtonGroupField + options={binning} + name={fieldName("binning")} + label="Bins" + /> + {showBins && ( + <> + <SliderField + min={1} + max={50} + name={fieldName("bins")} + ariaLabel="Anzahl Bins" + /> + <NumberField + label="Bin-Zentrum (unten)" + name={fieldName("minBin")} + required="Bitte geben Sie einen Wert ein." + /> + <NumberField + label="Bin-Zentrum (oben)" + name={fieldName("maxBin")} + required="Bitte geben Sie einen Wert ein." + /> + </> + )} + </Stack> </Stack> + <ChartsSamplePreview + chart={ + showGroupedConfigurations ? ( + <Histogram + key={"groupedHistogram"} + diagramData={getHistogramGroupedSampleData(values.bins)} + grouping={values.grouping} + scaling={values.scaling} + /> + ) : ( + <Histogram + key={"simpleHistogram"} + diagramData={getHistogramSimpleSampleData(values.bins)} + /> + ) + } + /> </Stack> ); } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/configureHistogramChartFormModel.ts b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/configureHistogramChartFormModel.ts index 1bd993950..cf9af522f 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/configureHistogramChartFormModel.ts +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/configureHistogramChartFormModel.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; + import { DiagramBinning, DiagramGrouping, @@ -16,4 +18,6 @@ export interface ConfigureHistogramChartFormModel { scaling: DiagramScaling; binning: DiagramBinning; bins: number; + minBin: OptionalFieldValue<number>; + maxBin: OptionalFieldValue<number>; } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/validateConfigureHistogramChartStep.ts b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/validateConfigureHistogramChartStep.ts new file mode 100644 index 000000000..2120d0654 --- /dev/null +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/validateConfigureHistogramChartStep.ts @@ -0,0 +1,27 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { FormikErrors } from "formik"; +import { isNumber } from "remeda"; + +import { ConfigureHistogramChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/configureHistogramChartFormModel"; + +export function validateConfigureHistogramChartStep( + model: ConfigureHistogramChartFormModel, +): FormikErrors<object> | undefined { + if ( + model.binning === "MANUAL" && + isNumber(model.minBin) && + isNumber(model.maxBin) && + model.minBin >= model.maxBin + ) { + return { + minBinMustBeLessThanMaxBin: + "Das untere Bin-Zentrum muss kleiner als das obere Bin-Zentrum sein.", + }; + } + + return undefined; +} diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureLineChartStep/ConfigureLineChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureLineChartStep/ConfigureLineChartStep.tsx index 127761319..8827942a4 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureLineChartStep/ConfigureLineChartStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureLineChartStep/ConfigureLineChartStep.tsx @@ -6,10 +6,18 @@ import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { Stack } from "@mui/joy"; +import { isNonNullish } from "remeda"; import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; +import { + ChartsSamplePreview, + chartSampleConfiguration, + lineChartGroupedSampleData, + lineChartSimpleSampleData, +} from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview"; import { ConfigureChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/createAnalysisFormModel"; import { mapAttributeToAutocompleteSelectionOption } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/mapAttribute"; +import { LineChart } from "@/lib/businessModules/statistics/components/shared/charts/LineChart"; import { axisRangeValueNames, isCategorical, @@ -26,6 +34,7 @@ export interface ConfigureLineChartStepProps export function ConfigureLineChartStep({ attributes, fieldName, + values, }: ConfigureLineChartStepProps) { const axisAttributes = attributes.map( mapAttributeToAutocompleteSelectionOption((attribute) => @@ -39,34 +48,63 @@ export function ConfigureLineChartStep({ ); const axisRange = buildEnumOptions(axisRangeValueNames); + const showGroupedConfigurations = + isNonNullish(values.secondaryAttribute) && + values.secondaryAttribute.length > 0; + return ( - <Stack gap={3}> - <Stack gap={2}> - <SingleAutocompleteField - options={axisAttributes} - name={fieldName("xAxis")} - placeholder="Bitte wählen" - label="X-Achse" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={axisAttributes} - name={fieldName("yAxis")} - placeholder="Bitte wählen" - label="Y-Achse" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={secondaryAttributes} - name={fieldName("secondaryAttribute")} - placeholder="Optional" - label="Sekundäres Attribut" + <Stack gap={4}> + <Stack gap={3}> + <Stack gap={2}> + <SingleAutocompleteField + options={axisAttributes} + name={fieldName("xAxis")} + placeholder="Bitte wählen" + label="X-Achse" + required="Bitte wählen Sie ein Attribut aus." + /> + <SingleAutocompleteField + options={axisAttributes} + name={fieldName("yAxis")} + placeholder="Bitte wählen" + label="Y-Achse" + required="Bitte wählen Sie ein Attribut aus." + /> + <SingleAutocompleteField + options={secondaryAttributes} + name={fieldName("secondaryAttribute")} + placeholder="Optional" + label="Sekundäres Attribut" + /> + </Stack> + <ToggleButtonGroupField + options={axisRange} + name={fieldName("axisRange")} + label="Achsenskalierung" /> </Stack> - <ToggleButtonGroupField - options={axisRange} - name={fieldName("axisRange")} - label="Achsenskalierung" + <ChartsSamplePreview + chart={ + showGroupedConfigurations ? ( + <LineChart + key={"groupedLineChart"} + diagramData={lineChartGroupedSampleData} + configuration={{ + axisRange: values.axisRange, + ...chartSampleConfiguration, + }} + /> + ) : ( + <LineChart + key={"simpleLineChart"} + diagramData={lineChartSimpleSampleData} + configuration={{ + axisRange: values.axisRange, + ...chartSampleConfiguration, + }} + /> + ) + } /> </Stack> ); diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigurePieChartStep/ConfigurePieChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigurePieChartStep/ConfigurePieChartStep.tsx index 49ec0e83b..a3b7d2f31 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigurePieChartStep/ConfigurePieChartStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigurePieChartStep/ConfigurePieChartStep.tsx @@ -7,8 +7,13 @@ import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/ import { Stack } from "@mui/joy"; import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; +import { + ChartsSamplePreview, + pieChartSampleData, +} from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview"; import { ConfigureChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/createAnalysisFormModel"; import { mapAttributeToAutocompleteSelectionOption } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/mapAttribute"; +import { PieChart } from "@/lib/businessModules/statistics/components/shared/charts/PieChart"; import { isCategorical } from "@/lib/businessModules/statistics/components/shared/charts/chartHelper"; import { AutocompleteSelectOption } from "@/lib/shared/components/AutocompleteSelectOptions"; import { SidebarStepContentProps } from "@/lib/shared/components/SidebarStepper/sidebarStep"; @@ -28,16 +33,17 @@ export function ConfigurePieChartStep({ ), ); return ( - <Stack gap={3}> - <Stack gap={2}> - <SingleAutocompleteField - options={primaryAttributes} - name={fieldName("primaryAttribute")} - placeholder="Bitte wählen" - label="Primäres Attribut" - required="Bitte wählen Sie ein Attribut aus." - /> - </Stack> + <Stack gap={4}> + <SingleAutocompleteField + options={primaryAttributes} + name={fieldName("primaryAttribute")} + placeholder="Bitte wählen" + label="Primäres Attribut" + required="Bitte wählen Sie ein Attribut aus." + /> + <ChartsSamplePreview + chart={<PieChart diagramData={pieChartSampleData} />} + /> </Stack> ); } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureScatterChartStep/ConfigureScatterChartStep.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureScatterChartStep/ConfigureScatterChartStep.tsx index 374434fa5..e9c9eaeb9 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureScatterChartStep/ConfigureScatterChartStep.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureScatterChartStep/ConfigureScatterChartStep.tsx @@ -6,10 +6,18 @@ import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { Stack } from "@mui/joy"; +import { isNonNullish } from "remeda"; import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; +import { + ChartsSamplePreview, + chartSampleConfiguration, + getScatterChartGroupedSampleData, + getScatterChartSimpleSampleData, +} from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ChartsSamplePreview"; import { ConfigureChartFormModel } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/createAnalysisFormModel"; import { mapAttributeToAutocompleteSelectionOption } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/mapAttribute"; +import { ScatterChart } from "@/lib/businessModules/statistics/components/shared/charts/ScatterChart"; import { axisRangeValueNames, isCategorical, @@ -27,6 +35,7 @@ export interface ConfigureScatterChartStepProps export function ConfigureScatterChartStep({ attributes, fieldName, + values, }: ConfigureScatterChartStepProps) { const axisAttributes = attributes.map( mapAttributeToAutocompleteSelectionOption((attribute) => @@ -40,38 +49,67 @@ export function ConfigureScatterChartStep({ ); const axisRange = buildEnumOptions(axisRangeValueNames); + const showGroupedConfigurations = + isNonNullish(values.secondaryAttribute) && + values.secondaryAttribute.length > 0; + return ( - <Stack gap={3}> - <Stack gap={2}> - <SingleAutocompleteField - options={axisAttributes} - name={fieldName("xAxis")} - placeholder="Bitte wählen" - label="X-Achse" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={axisAttributes} - name={fieldName("yAxis")} - placeholder="Bitte wählen" - label="Y-Achse" - required="Bitte wählen Sie ein Attribut aus." - /> - <SingleAutocompleteField - options={secondaryAttributes} - name={fieldName("secondaryAttribute")} - placeholder="Optional" - label="Sekundäres Attribut" - /> - </Stack> - <Stack gap={1}> - <ToggleButtonGroupField - options={axisRange} - name={fieldName("axisRange")} - label="Achsenskalierung" - /> - <SwitchField name={fieldName("trendline")} label="Trendlinie" /> + <Stack gap={4}> + <Stack gap={3}> + <Stack gap={2}> + <SingleAutocompleteField + options={axisAttributes} + name={fieldName("xAxis")} + placeholder="Bitte wählen" + label="X-Achse" + required="Bitte wählen Sie ein Attribut aus." + /> + <SingleAutocompleteField + options={axisAttributes} + name={fieldName("yAxis")} + placeholder="Bitte wählen" + label="Y-Achse" + required="Bitte wählen Sie ein Attribut aus." + /> + <SingleAutocompleteField + options={secondaryAttributes} + name={fieldName("secondaryAttribute")} + placeholder="Optional" + label="Sekundäres Attribut" + /> + </Stack> + <Stack gap={1}> + <ToggleButtonGroupField + options={axisRange} + name={fieldName("axisRange")} + label="Achsenskalierung" + /> + <SwitchField name={fieldName("trendline")} label="Trendlinie" /> + </Stack> </Stack> + <ChartsSamplePreview + chart={ + showGroupedConfigurations ? ( + <ScatterChart + key={`groupedScatterChart_${values.trendline}`} + diagramData={getScatterChartGroupedSampleData(values.trendline)} + configuration={{ + axisRange: values.axisRange, + ...chartSampleConfiguration, + }} + /> + ) : ( + <ScatterChart + key={`simpleScatterChart_${values.trendline}`} + diagramData={getScatterChartSimpleSampleData(values.trendline)} + configuration={{ + axisRange: values.axisRange, + ...chartSampleConfiguration, + }} + /> + ) + } + /> </Stack> ); } diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/CreateAnalysisSidebar.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/CreateAnalysisSidebar.tsx index ba1361590..f773e5cc7 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/CreateAnalysisSidebar.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/CreateAnalysisSidebar.tsx @@ -12,6 +12,7 @@ import { ConfigureBarChartStep } from "@/lib/businessModules/statistics/componen import { validateConfigureBarChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureBarChartStep/validateConfigureBarChartStep"; import { ConfigureChoroplethChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureChoroplethChartStep/ConfigureChoroplethChartStep"; import { ConfigureHistogramChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/ConfigureHistogramChartStep"; +import { validateConfigureHistogramChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureHistogramChartStep/validateConfigureHistogramChartStep"; import { ConfigureLineChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureLineChartStep/ConfigureLineChartStep"; import { ConfigurePieChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigurePieChartStep/ConfigurePieChartStep"; import { ConfigureScatterChartStep } from "@/lib/businessModules/statistics/components/evaluations/details/CreateAnalysisSidebar/ConfigureScatterChartStep/ConfigureScatterChartStep"; @@ -67,6 +68,8 @@ function CreateAnalysisSidebar({ trendline: false, binning: "AUTO", bins: 8, + minBin: "", + maxBin: "", colorScheme: "UNIFORM", characteristicParameter: "MEAN", }; @@ -158,6 +161,7 @@ function CreateAnalysisSidebar({ componentProps: { attributes }, }), initialValues: initialChartConfigurationValues, + validator: validateConfigureHistogramChartStep, }; } }, diff --git a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/table/EvaluationDetailsTable.tsx b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/table/EvaluationDetailsTable.tsx index c16aa2222..4f2d42c6c 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/table/EvaluationDetailsTable.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/evaluations/details/table/EvaluationDetailsTable.tsx @@ -46,7 +46,6 @@ export interface EvaluationDetailsTableProps { resolveProcedureId?: ( procedureReferenceId: string | undefined, ) => string | undefined; - loading: boolean; } export function EvaluationDetailsTable(props: EvaluationDetailsTableProps) { @@ -109,10 +108,7 @@ export function EvaluationDetailsTable(props: EvaluationDetailsTableProps) { ) } > - <TableSheet - loading={props.loading} - footer={<Pagination {...props.paginationProps} />} - > + <TableSheet footer={<Pagination {...props.paginationProps} />}> <DataTable wrapContent wrapHeader diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisAccordionDetails.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisAccordionDetails.tsx index 914d5e911..4c984bf3f 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisAccordionDetails.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisAccordionDetails.tsx @@ -190,6 +190,18 @@ function getDiagramConfigurationValues( ? String(diagramConfiguration.bins) : "Auto", ], + [ + "Bin-Zentrum (unten)", + isNonNullish(diagramConfiguration.minBin) + ? String(diagramConfiguration.minBin) + : undefined, + ], + [ + "Bin-Zentrum (oben)", + isNonNullish(diagramConfiguration.maxBin) + ? String(diagramConfiguration.maxBin) + : undefined, + ], ]; case DiagramType.LINE_CHART: return lineAndScatterValues(diagramConfiguration); diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisChartDiagram.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisChartDiagram.tsx index f5f3d7253..60d56532d 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisChartDiagram.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisChartDiagram.tsx @@ -10,7 +10,7 @@ import { Edit, OpenInFullOutlined, } from "@mui/icons-material"; -import { IconButton, Stack, Typography } from "@mui/joy"; +import { IconButton, Stack, Tooltip, Typography } from "@mui/joy"; import { useState } from "react"; import { isObjectType } from "remeda"; @@ -173,6 +173,7 @@ export function AnalysisChartDiagram(props: { </BaseModal> <AnalysisDiagramBox description={props.analysisDiagram.description} + onShowMoreDescription={() => setOpenFullScreenChart(true)} filterLabels={props.analysisDiagram.filterLabels} evaluatedDataAmount={props.analysisDiagram.evaluatedDataAmount} evaluatedDataAmountTotal={props.evaluatedDataAmountTotal} @@ -184,9 +185,21 @@ export function AnalysisChartDiagram(props: { alignItems="center" minWidth={0} > - <Typography level="title-md" data-testid="analysis-diagram-title"> - {props.analysisDiagram.title} - </Typography> + <Tooltip title={props.analysisDiagram.title}> + <Typography + sx={{ + height: "1.5rem", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + paddingRight: "1rem", + }} + level="title-md" + data-testid="analysis-diagram-title" + > + {props.analysisDiagram.title} + </Typography> + </Tooltip> <Stack direction="row" gap={1}> <IconButton aria-label="Im Vollbildmodus anzeigen" diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisDiagramBox.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisDiagramBox.tsx index 7a867a42f..fa81eb4c8 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisDiagramBox.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/AnalysisAccordion/AnalysisDiagramBox.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Box, Divider, Stack, Typography } from "@mui/joy"; +import { Box, Button, Divider, Stack, Typography } from "@mui/joy"; import { ReactNode } from "react"; interface AnalysisDiagramProps { @@ -13,6 +13,7 @@ interface AnalysisDiagramProps { evaluatedDataAmountTotal: number; header?: ReactNode; getChart: () => ReactNode; + onShowMoreDescription?: () => void; } export function AnalysisDiagramBox({ @@ -22,6 +23,7 @@ export function AnalysisDiagramBox({ evaluatedDataAmountTotal, header, getChart, + onShowMoreDescription, }: AnalysisDiagramProps) { function diagramContent() { if (evaluatedDataAmount === 0) { @@ -56,11 +58,12 @@ export function AnalysisDiagramBox({ > {header} {diagramContent()} - <Stack gap={2} marginTop={2}> + <Stack gap={2}> <Divider /> - <Typography level="body-md" data-testid="analysis-diagram-description"> - {description} - </Typography> + <Description + description={description} + onShowMoreDescription={onShowMoreDescription} + /> <Stack gap={0.5}> <Typography level="body-xs" @@ -81,3 +84,45 @@ export function AnalysisDiagramBox({ </Stack> ); } + +function Description(props: { + description: string | undefined; + onShowMoreDescription?: () => void; +}) { + if (props.onShowMoreDescription) { + const MAX_DESCRIPTION_LENGTH = 95; // Determined through testing + const showMore = (props.description?.length ?? 0) > MAX_DESCRIPTION_LENGTH; + + return ( + <Typography + sx={{ height: "4rem" }} + level="body-md" + data-testid="analysis-diagram-description" + > + {props.description?.slice(0, MAX_DESCRIPTION_LENGTH)} + {showMore && "..."} + {showMore && ( + <Button + variant="plain" + onClick={() => props.onShowMoreDescription!()} + > + Mehr anzeigen + </Button> + )} + </Typography> + ); + } else { + return ( + <Typography + sx={{ + height: "10rem", + overflowY: "scroll", + }} + level="body-md" + data-testid="analysis-diagram-description" + > + {props.description} + </Typography> + ); + } +} diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/BarChart.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/BarChart.tsx index bc0dacf56..8fb440f23 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/BarChart.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/BarChart.tsx @@ -18,7 +18,7 @@ import { } from "@/lib/businessModules/statistics/components/shared/charts/EChart"; import { calculateRelativeFormatting, - formatBreakLongStringOnce, + formatChartLabel, } from "@/lib/businessModules/statistics/components/shared/charts/dataHelper"; export interface BarChartProps { @@ -141,20 +141,18 @@ export function BarChart(props: BarChartProps) { type: "category", data: series.labels, axisLabel: { - ...(props.orientation === "VERTICAL" && - props.type !== DiagramType.HISTOGRAM_CHART - ? { - hideOverlap: false, - interval: 0, - width: 100, - overflow: "break", - } - : {}), - ...(props.orientation === "HORIZONTAL" - ? { - formatter: formatBreakLongStringOnce, - } - : {}), + formatter: (text: string) => { + if (props.orientation === "VERTICAL") { + return formatChartLabel(text, 100); + } + return formatChartLabel(text, 330); + }, + hideOverlap: false, + interval: + props.orientation === "VERTICAL" && + props.type !== DiagramType.HISTOGRAM_CHART + ? 0 + : undefined, }, axisLine: { show: props.type === DiagramType.HISTOGRAM_CHART, diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/LineChart.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/LineChart.tsx index 15571e0f2..f5c4b7561 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/LineChart.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/LineChart.tsx @@ -5,10 +5,7 @@ import { EChartsOption, SeriesOption } from "echarts"; -import { - AnalysisDiagramLineChart, - AnalysisLineDiagramConfiguration, -} from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; +import { AnalysisDiagramLineChart } from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; import { ChartApi, EChart, @@ -18,12 +15,11 @@ import { mapAxisTitleWithOptionalUnit, } from "@/lib/businessModules/statistics/components/shared/charts/dataHelper"; +import { NumericAxesConfiguration } from "./types"; + interface LineChartDiagramProps { diagramData: AnalysisDiagramLineChart["data"]; - configuration: Pick< - AnalysisLineDiagramConfiguration, - "axisRange" | "xAttribute" | "yAttribute" - >; + configuration: NumericAxesConfiguration; eChartApi?: (eChartApi: ChartApi) => void; } diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx index 566f208ff..e898cbac9 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/ScatterChart.tsx @@ -6,10 +6,7 @@ import { EChartsOption, SeriesOption } from "echarts"; import { unique } from "remeda"; -import { - AnalysisDiagramScatterChart, - AnalysisScatterDiagramConfiguration, -} from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; +import { AnalysisDiagramScatterChart } from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; import { ChartApi, EChart, @@ -19,12 +16,11 @@ import { mapAxisTitleWithOptionalUnit, } from "@/lib/businessModules/statistics/components/shared/charts/dataHelper"; +import { NumericAxesConfiguration } from "./types"; + interface ScatterChartDiagramProps { diagramData: AnalysisDiagramScatterChart["data"]; - configuration: Pick< - AnalysisScatterDiagramConfiguration, - "axisRange" | "xAttribute" | "yAttribute" - >; + configuration: NumericAxesConfiguration; eChartApi?: (eChartApi: ChartApi) => void; } diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/dataHelper.ts b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/dataHelper.ts index 068927763..9f8304c17 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/dataHelper.ts +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/dataHelper.ts @@ -9,7 +9,8 @@ import { AnalysisDiagramLineChart, AnalysisDiagramScatterChart, } from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; -import { FlatAttribute } from "@/lib/businessModules/statistics/api/models/flatAttribute"; + +import { XYAxes } from "./types"; export function calculateXYMinMax( filterSet: @@ -25,24 +26,118 @@ export function calculateXYMinMax( return [xMin, xMax, yMin, yMax]; } -export function formatBreakLongStringOnce(input: string) { - if (input.length <= 30) { - return input; +function getTextWidth(text: string) { + const charWidths = new Map<string, number>([ + ["f", 4], + ["i", 3], + ["j", 4], + ["l", 3], + ["m", 11], + ["r", 4], + ["t", 4], + ["w", 11], + ["A", 8], + ["B", 8], + ["C", 9], + ["D", 9], + ["E", 8], + ["F", 8], + ["G", 9], + ["H", 9], + ["I", 4], + ["J", 5], + ["K", 8], + ["L", 7], + ["M", 11], + ["N", 9], + ["O", 9], + ["P", 8], + ["Q", 9], + ["R", 8], + ["S", 8], + ["T", 8], + ["U", 9], + ["V", 8], + ["W", 11], + ["X", 8], + ["Y", 8], + ["Z", 8], + [" ", 3], + [".", 3], + [",", 3], + ["!", 3], + ["?", 6], + ["-", 4], + ["1", 4], + ]); + + return text + .split("") + .reduce((acc, character) => acc + (charWidths.get(character) ?? 7), 0); +} + +function splitWordEqually(word: string, maxWidth: number) { + // Determine Split point + const averageCharsFittingWidth = Math.floor( + maxWidth / (getTextWidth(word) / word.length), + ); + let amountOfSplits = 1; + for ( + ; + word.length / amountOfSplits > averageCharsFittingWidth; + ++amountOfSplits + ) {} + + // Split words + const splitInterval = Math.ceil(word.length / amountOfSplits); + const splitWords = []; + let splitIndex = 0; + for ( + ; + splitIndex + splitInterval < word.length; + splitIndex += splitInterval + ) { + splitWords.push(word.slice(splitIndex, splitIndex + splitInterval) + "-"); } + splitWords.push(word.slice(splitIndex)); + + return splitWords; +} - const textParts = input.split(" "); - const middle = Math.round(textParts.length / 2); - const topPart = textParts.slice(0, middle).join(" "); - const bottomPart = textParts.slice(middle).join(" "); - return topPart + "\n" + bottomPart; +export function formatChartLabel(text: string, maxWidth: number) { + return text + .split(/\s|(?<=-)/) + .flatMap((it) => { + if (getTextWidth(it) > maxWidth) { + return splitWordEqually(it, maxWidth); + } + return [it]; + }) + .reduce((acc, currentValue) => { + if ( + acc.length > 0 && + getTextWidth([...acc[acc.length - 1]!, currentValue].join(" ")) < + maxWidth + ) { + return [ + ...acc.slice(0, acc.length - 1), + [...acc[acc.length - 1]!, currentValue], + ]; + } + return [...acc, [currentValue]]; + }, [] as string[][]) + .map((it) => it.join(" ")) + .join("\n") + .replaceAll("- ", "-"); } -export function mapAxisTitleWithOptionalUnit(attribute: FlatAttribute) { - return (attribute.type === "DecimalAttribute" || - attribute.type === "IntegerAttribute") && +export function mapAxisTitleWithOptionalUnit(attribute: XYAxes) { + return formatChartLabel( isDefined(attribute.unit) - ? `${formatBreakLongStringOnce(attribute.name)} [${attribute.unit}]` - : formatBreakLongStringOnce(attribute.name); + ? `${attribute.name} [${attribute.unit}]` + : attribute.name, + 300, + ); } export function calculateRelativeFormatting(value: number): string { diff --git a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/types.ts b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/types.ts index 1dd11f3ff..715c00ca5 100644 --- a/employee-portal/src/lib/businessModules/statistics/components/shared/charts/types.ts +++ b/employee-portal/src/lib/businessModules/statistics/components/shared/charts/types.ts @@ -3,7 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiBaseModuleIdAttributeAllOfBaseAttribute } from "@eshg/statistics-api"; +import { + ApiBaseModuleIdAttributeAllOfBaseAttribute, + ApiDecimalAttribute, + ApiIntegerAttribute, +} from "@eshg/statistics-api"; + +import { DiagramAxisRange } from "@/lib/businessModules/statistics/api/models/evaluationDetailsViewTypes"; export const ImageType = { SVG: "svg", @@ -13,3 +19,14 @@ export const ImageType = { export type ImageType = (typeof ImageType)[keyof typeof ImageType]; export type AttributeType = ApiBaseModuleIdAttributeAllOfBaseAttribute["type"]; + +export type XYAxes = Pick< + ApiIntegerAttribute & ApiDecimalAttribute, + "name" | "unit" +>; + +export interface NumericAxesConfiguration { + axisRange: DiagramAxisRange; + xAttribute: XYAxes; + yAttribute: XYAxes; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/clients.ts b/employee-portal/src/lib/businessModules/stiProtection/api/clients.ts index 166014d4c..2b3336b28 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/clients.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/clients.ts @@ -7,6 +7,7 @@ import { Configuration as BaseConfiguration, CitizenAccessCodeUserApi, } from "@eshg/base-api"; +import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; import { AppointmentBlockApi, AppointmentTypeApi, @@ -22,9 +23,9 @@ import { ProcedureApi, ProgressEntryApi, StiProtectionProcedureApi, + TextTemplateApi, WaitingRoomApi, -} from "@eshg/employee-portal-api/stiProtection"; -import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/sti-protection-api"; function useConfiguration() { const configParameters = useApiConfiguration( @@ -114,3 +115,8 @@ export function useDiagnosisApi() { const config = useConfiguration(); return new DiagnosisApi(config); } + +export function useTextTemplateApi() { + const config = useConfiguration(); + return new TextTemplateApi(config); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentBlockGroup.ts b/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentBlockGroup.ts index 1e8cdf7de..99106e149 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentBlockGroup.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentBlockGroup.ts @@ -3,16 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiGetAppointmentBlock, - ApiGetAppointmentBlockGroup, -} from "@eshg/employee-portal-api/stiProtection"; import { BaseEntity, mapBaseEntity, } from "@eshg/lib-employee-portal/api/models/BaseEntity"; import { assertNonEmptyArray } from "@eshg/lib-portal/helpers/assertions"; +import { + ApiAppointmentType, + ApiGetAppointmentBlock, + ApiGetAppointmentBlockGroup, +} from "@eshg/sti-protection-api"; import { first, last, sumBy } from "remeda"; export interface AppointmentBlockStiProtection extends BaseEntity { diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentTypeConfig.ts b/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentTypeConfig.ts index 9ac9d5649..c8e506b05 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentTypeConfig.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/models/AppointmentTypeConfig.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiAppointmentTypeConfig, -} from "@eshg/employee-portal-api/stiProtection"; import { BaseEntity, mapBaseEntity, } from "@eshg/lib-employee-portal/api/models/BaseEntity"; +import { + ApiAppointmentType, + ApiAppointmentTypeConfig, +} from "@eshg/sti-protection-api"; export interface AppointmentTypeConfig extends BaseEntity { appointmentTypeDto: ApiAppointmentType; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentBlocks.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentBlocks.ts index 0a5c5d507..5bc231e0a 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentBlocks.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentBlocks.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiCreateAppointmentBlockGroupResponse, ApiCreateDailyAppointmentBlockGroupRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/sti-protection-api"; import { MutationOptions, useMutation } from "@tanstack/react-query"; import { useAppointmentBlockApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentTypes.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentTypes.ts index cd7257f6a..65164c0c6 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentTypes.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/appointmentTypes.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { ApiAppointmentTypeConfig, ApiUpdateAppointmentTypeRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +} from "@eshg/sti-protection-api"; import { useAppointmentTypeApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/consultation.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/consultation.ts index bbb05a1ce..27f433a3e 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/consultation.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/consultation.ts @@ -3,22 +3,53 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiConsultation } from "@eshg/employee-portal-api/stiProtection"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { MutationPassThrough } from "@eshg/lib-portal/types/query"; -import { useMutation } from "@tanstack/react-query"; +import { ApiConsultation } from "@eshg/sti-protection-api"; +import { MutationOptions, useMutation } from "@tanstack/react-query"; import { useConsultationApi } from "@/lib/businessModules/stiProtection/api/clients"; import { proceduresQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; -export function useUpsertConsultation( - procedureId: string, - passThrough: MutationPassThrough<ApiConsultation, void>, -) { - const api = useConsultationApi(); - return useMutation({ - mutationFn: (consultation) => - api.updateConsultation(procedureId, consultation), +interface UpsertConsultationParams { + consultation: ApiConsultation; +} + +export function useUpsertConsultationOptions({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertConsultationParams, void>; +}): MutationOptions<void, Error, UpsertConsultationParams> { + const consultationApi = useConsultationApi(); + const snackbar = useSnackbar(); + + return { + mutationFn: ({ consultation }: UpsertConsultationParams) => + consultationApi.updateConsultation(procedureId, consultation), mutationKey: proceduresQueryKey([procedureId, "consultation"]), - ...passThrough, - }); + ...(passThrough ?? { + onSuccess: () => { + snackbar.confirmation( + "Die Konsultation wurde erfolgreich gespeichert.", + ); + }, + onError: () => { + snackbar.error("Die Konsultation konnte nicht gespeichert werden."); + }, + }), + }; +} + +export function useUpsertConsultation({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertConsultationParams, void>; +}) { + const options = useUpsertConsultationOptions({ procedureId, passThrough }); + + return useMutation(options); } diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/diagnosis.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/diagnosis.ts index ebe674a35..54e61af91 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/diagnosis.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/diagnosis.ts @@ -3,22 +3,51 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiDiagnosis } from "@eshg/employee-portal-api/stiProtection"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { MutationPassThrough } from "@eshg/lib-portal/types/query"; -import { useMutation } from "@tanstack/react-query"; +import { ApiDiagnosis } from "@eshg/sti-protection-api"; +import { MutationOptions, useMutation } from "@tanstack/react-query"; import { useDiagnosisApi } from "@/lib/businessModules/stiProtection/api/clients"; import { proceduresQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; -export function useUpsertDiagnosis( - procedureId: string, - passThrough: MutationPassThrough<ApiDiagnosis, void>, -) { - const api = useDiagnosisApi(); - return useMutation({ - mutationFn: (consultation) => - api.updateDiagnosis(procedureId, consultation), +interface UpsertDiagnosisParams { + diagnosis: ApiDiagnosis; +} + +export function useUpsertDiagnosisOptions({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertDiagnosisParams, void>; +}): MutationOptions<void, Error, UpsertDiagnosisParams> { + const diagnosisApi = useDiagnosisApi(); + const snackbar = useSnackbar(); + + return { + mutationFn: ({ diagnosis }: UpsertDiagnosisParams) => + diagnosisApi.updateDiagnosis(procedureId, diagnosis), mutationKey: proceduresQueryKey([procedureId, "diagnosis"]), - ...passThrough, - }); + ...(passThrough ?? { + onSuccess: () => { + snackbar.confirmation("Die Diagnose wurde erfolgreich gespeichert."); + }, + onError: () => { + snackbar.error("Die Diagnose konnte nicht gespeichert werden."); + }, + }), + }; +} + +export function useUpsertDiagnosis({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertDiagnosisParams, void>; +}) { + const options = useUpsertDiagnosisOptions({ procedureId, passThrough }); + + return useMutation(options); } diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/examination.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/examination.ts index d0c36ed54..fddc543b4 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/examination.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/examination.ts @@ -3,53 +3,100 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { MutationPassThrough } from "@eshg/lib-portal/types/query"; import { ApiLaboratoryTestExamination, ApiRapidTestExamination, -} from "@eshg/employee-portal-api/stiProtection"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { useMutation } from "@tanstack/react-query"; +} from "@eshg/sti-protection-api"; +import { MutationOptions, useMutation } from "@tanstack/react-query"; import { useExaminationApi } from "@/lib/businessModules/stiProtection/api/clients"; import { proceduresQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; -export function useUpsertRapidTest(procedureId: string) { +interface UpsertRapidTestParams { + rapidTests: ApiRapidTestExamination; +} + +export function useUpsertRapidTestOptions({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertRapidTestParams, void>; +}): MutationOptions<void, Error, UpsertRapidTestParams> { const examinationApi = useExaminationApi(); const snackbar = useSnackbar(); - return useMutation({ - mutationFn: ({ rapidTests }: { rapidTests: ApiRapidTestExamination }) => + return { + mutationFn: ({ rapidTests }: UpsertRapidTestParams) => examinationApi.updateRapidTestExamination(procedureId, rapidTests), - onSuccess: () => { - snackbar.confirmation("Die Schnelltests wurden erfolgreich gespeichert."); - }, - onError: () => { - snackbar.error("Die Schnelltests konnten nicht gespeichert werden."); - }, mutationKey: proceduresQueryKey([procedureId, "rapidTests"]), - }); + ...(passThrough ?? { + onSuccess: () => { + snackbar.confirmation( + "Die Schnelltests wurden erfolgreich gespeichert.", + ); + }, + onError: () => { + snackbar.error("Die Schnelltests konnten nicht gespeichert werden."); + }, + }), + }; +} + +export function useUpsertRapidTests({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertRapidTestParams, void>; +}) { + const options = useUpsertRapidTestOptions({ procedureId, passThrough }); + + return useMutation(options); +} + +interface UpsertLaboratoryTestParams { + laboratoryTests: ApiLaboratoryTestExamination; } -export function useUpsertLaboratoryTest(procedureId: string) { +export function useUpsertLaboratoryTestOptions({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertLaboratoryTestParams, void>; +}): MutationOptions<void, Error, UpsertLaboratoryTestParams> { const examinationApi = useExaminationApi(); const snackbar = useSnackbar(); - return useMutation({ - mutationFn: ({ - laboratoryTests, - }: { - laboratoryTests: ApiLaboratoryTestExamination; - }) => + return { + mutationFn: ({ laboratoryTests }: UpsertLaboratoryTestParams) => examinationApi.updateLaboratoryTestExamination( procedureId, laboratoryTests, ), - onSuccess: () => { - snackbar.confirmation("Die Labortests wurden erfolgreich gespeichert."); - }, - onError: () => { - snackbar.error("Die Labortests konnten nicht gespeichert werden."); - }, mutationKey: proceduresQueryKey([procedureId, "laboratoryTests"]), - }); + ...(passThrough ?? { + onSuccess: () => { + snackbar.confirmation("Die Labortests wurden erfolgreich gespeichert."); + }, + onError: () => { + snackbar.error("Die Labortests konnten nicht gespeichert werden."); + }, + }), + }; +} + +export function useUpsertLaboratoryTest({ + procedureId, + passThrough, +}: { + procedureId: string; + passThrough?: MutationPassThrough<UpsertLaboratoryTestParams, void>; +}) { + const options = useUpsertLaboratoryTestOptions({ procedureId, passThrough }); + + return useMutation(options); } diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/medicalHistory.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/medicalHistory.ts index 9102afeac..d7fee90ea 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/medicalHistory.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/medicalHistory.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiCreateMedicalHistoryRequest } from "@eshg/employee-portal-api/stiProtection"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiCreateMedicalHistoryRequest } from "@eshg/sti-protection-api"; import { MutationOptions, useMutation } from "@tanstack/react-query"; import { useMedicalHistoryApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts index ae3cb1f97..66307a664 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/procedures.ts @@ -3,17 +3,19 @@ * 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 { MutationPassThrough } from "@eshg/lib-portal/types/query"; import { ApiCreateAppointmentRequest, + ApiCreateFollowUpProcedureRequest, + ApiCreateFollowUpProcedureResponse, ApiCreateProcedureRequest, ApiCreateProcedureResponse, ApiUpdateAppointmentRequest, ApiUpdatePersonDetailsRequest, CancelAppointmentRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { MutationPassThrough } from "@eshg/lib-portal/types/query"; +} from "@eshg/sti-protection-api"; import { MutationOptions } from "@tanstack/react-query"; import { useStiProtectionProcedureApi } from "@/lib/businessModules/stiProtection/api/clients"; @@ -46,13 +48,8 @@ export function useCreateStiProcedureMutation({ ApiCreateProcedureRequest, ApiCreateProcedureResponse > = {}) { - const api = useStiProtectionProcedureApi(); - return useHandledMutation({ - mutationFn: (data: ApiCreateProcedureRequest) => api.createProcedure(data), - mutationKey: stiProtectionApiQueryKey(["procedures"]), - onSuccess, - onError, - }); + const options = useCreateStiProcedureOptions({ onSuccess, onError }); + return useHandledMutation(options); } export function useCloseProcedureMutation({ @@ -109,6 +106,43 @@ export function useReopenProcedure({ }); } +interface CreateFollowUpProcedureParams { + id: string; + data: ApiCreateFollowUpProcedureRequest; +} + +export function useCreateStiFollowUpProcedureOptions({ + onSuccess, + onError, +}: MutationPassThrough< + CreateFollowUpProcedureParams, + ApiCreateFollowUpProcedureResponse +> = {}): MutationOptions< + ApiCreateFollowUpProcedureResponse, + Error, + CreateFollowUpProcedureParams +> { + const api = useStiProtectionProcedureApi(); + return { + mutationFn: ({ id, data }: CreateFollowUpProcedureParams) => + api.createFollowUpProcedure(id, data), + mutationKey: stiProtectionApiQueryKey(["procedures"]), + onSuccess, + onError, + }; +} + +export function useCreateStiFollowUpProcedureMutation({ + onSuccess, + onError, +}: MutationPassThrough< + CreateFollowUpProcedureParams, + ApiCreateFollowUpProcedureResponse +> = {}) { + const options = useCreateStiFollowUpProcedureOptions({ onSuccess, onError }); + return useHandledMutation(options); +} + interface UpdatePersonDetailsParams { id: string; data: ApiUpdatePersonDetailsRequest; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/textTemplates.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/textTemplates.ts new file mode 100644 index 000000000..d8869effd --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/textTemplates.ts @@ -0,0 +1,56 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +import { + ApiCreateTextTemplateRequest, + ApiCreateTextTemplateResponse, + ApiTextTemplate, +} from "@eshg/sti-protection-api"; + +import { useTextTemplateApi } from "@/lib/businessModules/stiProtection/api/clients"; + +import { MutationPassThrough } from "./types"; + +export function useCreateTextTemplate({ + onSuccess, + onError, +}: MutationPassThrough< + ApiCreateTextTemplateResponse, + ApiCreateTextTemplateRequest +> = {}) { + const appointmentTypeApi = useTextTemplateApi(); + return useHandledMutation({ + mutationFn: (template: ApiCreateTextTemplateRequest) => + appointmentTypeApi.createTextTemplate(template), + onSuccess, + onError, + }); +} + +export function useUpdateTextTemplate({ + onSuccess, + onError, +}: MutationPassThrough<void, ApiTextTemplate> = {}) { + const appointmentTypeApi = useTextTemplateApi(); + return useHandledMutation({ + mutationFn: (template: ApiTextTemplate) => + appointmentTypeApi.updateTextTemplate(template.externalId, template), + onSuccess, + onError, + }); +} + +export function useDeleteTextTemplate({ + onSuccess, + onError, +}: MutationPassThrough<void, string> = {}) { + const appointmentTypeApi = useTextTemplateApi(); + return useHandledMutation({ + mutationFn: (id: string) => appointmentTypeApi.deleteTextTemplate(id), + onSuccess, + onError, + }); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/waitingRoomApi.ts b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/waitingRoomApi.ts index 339adb52e..0cba56591 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/mutations/waitingRoomApi.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/mutations/waitingRoomApi.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { UpdateWaitingRoomDetailsRequest } from "@eshg/employee-portal-api/stiProtection"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { MutationPassThrough } from "@eshg/lib-portal/types/query"; +import { UpdateWaitingRoomDetailsRequest } from "@eshg/sti-protection-api"; import { useWaitingRoomApi } from "@/lib/businessModules/stiProtection/api/clients"; import { stiProtectionApiQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/apiQueryKeys.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/apiQueryKeys.ts index 8d3cb0b72..14c93c93b 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/apiQueryKeys.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/apiQueryKeys.ts @@ -34,3 +34,6 @@ export const appointmentTypesApiQueryKey = queryKeyFactory( export const archivingApiQueryKey = queryKeyFactory( stiProtectionApiQueryKey(["archivingApi"]), ); +export const textTemplateApiQueryKey = queryKeyFactory( + stiProtectionApiQueryKey(["textTemplateApi"]), +); diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentBlocks.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentBlocks.ts index 6fc5e17e6..dec5d998f 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentBlocks.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentBlocks.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { mapPaginatedList } from "@eshg/lib-employee-portal/api/models/PaginatedList"; +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { ApiAppointmentType, ApiCreateDailyAppointmentBlockGroupRequest, GetAppointmentBlockGroupsRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { mapPaginatedList } from "@eshg/lib-employee-portal/api/models/PaginatedList"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/sti-protection-api"; import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; import { useAppointmentBlockApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentTypes.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentTypes.ts index 8778af79d..db5c280e5 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentTypes.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/appointmentTypes.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { AppointmentTypeApi } from "@eshg/employee-portal-api/stiProtection"; +import { AppointmentTypeApi } from "@eshg/sti-protection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useAppointmentTypeApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/archiving.ts index 7db3c0997..d3b44501b 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/stiProtection/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/identity.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/identity.ts index 88a58efa2..1ca685d00 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/identity.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/identity.ts @@ -5,6 +5,7 @@ "use client"; +import { STATIC_QUERY_OPTIONS } from "@eshg/lib-portal/api/queryOptions"; import { useQuery } from "@tanstack/react-query"; import { useStiProtectionProcedureApi } from "@/lib/businessModules/stiProtection/api/clients"; @@ -23,5 +24,6 @@ export function usePinCheck(procedureId: string, pin: string | undefined) { }, queryKey: ["pin-validation", procedureId, pin], enabled: pin != null, + staleTime: STATIC_QUERY_OPTIONS.staleTime, }); } diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistoryDocument.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistoryDocument.ts index 40897ec4e..5c96c1c47 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistoryDocument.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/medicalHistoryDocument.ts @@ -6,7 +6,7 @@ import { ApiConcern, MedicalHistoryDocumentApi, -} from "@eshg/employee-portal-api/stiProtection"; +} from "@eshg/sti-protection-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useMedicalHistoryDocumentApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/procedures.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/procedures.ts index b34bd052c..0b8e0cdc9 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/procedures.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/procedures.ts @@ -5,12 +5,12 @@ "use client"; +import { useFileDownload } from "@eshg/lib-portal/api/files/download"; import { ApiGetStiProtectionProceduresSortBy, ApiGetStiProtectionProceduresSortOrder, ApiStiProtectionProcedureOverview, -} from "@eshg/employee-portal-api/stiProtection"; -import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +} from "@eshg/sti-protection-api"; import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { useStiProtectionProcedureApi } from "@/lib/businessModules/stiProtection/api/clients"; @@ -83,11 +83,9 @@ const SortByMap: Record< string, ApiGetStiProtectionProceduresSortBy | undefined > = { - yearOfBirth: ApiGetStiProtectionProceduresSortBy.YearOfBirth, - gender: ApiGetStiProtectionProceduresSortBy.Gender, - status: ApiGetStiProtectionProceduresSortBy.Status, - concern: ApiGetStiProtectionProceduresSortBy.Concern, createdAt: ApiGetStiProtectionProceduresSortBy.CreatedAt, + sampleBarCode: ApiGetStiProtectionProceduresSortBy.SampleBarcode, + appointmentStart: ApiGetStiProtectionProceduresSortBy.Appointment, } as const satisfies Partial< Record<ColumnNames, ApiGetStiProtectionProceduresSortBy> >; diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/textTemplates.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/textTemplates.ts new file mode 100644 index 000000000..5175b6d64 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/textTemplates.ts @@ -0,0 +1,28 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiTextTemplateContext } from "@eshg/sti-protection-api"; +import { + keepPreviousData, + queryOptions, + useSuspenseQuery, +} from "@tanstack/react-query"; + +import { useTextTemplateApi } from "@/lib/businessModules/stiProtection/api/clients"; +import { textTemplateApiQueryKey } from "@/lib/businessModules/stiProtection/api/queries/apiQueryKeys"; + +export function useTextTemplatesQuery(contexts?: ApiTextTemplateContext[]) { + const textTemplateApi = useTextTemplateApi(); + return queryOptions({ + queryKey: textTemplateApiQueryKey(["list", ...(contexts ?? [])]), + queryFn: () => textTemplateApi.getTextTemplates(new Set(contexts)), + select: (response) => response.textTemplates, + placeholderData: keepPreviousData, + }); +} + +export function useTextTemplates(contexts?: ApiTextTemplateContext[]) { + return useSuspenseQuery(useTextTemplatesQuery(contexts)); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/api/queries/waitingRoomApi.ts b/employee-portal/src/lib/businessModules/stiProtection/api/queries/waitingRoomApi.ts index 95beed7be..bec5144a5 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/api/queries/waitingRoomApi.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/api/queries/waitingRoomApi.ts @@ -6,7 +6,7 @@ import { ApiGetWaitingRoomProceduresResponse, GetWaitingRoomProceduresRequest, -} from "@eshg/employee-portal-api/stiProtection"; +} from "@eshg/sti-protection-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useWaitingRoomApi } from "@/lib/businessModules/stiProtection/api/clients"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx index 32c9dcae4..9fbc8d3b1 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupForm.tsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { ApiAppointmentType, ApiAppointmentTypeConfig, ApiUser, -} from "@eshg/employee-portal-api/stiProtection"; -import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; -import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +} from "@eshg/sti-protection-api"; import { Divider, Stack } from "@mui/joy"; import { Formik, FormikErrors } from "formik"; import { isDefined, isEmpty, mapToObj } from "remeda"; @@ -20,12 +19,12 @@ import { routes } from "@/lib/businessModules/stiProtection/shared/routes"; import { AppointmentBlockGroupValuesWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockFormWithDays"; import { AppointmentBlockGroupFields } from "@/lib/shared/components/appointmentBlocks/AppointmentBlockGroupFields"; import { AppointmentCountWithDays } from "@/lib/shared/components/appointmentBlocks/AppointmentCountWithDays"; +import { StaffUser } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffField"; import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection"; import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock"; import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { FormSheet } from "@/lib/shared/components/form/FormSheet"; -import { fullName } from "@/lib/shared/components/users/userFormatter"; import { validateFieldArray } from "@/lib/shared/helpers/validators"; import { mapFormValues } from "./CreateAppointmentBlockGroupForm"; @@ -87,10 +86,11 @@ function validateForm( return errors; } -function userToOption(user: ApiUser): SelectOption { +function userToOption(user: ApiUser): StaffUser { return { - value: user.userId, - label: fullName(user), + userId: user.userId, + firstName: user.firstName, + lastName: user.lastName, }; } diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx index 30f3e9674..7eda2793d 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/AppointmentBlockGroupsTable.tsx @@ -5,11 +5,11 @@ "use client"; +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { ApiAppointmentBlockSortKey, ApiAppointmentType, -} from "@eshg/employee-portal-api/stiProtection"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/sti-protection-api"; import { Chip } from "@mui/joy"; import { ColumnSort, Row, createColumnHelper } from "@tanstack/react-table"; import { ReactNode } from "react"; 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 1d8ca29b7..a37b9cb12 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/CreateAppointmentBlockGroupForm.tsx @@ -5,13 +5,13 @@ "use client"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { mapRequiredValue } from "@eshg/lib-portal/helpers/form"; import { ApiAppointmentType, ApiCreateDailyAppointmentBlock, ApiCreateDailyAppointmentBlockGroupRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { mapRequiredValue } from "@eshg/lib-portal/helpers/form"; +} from "@eshg/sti-protection-api"; import { useSuspenseQueries } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/options.ts b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/options.ts index 1de62f852..c12e28360 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/options.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentBlocks/options.ts @@ -3,12 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiConcern, -} from "@eshg/employee-portal-api/stiProtection"; import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; +import { ApiAppointmentType, ApiConcern } from "@eshg/sti-protection-api"; import { APPOINTMENT_TYPES } from "@/lib/businessModules/stiProtection/shared/constants"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeEditForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeEditForm.tsx index 176f49a57..eed6931f8 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeEditForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeEditForm.tsx @@ -5,9 +5,9 @@ "use client"; -import { ApiAppointmentType } from "@eshg/employee-portal-api/stiProtection"; import { BaseField } from "@eshg/lib-portal/components/formFields/BaseField"; import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; +import { ApiAppointmentType } from "@eshg/sti-protection-api"; import { FormLabel, Input, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeOverviewTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeOverviewTable.tsx index d329c0059..2515e4959 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeOverviewTable.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/AppointmentTypeOverviewTable.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiAppointmentTypeConfig } from "@eshg/employee-portal-api/stiProtection"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiAppointmentTypeConfig } from "@eshg/sti-protection-api"; import { FormikProps } from "formik"; import { useRef, useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/columns.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/columns.tsx index 3e8d5e0d3..d5dc6ec47 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/columns.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/appointmentTypes/columns.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiAppointmentTypeConfig } from "@eshg/employee-portal-api/stiProtection"; +import { ApiAppointmentTypeConfig } from "@eshg/sti-protection-api"; import { Edit } from "@mui/icons-material"; import { ColumnHelper, createColumnHelper } from "@tanstack/react-table"; 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 6e0dc14d3..0d659968e 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 @@ -5,10 +5,13 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; -import { ApiStiProtectionProcedureOverview } from "@eshg/employee-portal-api/stiProtection"; import { Row } from "@eshg/lib-portal/components/Row"; -import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { + formatDate, + formatDateTime, +} from "@eshg/lib-portal/formatters/dateTime"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; +import { ApiStiProtectionProcedureOverview } from "@eshg/sti-protection-api"; import { EditOutlined, ToggleOffOutlined } from "@mui/icons-material"; import { useSuspenseQueries } from "@tanstack/react-query"; import { ColumnSort, createColumnHelper } from "@tanstack/react-table"; @@ -22,6 +25,7 @@ import { import { CONCERN_VALUES, GENDER_VALUES, + LAB_STATUS_VALUES, PROCEDURE_STATUS_VALUES, } from "@/lib/businessModules/stiProtection/shared/constants"; import { isProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers"; @@ -54,10 +58,20 @@ function getProceduresColumns({ reopenDialog: UseCloseAndReopenConfirmationDialog; }) { return [ + columnHelper.accessor("accessCode", { + header: "Anmeldecode", + cell: ({ getValue }) => getValue(), + enableSorting: false, + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), columnHelper.accessor("yearOfBirth", { header: "Geburtsjahr", cell: ({ getValue }) => getValue(), - enableSorting: true, + enableSorting: false, meta: { canNavigate: { parentRow: true, @@ -67,7 +81,7 @@ function getProceduresColumns({ columnHelper.accessor("gender", { header: "Geschlecht", cell: ({ getValue }) => GENDER_VALUES[getValue()], - enableSorting: true, + enableSorting: false, meta: { canNavigate: { parentRow: true, @@ -77,6 +91,16 @@ function getProceduresColumns({ columnHelper.accessor("status", { header: "Status", cell: ({ getValue }) => PROCEDURE_STATUS_VALUES[getValue()], + enableSorting: false, + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("appointmentStart", { + header: "Termin", + cell: ({ getValue }) => formatDateTime(getValue()), enableSorting: true, meta: { canNavigate: { @@ -87,7 +111,7 @@ function getProceduresColumns({ columnHelper.accessor("concern", { header: "Anliegen", cell: ({ getValue }) => CONCERN_VALUES[getValue()], - enableSorting: true, + enableSorting: false, meta: { canNavigate: { parentRow: true, @@ -95,7 +119,7 @@ function getProceduresColumns({ }, }), columnHelper.accessor("createdAt", { - header: "Erstell.", + header: "Erstellt", cell: ({ getValue }) => formatDate(getValue()), enableSorting: true, meta: { @@ -104,6 +128,26 @@ function getProceduresColumns({ }, }, }), + columnHelper.accessor("sampleBarCode", { + header: "Labor-Barcode", + cell: ({ getValue }) => getValue(), + enableSorting: true, + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("labStatus", { + header: "Laborstatus", + cell: ({ getValue }) => LAB_STATUS_VALUES[getValue()], + enableSorting: false, + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), columnHelper.display({ id: "actions", header: "Aktionen", diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateAccordion.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateAccordion.tsx index ebf2ffa17..e9e22b963 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateAccordion.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateAccordion.tsx @@ -17,16 +17,18 @@ import { useTransition } from "react"; import { AddTextTemplate } from "@/lib/shared/components/icons/AddTextTemplate"; -import { useTextTemplatesSidebar } from "./TextTemplatesSidebarProvider"; +import { AppendText } from "./TextTemplatesSidebar"; export function TextTemplateAccordion({ name, - text, + content, index, + appendText, }: { name: string; - text: string; + content: string; index: number; + appendText: AppendText; }) { return ( <Accordion @@ -45,10 +47,14 @@ export function TextTemplateAccordion({ > {name} </AccordionSummary> - <AddTextTemplateButton text={text} index={index} /> + <AddTextTemplateButton + text={content} + appendText={appendText} + index={index} + /> </Row> <AccordionDetails> - <Typography padding={2}>{text}</Typography> + <Typography padding={2}>{content}</Typography> </AccordionDetails> </Accordion> ); @@ -57,7 +63,7 @@ export function TextTemplateAccordion({ const AnimatedIconButton = styled(IconButton)(({ theme }) => ({ transition: "all 300ms", "&[aria-disabled=true]": { - "background-color": theme.palette.success.plainActiveBg, + backgroundColor: theme.palette.success.plainActiveBg, borderColor: "transparent", cursor: "default", ".MuiSvgIcon-root": { color: theme.palette.success.outlinedColor }, @@ -67,12 +73,14 @@ const AnimatedIconButton = styled(IconButton)(({ theme }) => ({ function AddTextTemplateButton({ text, index, + appendText, }: { text: string; index: number; + appendText: AppendText; }) { const [isAppending, startAppending] = useTransition(); - const { appendText } = useTextTemplatesSidebar(); + // const { appendText } = useTextTemplatesSidebar(); function onClick() { if (isAppending) { @@ -87,6 +95,7 @@ function AddTextTemplateButton({ return ( <AnimatedIconButton aria-keyshortcuts={`${shortcut}`} + color={"primary"} variant={"outlined"} aria-disabled={isAppending} title={`Vorlage einfügen${shortcut ? ` (${shortcut})` : ""}`} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateEditSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateEditSidebar.tsx new file mode 100644 index 000000000..102c0e879 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateEditSidebar.tsx @@ -0,0 +1,103 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; +import { + ApiCreateTextTemplateRequest, + ApiTextTemplate, + ApiTextTemplateContext, +} from "@eshg/sti-protection-api"; +import { Button } from "@mui/joy"; +import { Formik } from "formik"; +import { ReactNode } from "react"; + +import { ButtonBar } from "@/lib/shared/components/buttons/ButtonBar"; +import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; +import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar"; +import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; +import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; + +import { TextTemplateFields } from "./TextTemplateFields"; + +interface TextTemplateEditSidebarProps { + title: string; + isOpen: boolean; + onClose: () => void; +} + +interface TextTemplateEditSidebarCreateProps + extends TextTemplateEditSidebarProps { + onCreate: (values: ApiCreateTextTemplateRequest) => Promise<void>; +} + +interface TextTemplateEditSidebarEditProps + extends TextTemplateEditSidebarProps { + onUpdate: (values: ApiTextTemplate) => Promise<void>; + initialValues: ApiTextTemplate | undefined; +} + +interface InitialFormData { + content: string; + context: ApiTextTemplateContext | null; + name: string; +} +const defaultValues: InitialFormData = { + context: null, + content: "", + name: "", +}; + +export function TextTemplateEditSidebar( + props: TextTemplateEditSidebarCreateProps, +): ReactNode; +export function TextTemplateEditSidebar( + props: TextTemplateEditSidebarEditProps, +): ReactNode; +export function TextTemplateEditSidebar({ + title, + isOpen, + onClose, + ...props +}: + | TextTemplateEditSidebarCreateProps + | TextTemplateEditSidebarEditProps): ReactNode { + const initialValues = + "initialValues" in props ? props.initialValues : defaultValues; + + if (initialValues == null) { + return null; + } + + return ( + <Sidebar open={isOpen} onClose={onClose}> + <Formik + enableReinitialize + initialValues={initialValues} + onSubmit={async (values, helpers) => { + await ("onUpdate" in props + ? props.onUpdate(values as ApiTextTemplate) + : props.onCreate(values as ApiCreateTextTemplateRequest)); + helpers.resetForm(); + }} + > + <SidebarForm> + <SidebarContent title={title}> + <TextTemplateFields /> + </SidebarContent> + <SidebarActions> + <ButtonBar + left={ + <Button variant="plain" onClick={onClose}> + Abbrechen + </Button> + } + right={<SubmitButton submitting={false}>Speichern</SubmitButton>} + /> + </SidebarActions> + </SidebarForm> + </Formik> + </Sidebar> + ); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateFields.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateFields.tsx new file mode 100644 index 000000000..5578f323a --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplateFields.tsx @@ -0,0 +1,48 @@ +/** + * Copyright 2025 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 { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; +import { Stack } from "@mui/joy"; + +import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; + +import { TextTemplateContextOptions } from "./constants"; + +export function TextTemplateFields() { + return ( + <Stack gap={2}> + <InputField + label="Name" + name="name" + required="Bitte einen Namen angeben" + /> + <SelectField + label="Kontext" + name="context" + options={TextTemplateContextOptions} + required="Bitte einen Kontext auswählen" + /> + <TextareaField + label="Inhalt" + name="content" + required="Bitte Text eingeben" + placeholder="z.B. Wert-Name: $Eingabe" + /> + <Alert + color="primary" + message={ + <> + Das <strong>$</strong>-Zeichen kann als Platzhalter verwendet + werden. Nach dem Einfügen kann man zwischen den Platzhalter mit{" "} + <strong>Strg+Leertaste</strong> oder <strong>Strg+Eingabe</strong>{" "} + springen. + </> + } + /> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTable.tsx new file mode 100644 index 000000000..991b72864 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTable.tsx @@ -0,0 +1,128 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +"use client"; + +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { + ApiCreateTextTemplateRequest, + ApiTextTemplate, +} from "@eshg/sti-protection-api"; +import { Add } from "@mui/icons-material"; +import { Button, Stack } from "@mui/joy"; +import { useState } from "react"; + +import { + useCreateTextTemplate, + useDeleteTextTemplate, + useUpdateTextTemplate, +} from "@/lib/businessModules/stiProtection/api/mutations/textTemplates"; +import { useTextTemplates } from "@/lib/businessModules/stiProtection/api/queries/textTemplates"; +import { EmployeePortalConfirmationDialog } from "@/lib/shared/components/confirmationDialog/EmployeePortalConfirmationDialog"; +import { DataTable } from "@/lib/shared/components/table/DataTable"; +import { TableSheet } from "@/lib/shared/components/table/TableSheet"; +import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; + +import { TextTemplateEditSidebar } from "./TextTemplateEditSidebar"; +import { textTemplateColumns } from "./TextTemplatesOverviewTableColumns"; + +export function TextTemplatesOverviewTable() { + const [sidebarOpenId, setSidebarOpenId] = useSearchParam("edit", "string"); + const [isNewSidebarOpen, setNewSidebarOpen] = useSearchParam( + "new", + "boolean", + ); + const [confirmingDelete, setConfirmingDelete] = useState< + string | undefined + >(); + const snackbar = useSnackbar(); + + const addMutation = useCreateTextTemplate({ + onSuccess: () => { + snackbar.confirmation("Die Vorlage wurde erzeugt."); + }, + }); + const updateMutation = useUpdateTextTemplate({ + onSuccess: () => { + snackbar.confirmation("Der Vorlage wurde aktualisiert."); + }, + }); + const deleteMutation = useDeleteTextTemplate({ + onSuccess: () => { + snackbar.confirmation("Die Vorlage wurde gelöscht."); + }, + }); + + const { data: textTemplates } = useTextTemplates(); + const selectedTemplate = textTemplates.find( + (t) => t.externalId === sidebarOpenId, + ); + + async function onUpdate(data: ApiTextTemplate) { + await updateMutation.mutateAsync(data); + setSidebarOpenId(null); + } + + async function onCreate(data: ApiCreateTextTemplateRequest) { + await addMutation.mutateAsync(data); + setNewSidebarOpen(false); + } + + async function deleteTemplate() { + if (confirmingDelete == null) { + return; + } + await deleteMutation.mutateAsync(confirmingDelete); + setConfirmingDelete(undefined); + } + + return ( + <Stack gap={3}> + <Button + sx={{ alignSelf: "end" }} + startDecorator={<Add />} + onClick={() => setNewSidebarOpen(true)} + > + Vorlage hinzufügen + </Button> + <TableSheet aria-label="Tabelle der Textvorlagen"> + <DataTable + data={textTemplates} + columns={textTemplateColumns({ + onEdit: ({ externalId }) => setSidebarOpenId(externalId), + onDelete: ({ externalId }) => setConfirmingDelete(externalId), + })} + /> + </TableSheet> + <EmployeePortalConfirmationDialog + open={confirmingDelete != null} + title="Vorlage löschen?" + description="Möchten Sie die Vorlage “Rechtliche Grundlage†wirklich löschen? Die Aktion kann nicht rückgängig gemacht werden." + confirmLabel="Löschen" + color="danger" + onCancel={() => { + setConfirmingDelete(undefined); + }} + onClose={() => { + setConfirmingDelete(undefined); + }} + onConfirm={deleteTemplate} + /> + <TextTemplateEditSidebar + title={"Vorlage bearbeiten"} + isOpen={sidebarOpenId != null} + initialValues={selectedTemplate} + onClose={() => setSidebarOpenId(null)} + onUpdate={onUpdate} + /> + <TextTemplateEditSidebar + title={"Vorlage hinzufügen"} + isOpen={isNewSidebarOpen} + onCreate={onCreate} + onClose={() => setNewSidebarOpen(false)} + /> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTableColumns.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTableColumns.tsx new file mode 100644 index 000000000..794387166 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesOverviewTableColumns.tsx @@ -0,0 +1,78 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiTextTemplate } from "@eshg/sti-protection-api"; +import { Delete, Edit } from "@mui/icons-material"; +import { createColumnHelper } from "@tanstack/react-table"; + +import { ActionsMenu } from "@/lib/shared/components/buttons/ActionsMenu"; + +import { TextTemplateContextLabels } from "./constants"; + +interface RowActions { + onEdit: (t: ApiTextTemplate) => void; + onDelete: (t: ApiTextTemplate) => void; +} +export function textTemplateColumns({ onEdit, onDelete }: RowActions) { + const columnHelper = createColumnHelper<ApiTextTemplate>(); + return [ + columnHelper.accessor("name", { + header: "Name", + cell: (props) => props.getValue(), + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("context", { + header: "Kontext", + sortingFn: (a, b) => + TextTemplateContextLabels[a.original.context].localeCompare( + TextTemplateContextLabels[b.original.context], + ), + cell: (props) => TextTemplateContextLabels[props.getValue()], + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.accessor("content", { + header: "Inhalt", + cell: (props) => props.getValue(), + meta: { + canNavigate: { + parentRow: true, + }, + }, + }), + columnHelper.display({ + header: "Aktionen", + id: "navigationControl", + cell: (props) => ( + <ActionsMenu + actionItems={[ + { + label: "Bearbeiten", + startDecorator: <Edit />, + onClick: () => onEdit(props.row.original), + }, + { + label: "Löschen", + startDecorator: <Delete />, + onClick: () => onDelete(props.row.original), + color: "danger", + }, + ]} + /> + ), + meta: { + width: 96, + cellStyle: "button", + }, + }), + ]; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebar.tsx index d6c692d7e..6827017b4 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebar.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebar.tsx @@ -4,6 +4,7 @@ */ import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; +import { ApiTextTemplateContext } from "@eshg/sti-protection-api"; import { OpenInNew } from "@mui/icons-material"; import { AccordionGroup, @@ -13,26 +14,38 @@ import { Select, Stack, } from "@mui/joy"; -import { KeyboardEvent, useId, useRef } from "react"; +import { + MutableRefObject, + useCallback, + useEffect, + useId, + useRef, + useState, +} from "react"; +import { useTextTemplates } from "@/lib/businessModules/stiProtection/api/queries/textTemplates"; import { routes } from "@/lib/businessModules/stiProtection/shared/routes"; -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 { TextTemplateAccordion } from "./TextTemplateAccordion"; -import { useTextTemplatesSidebar } from "./TextTemplatesSidebarProvider"; -import { ExampleTextTemplates, TextTemplateContextOptions } from "./constants"; +import { TextTemplateContextOptions } from "./constants"; -export function TextTemplatesSidebar() { - const { isOpen, close, context } = useTextTemplatesSidebar(); - const accordionsRef = useRef<HTMLDivElement | null>(null); +export type AppendText = (text: string) => Promise<void> | undefined; +interface TextTemplatesSidebarProps { + onClose: () => void; + context: ApiTextTemplateContext; + appendTextRef: MutableRefObject<AppendText | null>; +} - const textTemplates = ExampleTextTemplates.filter( - (k) => k.context === context, - ); +export function TextTemplatesSidebar({ + onClose, + context, + appendTextRef, +}: TextTemplatesSidebarProps) { + const accordionsRef = useRef<HTMLDivElement | null>(null); - function onKeyDown(e: KeyboardEvent) { + const onKeyDown = useCallback((e: KeyboardEvent) => { const index = parseInt(e.key); if (isNaN(index)) { return; @@ -41,47 +54,84 @@ export function TextTemplatesSidebar() { `button[aria-keyshortcuts="${index}"]`, ) as HTMLElement | undefined; button?.click(); + }, []); + + function appendText(text: string) { + if (appendTextRef.current == null) { + return; + } + return appendTextRef.current(text); } + useEffect(() => { + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [onKeyDown]); + return ( - <div onKeyDown={onKeyDown}> - <Sidebar open={isOpen} onClose={close}> - <SidebarContent title={"Textvorlage einfügen"}> - <Stack gap={2}> - <ContextSelect /> - <AccordionGroup sx={{ gap: 1 }} ref={accordionsRef}> - <Divider /> - {textTemplates.map(({ name, text }, index) => ( - <TextTemplateAccordion - key={name} - name={name} - text={text} - index={index} - /> - ))} - </AccordionGroup> - <InternalLinkButton - href={routes.textTemplates} - sx={{ alignSelf: "start" }} - variant="plain" - endDecorator={<OpenInNew />} - > - Textvorlagen verwalten - </InternalLinkButton> - </Stack> - </SidebarContent> - <SidebarActions> - <Button onClick={close} sx={{ alignSelf: "end" }}> - Schließen - </Button> - </SidebarActions> - </Sidebar> - </div> + <> + <SidebarContent title={"Textvorlage einfügen"}> + <TextTemplatesSidebarContent + context={context} + appendText={appendText} + accordionsRef={accordionsRef} + /> + </SidebarContent> + <SidebarActions> + <Button onClick={onClose} sx={{ alignSelf: "end" }}> + Schließen + </Button> + </SidebarActions> + </> + ); +} +interface TextTemplatesSidebarContentProps + extends Pick<TextTemplatesSidebarProps, "context"> { + appendText: AppendText; + accordionsRef: MutableRefObject<HTMLDivElement | null>; +} +export function TextTemplatesSidebarContent({ + context: givenContext, + appendText, + accordionsRef, +}: TextTemplatesSidebarContentProps) { + const [context, setContext] = useState<ApiTextTemplateContext | null>( + givenContext, + ); + const filterContexts = context == null ? undefined : [context]; + const { data: textTemplates } = useTextTemplates(filterContexts); + return ( + <Stack gap={2}> + <ContextSelect context={context} setContext={setContext} /> + <AccordionGroup sx={{ gap: 1 }} ref={accordionsRef}> + <Divider /> + {textTemplates.map(({ name, content }, index) => ( + <TextTemplateAccordion + key={name} + name={name} + content={content} + index={index} + appendText={appendText} + /> + ))} + </AccordionGroup> + <InternalLinkButton + href={routes.textTemplates} + sx={{ alignSelf: "start" }} + variant="plain" + endDecorator={<OpenInNew />} + > + Textvorlagen verwalten + </InternalLinkButton> + </Stack> ); } -export function ContextSelect() { - const { context, setContext } = useTextTemplatesSidebar(); +interface ContextSelectProps { + context: ApiTextTemplateContext | null; + setContext: (c: ApiTextTemplateContext | null) => void; +} +export function ContextSelect({ context, setContext }: ContextSelectProps) { const buttonId = useId(); const labelId = useId(); return ( @@ -91,8 +141,13 @@ export function ContextSelect() { </label> <Select slotProps={{ button: { id: buttonId, "aria-labelledby": labelId } }} - value={context ?? null} - onChange={(_e, newValue) => setContext(newValue)} + value={context} + onChange={(_e, newValue) => { + if (!newValue) { + return; + } + setContext(newValue); + }} > {TextTemplateContextOptions.map((option, index) => ( <Option key={index} label={option.label} value={option.value}> diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebarProvider.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebarProvider.tsx deleted file mode 100644 index b37cf5d9d..000000000 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebarProvider.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { - MutableRefObject, - PropsWithChildren, - createContext, - useContext, - useState, -} from "react"; - -import { TextTemplatesSidebar } from "./TextTemplatesSidebar"; -import { ApiTextTemplateContext } from "./constants"; -import { UseFieldHandle } from "./useFieldHandle"; - -const TextTemplatesContext = createContext< - TextTemplatesSidebarContext | undefined ->(undefined); - -export function TextTemplatesSidebarProvider({ children }: PropsWithChildren) { - const [context, setContext] = useState<ApiTextTemplateContext | null>(null); - const [fieldHandleRef, setFieldHandleRef] = useState<FieldHandleRef>(); - - function open( - context: ApiTextTemplateContext, - setFieldValue: FieldHandleRef, - ) { - setContext(context); - setFieldHandleRef(setFieldValue); - } - function close() { - fieldHandleRef?.current?.finishEditing(); - setContext(null); - setFieldHandleRef(undefined); - } - async function appendText(text: string) { - if (fieldHandleRef?.current == null) { - throw Error("No referenced TextArea to append to"); - } - await fieldHandleRef.current.appendText(text); - } - - return ( - <TextTemplatesContext.Provider - value={{ - context, - setContext, - open, - close, - isOpen: context != null, - appendText, - }} - > - {children} - <TextTemplatesSidebar /> - </TextTemplatesContext.Provider> - ); -} -type FieldSetter = (text: string) => Promise<void>; -type FieldHandleRef = MutableRefObject<UseFieldHandle | null>; -export interface TextTemplatesSidebarContext { - isOpen: boolean; - context: ApiTextTemplateContext | null; - setContext: (context: ApiTextTemplateContext | null) => void; - open: ( - context: ApiTextTemplateContext, - setFieldValue: FieldHandleRef, - ) => void; - close: () => void; - appendText: FieldSetter; -} - -export function useTextTemplatesSidebar() { - const context = useContext(TextTemplatesContext); - if (!context) { - throw Error("No TextTemplatesProvider found."); - } - return context; -} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates.tsx index a02a67c35..8ab1ed2c7 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates.tsx @@ -3,8 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ApiTextTemplateContext } from "@eshg/sti-protection-api"; import { Add } from "@mui/icons-material"; -import { Button } from "@mui/joy"; +import { Button, styled } from "@mui/joy"; import { useFormikContext } from "formik"; import { KeyboardEvent, useRef } from "react"; @@ -14,13 +15,13 @@ import { TextareaFieldProps, } from "@/lib/shared/components/formFields/TextareaField"; -import { useTextTemplatesSidebar } from "./TextTemplatesSidebarProvider"; -import { ApiTextTemplateContext } from "./constants"; +import { AppendText, TextTemplatesSidebar } from "./TextTemplatesSidebar"; import { - UseFieldHandle, + appendText, nextInsertPoint, - useFieldHandle, -} from "./useFieldHandle"; + selectFirstPoint, +} from "./nextInsertPoint"; +import { useSidebarFromSearchParam } from "./useSidebarFromSearchParam"; export interface TextareaWithTextTemplatesProps extends TextareaFieldProps { context: ApiTextTemplateContext; @@ -30,17 +31,28 @@ export function TextareaFieldWithTextTemplates({ context, ...props }: TextareaWithTextTemplatesProps) { - const { open } = useTextTemplatesSidebar(); - const { getFieldMeta } = useFormikContext(); + const { setFieldValue, getFieldMeta } = useFormikContext(); const { value } = getFieldMeta(props.name); const ref = useRef<HTMLTextAreaElement | null>(null); - const setterRef = useRef<UseFieldHandle | null>(null); - setterRef.current = useFieldHandle({ name: props.name, ref }); + const appendTextRef = useRef<AppendText | null>(null); + appendTextRef.current = async (text) => { + await setFieldValue(props.name, appendText(text, value)); + }; - function setOpen() { - open(context, setterRef); - } + const { open } = useSidebarFromSearchParam({ + component: TextTemplatesSidebar, + paramName: "text-template", + paramValue: props.name, + props: { + context, + appendTextRef, + }, + afterClose() { + selectFirstPoint(ref.current); + }, + fallbackTitle: "Textvorlage einfügen", + }); function onKeyDown(e: KeyboardEvent<HTMLTextAreaElement>) { if (ref.current == null) { @@ -50,7 +62,7 @@ export function TextareaFieldWithTextTemplates({ if (e.code === "Space" && e.ctrlKey) { insertionPoint = nextInsertPoint(value, ref.current.selectionEnd ?? 0); if (insertionPoint == null) { - setOpen(); + open(); } } else if (e.code === "Enter" && e.ctrlKey) { insertionPoint = @@ -65,10 +77,9 @@ export function TextareaFieldWithTextTemplates({ ref.current.selectionStart = insertionPoint.start; ref.current.selectionEnd = insertionPoint.end; } - return ( <FieldSetColumn gap={1} alignItems="start"> - <TextareaField + <StyledTextarea {...props} slotProps={{ textarea: { ref, rows: 20, onKeyDownCapture: onKeyDown } }} /> @@ -76,11 +87,15 @@ export function TextareaFieldWithTextTemplates({ startDecorator={<Add />} aria-keyshortcuts="Control+Space" variant="plain" - onClick={setOpen} - title="Textvorlage Menü Öffnen (Strg+Leertaste)" + onClick={open} + title="Menü der Textvorlagen öffnen (Strg+Leertaste)" > Textvorlage einfügen </Button> </FieldSetColumn> ); } + +const StyledTextarea = styled(TextareaField)(() => ({ + width: "100%", +})); diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/constants.ts b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/constants.ts index 9e6a8f747..1bf10c3d3 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/constants.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/constants.ts @@ -3,106 +3,20 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -// TODO: Replace with OpenApi type when available -export enum ApiTextTemplateContext { - ConsultationReason = "CONSULTATION_REASON", - Consultation = "CONSULTATION", - RapidTests = "RAPID_TESTS", - Diagnosis = "DIAGNOSIS", - LaboratoryTests = "LABORATORY_TESTS", -} -export interface ApiTextTemplate { - name: string; - text: string; - context: ApiTextTemplateContext; -} +import { ApiTextTemplateContext } from "@eshg/sti-protection-api"; export const TextTemplateContextLabels = { [ApiTextTemplateContext.ConsultationReason]: "Konsultationsgrund", - [ApiTextTemplateContext.Consultation]: "Konsultation", - [ApiTextTemplateContext.RapidTests]: "Schnelltests", - [ApiTextTemplateContext.Diagnosis]: "Diagnose", - [ApiTextTemplateContext.LaboratoryTests]: "Labortests", + [ApiTextTemplateContext.ConsultationRemark]: + "Konsultation - Allgemeine Bemerkung", + [ApiTextTemplateContext.RapidTestsRemark]: + "Schnelltests - Allgemeine Bemerkung", + [ApiTextTemplateContext.DiagnosisResult]: "Diagnose - Ergebnisse", + [ApiTextTemplateContext.LaboratoryTestsRemark]: + "Labortests - Allgemeine Bemerkung", + [ApiTextTemplateContext.DiagnosisRemark]: "Diagnose - Allgemeine Bemerkung", } as const satisfies Record<ApiTextTemplateContext, string>; export const TextTemplateContextOptions = Object.entries( TextTemplateContextLabels, -).map(([key, value]) => ({ - label: value, - value: key as ApiTextTemplateContext, -})); - -export const ExampleTextTemplates = [ - { - name: "Genitale Infektionen ", - context: ApiTextTemplateContext.ConsultationReason, - text: `Nein, Mann! Ich will noch nicht gehen. -Ich will: $etwas`, - }, - { - name: "PAP", - context: ApiTextTemplateContext.ConsultationReason, - text: `PAP: $`, - }, - { - name: "Syphilis-seronarbe Bestätigungstest bei positiv", - context: ApiTextTemplateContext.ConsultationReason, - text: `Syphilis-werte: $`, - }, - { - name: "Gynäkologische Vorsorge", - context: ApiTextTemplateContext.Consultation, - text: `Vagina: $JaOderNein -Urinanalyse: $Uneindeutig`, - }, - { - name: "Konsultation - Standardvorlage 1", - context: ApiTextTemplateContext.RapidTests, - text: `Wie heißt du? $NAME -Was ist dein Quest? $QUEST -Wie hoch ist die Fluggeschwindigkeit einer unbeladenen Schwalbe? $AHHHHH`, - }, - { - name: "HIV-Bestätigungsdiagnostik ", - context: ApiTextTemplateContext.Diagnosis, - text: `HIV: $Nein`, - }, - { - name: "HIV & Syphilis Basis ", - context: ApiTextTemplateContext.LaboratoryTests, - text: `HIV & Syphilis: $`, - }, - { - name: "Genitale Infektionen ", - context: ApiTextTemplateContext.Consultation, - text: `Nein, Mann! Ich will noch nicht gehen. -Ich will: $etwas`, - }, - { - name: "PAP", - context: ApiTextTemplateContext.Consultation, - text: `PAP: $`, - }, - { - name: "Syphilis-seronarbe Bestätigungstest bei positiv", - context: ApiTextTemplateContext.Consultation, - text: `Syphilis-werte: $`, - }, - { - name: "Konsultation - Standardvorlage 1", - context: ApiTextTemplateContext.Consultation, - text: `Wer bist du? $NAME -Was ist dein Quest? $QUEST -Wie hoch ist die Fluggeschwindigkeit einer unbeladenen Schwalbe? $AHHHHH`, - }, - { - name: "HIV-Bestätigungsdiagnostik", - context: ApiTextTemplateContext.Consultation, - text: `HIV: $Nein`, - }, - { - name: "HIV & Syphilis Basis", - context: ApiTextTemplateContext.Consultation, - text: `HIV & Syphilis: $`, - }, -] as const satisfies ApiTextTemplate[]; +).map(([value, label]) => ({ label, value })); diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/nextInsertPoint.ts b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/nextInsertPoint.ts new file mode 100644 index 000000000..cfc9fd074 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/nextInsertPoint.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { isString } from "remeda"; + +const replacementRegex = /\$(\d|\w|_)*/; +export interface InsertPoint { + start: number; + end: number; +} +export function nextInsertPoint( + value: unknown, + startSearch: number, +): InsertPoint | undefined { + const currentValue = isString(value) ? value : ""; + const remainingText = currentValue.slice(startSearch); + const match = replacementRegex.exec(remainingText); + if (match?.index == null || match[0] == null) { + return; + } + const start = startSearch + match.index; + return { start, end: start + match[0].length }; +} + +export function selectFirstPoint(textarea: HTMLTextAreaElement | null) { + if (textarea == null) { + return; + } + textarea.focus(); + const firstPoint = nextInsertPoint(textarea.value, 0); + if (firstPoint) { + textarea.selectionStart = firstPoint.start; + textarea.selectionEnd = firstPoint.end; + } +} + +export function appendText(text: string, value: unknown) { + const currentValue = isString(value) ? value : ""; + if (currentValue === "") { + return text; + } + return currentValue + `\n` + text; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useFieldHandle.tsx b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useFieldHandle.tsx deleted file mode 100644 index b7953de16..000000000 --- a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useFieldHandle.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { useFormikContext } from "formik"; -import { MutableRefObject } from "react"; -import { isString } from "remeda"; - -export interface UseFieldHandle { - appendText: (text: string) => Promise<void>; - finishEditing: () => void; -} - -export function useFieldHandle({ - name, - ref, -}: { - name: string; - ref: MutableRefObject<HTMLTextAreaElement | null>; -}) { - const { setFieldValue, getFieldMeta } = useFormikContext(); - const { value } = getFieldMeta(name); - - return { - async appendText(text: string) { - const currentValue = isString(value) ? value : ""; - const newValue = currentValue + `\n` + text; - await setFieldValue(name, newValue.trim()); - }, - finishEditing() { - if (ref?.current == null) { - return; - } - ref.current.focus(); - const firstPoint = nextInsertPoint(value, 0); - if (firstPoint) { - ref.current.selectionStart = firstPoint.start; - ref.current.selectionEnd = firstPoint.end; - } - }, - }; -} - -const replacementRegex = /\$(\d|\w|_)*/; -export interface InsertPoint { - start: number; - end: number; -} -export function nextInsertPoint( - value: unknown, - startSearch: number, -): InsertPoint | undefined { - const currentValue = isString(value) ? value : ""; - const remainingText = currentValue.slice(startSearch); - const match = replacementRegex.exec(remainingText); - if (match?.index == null || match[0] == null) { - return; - } - const start = startSearch + match.index; - return { start, end: start + match[0].length }; -} diff --git a/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useSidebarFromSearchParam.ts b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useSidebarFromSearchParam.ts new file mode 100644 index 000000000..62a92b238 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/components/textTemplates/useSidebarFromSearchParam.ts @@ -0,0 +1,98 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useCallback, useEffect, useRef } from "react"; + +import { + DrawerOpenOptions, + DrawerProps, +} from "@/lib/shared/components/drawer/drawerContext"; +import { + UseSidebarResult, + useSidebar, +} from "@/lib/shared/components/drawer/useSidebar"; +import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; + +interface UseSidebarFromSearchParamOptionsBaseProps< + TSidebarProps extends DrawerProps, +> extends DrawerOpenOptions<TSidebarProps> { + paramName: string; + paramValue?: string; + afterClose?: () => void; +} + +type CustomProps<TSidebarProps extends DrawerProps> = + Omit<TSidebarProps, keyof DrawerProps> extends Record<string, never> + ? never + : Omit<TSidebarProps, keyof DrawerProps>; + +interface UseSidebarFromSearchParamOptionsCustomProps< + TSidebarProps extends DrawerProps, +> extends UseSidebarFromSearchParamOptionsBaseProps<TSidebarProps> { + props: CustomProps<TSidebarProps>; +} + +interface UseSidebarFromSearchParamResult + extends Omit<UseSidebarResult<DrawerProps>, "open"> { + open: () => void; +} + +type UseSidebarFromSearchParamOptionsProps<TSidebarProps extends DrawerProps> = + Omit<TSidebarProps, keyof DrawerProps> extends Record<string, never> + ? UseSidebarFromSearchParamOptionsBaseProps<DrawerProps> + : UseSidebarFromSearchParamOptionsCustomProps<TSidebarProps>; + +export function useSidebarFromSearchParam<TSidebarProps extends DrawerProps>({ + paramName, + paramValue, + afterClose, + ...options +}: UseSidebarFromSearchParamOptionsProps<TSidebarProps>): UseSidebarFromSearchParamResult { + const [openName, setOpenName] = useSearchParam(paramName); + + const sidebar = useSidebar(options); + // Sidebar is a new object every re-render + const sidebarRef = useRef(sidebar); + sidebarRef.current = sidebar; + + const isOpen = paramValue ? openName === paramValue : !!openName; + const givenProps = + "props" in options + ? (options.props as CustomProps<TSidebarProps>) + : undefined; + const propsRef = useRef(givenProps); + propsRef.current = givenProps; + + const open = useCallback(() => { + setOpenName(paramValue ?? true); + if (propsRef.current) { + sidebarRef.current.open(propsRef.current); + } else { + (sidebarRef.current as { open: () => void }).open(); + } + }, [paramValue, setOpenName]); + + useEffect(() => { + if (!isOpen) { + return; + } + open(); + }, [isOpen, open]); + + const afterCloseRef = useRef(afterClose); + afterCloseRef.current = afterClose; + + useEffect(() => { + if (!sidebar.isOpen) { + return; + } + return () => { + setOpenName(null); + afterCloseRef.current?.(); + }; + }, [sidebar.isOpen, setOpenName]); + + return { ...sidebar, open }; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar.tsx index 831a903ea..f1358ef91 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { Button } from "@mui/joy"; import { useFormikContext } from "formik"; import { useRouter } from "next/navigation"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx index 92f1ac9ae..89368245f 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar.tsx @@ -3,34 +3,32 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiAppointment, ApiAppointmentBookingType, ApiAppointmentType, ApiConcern, ApiCountryCode, - ApiCreateProcedureRequest, ApiCreateProcedureResponse, ApiGender, -} from "@eshg/employee-portal-api/stiProtection"; -import { GENDER_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { countryOptions } from "@eshg/lib-portal/helpers/countryOption"; -import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; -import { differenceInMinutes } from "date-fns"; -import { Formik, FormikHelpers } from "formik"; +} from "@eshg/sti-protection-api"; +import { Formik } from "formik"; import { useRouter } from "next/navigation"; -import { useReducer, useState } from "react"; +import { useState } from "react"; import { useCreateStiProcedureMutation, useCreateStiProcedureOptions, } from "@/lib/businessModules/stiProtection/api/mutations/procedures"; -import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants"; import { - deleteUndefined, - optionalInt, -} from "@/lib/businessModules/stiProtection/shared/helpers"; + AppointmentForm, + CreateAppointmentForm, +} from "@/lib/businessModules/stiProtection/shared/procedure/AppointmentForm"; +import { SharePinModal } from "@/lib/businessModules/stiProtection/shared/procedure/SharePinModal"; +import { CONCERN_OPTIONS } from "@/lib/businessModules/stiProtection/shared/procedure/helpers"; +import { mapProcedureFormToApi } from "@/lib/businessModules/stiProtection/shared/procedure/mappers"; +import { useFormWithSteps } from "@/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps"; import { routes } from "@/lib/businessModules/stiProtection/shared/routes"; import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; @@ -42,20 +40,15 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm"; -import { AppointmentForm, CombinedAppointmentForm } from "./AppointmentForm"; import { PersonalDataForm, personalDataFormValidation, } from "./PersonalDataForm"; -import { SharePinModal } from "./SharePinModal"; import { AppointmentFieldSetProps, SummaryForm } from "./SummaryForm"; -export const CONCERN_OPTIONS = Object.entries(CONCERN_VALUES).map( - ([value, label]) => ({ - content: <b>{label}</b>, - value: value as ApiConcern, - }), -); +export type CombinedAppointmentForm = Partial< + AddNewProcedureForm & CreateAppointmentForm +>; const steps = [ { @@ -122,14 +115,6 @@ export function AddNewProcedureSidebar() { const [dataToShare, setDataToShare] = useState< { pin: string; id: string } | undefined >(); - const lastStepIndex = steps.length - 1; - const [stepIndex, changeToStep] = useReducer( - (_index: number, newIndex: number) => - Math.max(Math.min(newIndex, lastStepIndex), 0), - 0, - ); - - const step = steps[stepIndex]!; const snackbar = useSnackbar(); const addNewProcedureOptions = useCreateStiProcedureOptions(); @@ -141,6 +126,21 @@ export function AddNewProcedureSidebar() { }, }); + function onFinalSubmit(newValues: AddNewProcedureForm) { + const mappedValues = mapProcedureFormToApi(newValues); + return addNewProcedure.mutateAsync(mappedValues); + } + + const { + Fields, + handleNext, + handlePrev, + changeToStep, + step, + isOnFirstStep, + isOnLastStep, + } = useFormWithSteps({ steps, onFinalSubmit }); + function pinIsShared() { if (dataToShare == null) { return; @@ -156,22 +156,6 @@ export function AddNewProcedureSidebar() { }, }); - const isOnFirstStep = stepIndex === 0; - const isOnLastStep = stepIndex === lastStepIndex; - - function handleNext( - newValues: AddNewProcedureForm, - _helpers: FormikHelpers<AddNewProcedureForm>, - ) { - if (isOnLastStep) { - const mappedValues = mapFormToApi(newValues); - return addNewProcedure.mutateAsync(mappedValues); - } - changeToStep(stepIndex + 1); - } - - const Fields = step.fields; - return ( <> <Sidebar open={isOpen} onClose={handleClose}> @@ -185,7 +169,7 @@ export function AddNewProcedureSidebar() { <ConfirmLeaveDirtyFormEffect onSaveMutation={{ mutationOptions: addNewProcedureOptions, - variableSupplier: () => mapFormToApi(values), + variableSupplier: () => mapProcedureFormToApi(values), }} /> <SidebarContent title={step.title} subtitle={step.subTitle}> @@ -202,11 +186,7 @@ export function AddNewProcedureSidebar() { <MultiFormButtonBar submitting={addNewProcedure.isPending} onCancel={handleClose} - onBack={ - isOnFirstStep - ? undefined - : () => changeToStep(stepIndex - 1) - } + onBack={isOnFirstStep ? undefined : handlePrev} submitLabel={isOnLastStep ? "Vorgang anlegen" : "Weiter"} /> </SidebarActions> @@ -218,55 +198,3 @@ export function AddNewProcedureSidebar() { </> ); } - -function mapFormToApi(form: AddNewProcedureForm): ApiCreateProcedureRequest { - if (!form.yearOfBirth) { - throw new Error("Year of birth must be defined"); - } - if (!form.appointmentBookingType) { - throw new Error("Appointment booking type must be defined"); - } - const isCustomAppointment = - form.appointmentBookingType === ApiAppointmentBookingType.UserDefined; - - const appointmentStart = isCustomAppointment - ? new Date(form.customAppointmentDate) - : form.blockAppointment?.start; - - if (!appointmentStart) { - throw new Error("Appointment start must be defined"); - } - - const blockAppointmentEnd = form.blockAppointment?.end; - if (!isCustomAppointment && blockAppointmentEnd == null) { - throw new Error("Appointment end must be defined"); - } - - return deleteUndefined({ - appointmentBookingType: form.appointmentBookingType, - concern: CONCERN_OPTIONS.find((t) => t.value === form.concern)?.value, - countryOfBirth: countryOptions().find( - (t) => t.value === form.countryOfBirth, - )?.value, - gender: GENDER_OPTIONS.find((t) => t.value === form.gender)?.value as - | ApiGender - | undefined, - durationInMinutes: isCustomAppointment - ? optionalInt(form.customAppointmentDuration) - : differenceInMinutes(blockAppointmentEnd!, appointmentStart), - appointmentStart, - inGermanySince: optionalInt(form.inGermanySince), - yearOfBirth: parseInt(form.yearOfBirth, 10), - }); -} - -export function getAppointmentDate(form: CombinedAppointmentForm) { - const customAppointmentDate = isNonEmptyString(form.customAppointmentDate) - ? new Date(form.customAppointmentDate) - : undefined; - const date = - form.appointmentBookingType === ApiAppointmentBookingType.AppointmentBlock - ? form.blockAppointment?.start - : customAppointmentDate; - return date ?? undefined; -} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx index 5403b80f9..133b97bb0 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SummaryForm.tsx @@ -3,14 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentType, - ApiConcern, -} from "@eshg/employee-portal-api/stiProtection"; import { GENDER_VALUES } from "@eshg/lib-portal/components/formFields/constants"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; +import { ApiAppointmentType, ApiConcern } from "@eshg/sti-protection-api"; import { EditOutlined } from "@mui/icons-material"; import { Divider, IconButton, Stack, Typography } from "@mui/joy"; import { useFormikContext } from "formik"; @@ -18,15 +15,16 @@ import { ReactNode, useId } from "react"; import { APPOINTMENT_TYPES } from "@/lib/businessModules/stiProtection/shared/constants"; import { concernToAppointmentType } from "@/lib/businessModules/stiProtection/shared/helpers"; +import { getAppointmentDate } from "@/lib/businessModules/stiProtection/shared/procedure/mappers"; -import { getAppointmentDate } from "./AddNewProcedureSidebar"; -import { CombinedAppointmentForm } from "./AppointmentForm"; +import { CombinedAppointmentForm } from "./AddNewProcedureSidebar"; const germanDateFormatter = Intl.DateTimeFormat("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", }); + const germanTimeFormatter = Intl.DateTimeFormat("de-DE", { timeStyle: "short", }); diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/ConsultationForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/ConsultationForm.tsx index 3a7e61994..dd3c64a57 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/ConsultationForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/ConsultationForm.tsx @@ -5,24 +5,26 @@ "use client"; +import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { ApiConsultation, ApiStiProtectionProcedure, -} from "@eshg/employee-portal-api/stiProtection"; -import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { Divider, Sheet, Typography } from "@mui/joy"; + ApiTextTemplateContext, +} from "@eshg/sti-protection-api"; +import { Sheet, Typography } from "@mui/joy"; import { Formik } from "formik"; -import { useUpsertConsultation } from "@/lib/businessModules/stiProtection/api/mutations/consultation"; -import { TextTemplatesSidebarProvider } from "@/lib/businessModules/stiProtection/components/textTemplates/TextTemplatesSidebarProvider"; +import { + useUpsertConsultation, + useUpsertConsultationOptions, +} from "@/lib/businessModules/stiProtection/api/mutations/consultation"; import { TextareaFieldWithTextTemplates } from "@/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates"; -import { ApiTextTemplateContext } from "@/lib/businessModules/stiProtection/components/textTemplates/constants"; import { SidecarFormLayout, SidecarSheet, } from "@/lib/businessModules/stiProtection/features/procedures/SidecarFormLayout"; import { TabStickyBottomButtonBar } from "@/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar"; +import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { GeneralSection } from "./GeneralSection"; import { PregnancySection } from "./PregnancySection"; @@ -39,51 +41,59 @@ export function ConsultationForm({ procedure: ApiStiProtectionProcedure; consultation: ApiConsultation; }>) { - const snackbar = useSnackbar(); - const upsertConsultation = useUpsertConsultation(procedure.id, { - onSuccess: () => { - snackbar.confirmation("Die Konsultation wurde erfolgreich gespeichert."); - }, - onError: () => { - snackbar.error("Die Konsultation konnte nicht gespeichert werden."); - }, + const { id: procedureId } = procedure; + const upsertConsultationOptions = useUpsertConsultationOptions({ + procedureId, }); + const upsertConsultation = useUpsertConsultation({ procedureId }); function onSubmit(values: ConsultationFormData) { const consultation = mapFormValuesToApi(values); - return upsertConsultation.mutateAsync(consultation); + return upsertConsultation.mutateAsync({ consultation }); } return ( - <Formik initialValues={mapApiToForm(consultation)} onSubmit={onSubmit}> - <FormPlus> - <SidecarFormLayout> - <Sheet> - <Typography level="h2" mb={5}> - Konsultation - </Typography> + <Formik + initialValues={mapApiToForm(consultation)} + onSubmit={onSubmit} + enableReinitialize + > + {({ values }) => ( + <FormPlus> + <ConfirmLeaveDirtyFormEffect + onSaveMutation={{ + mutationOptions: upsertConsultationOptions, + variableSupplier: () => ({ + procedureId, + consultation: mapFormValuesToApi(values), + }), + }} + /> - <GeneralSection /> + <SidecarFormLayout> + <Sheet> + <Typography level="h2" mb={5}> + Konsultation + </Typography> - <Divider sx={(theme) => ({ my: theme.spacing(5) })} /> + <GeneralSection /> - <PregnancySection /> - </Sheet> - <SidecarSheet> - <Typography level="h3" mb={3}> - Zusatzinfos - </Typography> - <TextTemplatesSidebarProvider> + <PregnancySection /> + </Sheet> + <SidecarSheet> + <Typography level="h3" mb={3}> + Zusatzinfos + </Typography> <TextareaFieldWithTextTemplates name="general.notes" label="Allgemeine Bemerkungen" - context={ApiTextTemplateContext.Consultation} + context={ApiTextTemplateContext.ConsultationRemark} /> - </TextTemplatesSidebarProvider> - </SidecarSheet> - </SidecarFormLayout> - <TabStickyBottomButtonBar procedure={procedure} /> - </FormPlus> + </SidecarSheet> + </SidecarFormLayout> + <TabStickyBottomButtonBar procedure={procedure} /> + </FormPlus> + )} </Formik> ); } diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/GeneralSection.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/GeneralSection.tsx index bbb9d8a9f..3510897a8 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/GeneralSection.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/GeneralSection.tsx @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { ApiTextTemplateContext } from "@eshg/sti-protection-api"; import { Typography, useTheme } from "@mui/joy"; import { SectionGrid } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/SectionGrid"; @@ -10,6 +11,7 @@ import { YesOrNoFieldData, YesOrNoWithFollowUp, } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; +import { TextareaFieldWithTextTemplates } from "@/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; @@ -42,7 +44,11 @@ export function GeneralSection() { <SectionGrid defaultColumn={1}> <Typography level="h3">Allgemein</Typography> - <TextareaField name="general.mainReason" label="Konsultationsgrund" /> + <TextareaFieldWithTextTemplates + context={ApiTextTemplateContext.ConsultationReason} + name="general.mainReason" + label="Konsultationsgrund" + /> <TextareaField name="general.furtherGenderInfo" label="Weitere Geschlechtsangaben" diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/helpers.ts index d2a471454..8c7014aac 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/helpers.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/consultation/helpers.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiConsultation } from "@eshg/employee-portal-api/stiProtection"; import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; +import { ApiConsultation } from "@eshg/sti-protection-api"; import { mapBoolToYesOrNo, 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 b3613affc..61479b739 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 @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; -import { Sheet } from "@mui/joy"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; +import { Chip, Sheet, styled } from "@mui/joy"; import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants"; import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; @@ -27,6 +27,7 @@ export function AdditionalDataSection({ <Sheet> <DetailsSection title="Zusatzinfos"> <DetailsColumn> + {procedure.isFollowUp ? <FollowUpProcedureChip /> : null} <DetailsCell label="Art" value={CONCERN_VALUES[procedure.concern]} /> <DetailsCell label="Nächster Termin" @@ -38,3 +39,12 @@ export function AdditionalDataSection({ </Sheet> ); } + +const FollowUpChip = styled(Chip)(({ theme }) => ({ + backgroundColor: theme.palette.primary.softBg, + color: theme.palette.text.primary, +})); + +function FollowUpProcedureChip() { + return <FollowUpChip>Folgevorgang</FollowUpChip>; +} 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 index c842d64e8..53a14c1e2 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AnonIdentityDocumentCard.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { Sheet, Stack } from "@mui/joy"; import { useAnonymousIdentificationDocumentQuery } from "@/lib/businessModules/stiProtection/api/queries/procedures"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AppointmentDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AppointmentDetails.tsx index b33185066..16a23e442 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AppointmentDetails.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/AppointmentDetails.tsx @@ -3,12 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { Row } from "@eshg/lib-portal/components/Row"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiAppointmentHistoryEntry, ApiStiProtectionProcedure, -} from "@eshg/employee-portal-api/stiProtection"; -import { Row } from "@eshg/lib-portal/components/Row"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/sti-protection-api"; import { EditCalendar, EventBusy } from "@mui/icons-material"; import { Button, Chip, Sheet, Stack } from "@mui/joy"; import { ColumnSort, createColumnHelper } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CheckPinSection.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CheckPinSection.tsx index d8029c565..eb06fcf70 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CheckPinSection.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CheckPinSection.tsx @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; import { Row } from "@eshg/lib-portal/components/Row"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { CheckCircleOutlined, ErrorOutlineOutlined, diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx index c5ee4fcd2..4a0c02ee9 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseAndReopenDialogs.tsx @@ -3,12 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; import { ApiAppointmentHistoryEntry, ApiStiProtectionProcedure, ApiStiProtectionProcedureOverview, -} from "@eshg/employee-portal-api/stiProtection"; -import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; +} from "@eshg/sti-protection-api"; import { Typography, styled } from "@mui/joy"; import { useState } from "react"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateAppointmentSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateAppointmentSidebar.tsx index db6233983..bd7bbe182 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateAppointmentSidebar.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateAppointmentSidebar.tsx @@ -3,16 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiAppointmentBookingType, ApiAppointmentType, - ApiConcern, ApiCreateAppointmentRequest, ApiStiProtectionProcedure, ApiUpdateAppointmentRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { ApiAppointment } from "@eshg/measles-protection-api"; +} from "@eshg/sti-protection-api"; import { differenceInMinutes } from "date-fns"; import { Formik, FormikHelpers } from "formik"; import { ReactNode, useMemo, useReducer } from "react"; @@ -21,7 +19,6 @@ import { useCreateAppointmentMutation, useEditAppointmentMutation, } from "@/lib/businessModules/stiProtection/api/mutations/procedures"; -import { AppointmentForm } from "@/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm"; import { AppointmentFieldSetProps, SummaryForm, @@ -31,6 +28,11 @@ import { isProcedureOpen, optionalInt, } from "@/lib/businessModules/stiProtection/shared/helpers"; +import { + AppointmentForm, + CreateAppointmentForm, + initialValues, +} from "@/lib/businessModules/stiProtection/shared/procedure/AppointmentForm"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; import { Sidebar } from "@/lib/shared/components/sidebar/Sidebar"; @@ -39,24 +41,6 @@ import { SidebarContent } from "@/lib/shared/components/sidebar/SidebarContent"; import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm"; -export interface CreateAppointmentForm { - appointmentType?: ApiAppointmentType | "" | null; - appointmentBookingType?: ApiAppointmentBookingType | ""; - blockAppointment?: null | ApiAppointment; - concern?: ApiConcern | "RESULTS_REVIEW" | ""; - customAppointmentDate: string; - customAppointmentDuration: string; -} - -const initialValues: CreateAppointmentForm = { - appointmentType: null, - appointmentBookingType: "", - blockAppointment: null, - concern: "", - customAppointmentDate: "", - customAppointmentDuration: "", -}; - interface CreateAppointmentSidebarProps { procedure: ApiStiProtectionProcedure; } diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateFollowUpProcedureSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateFollowUpProcedureSidebar.tsx new file mode 100644 index 000000000..485512556 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CreateFollowUpProcedureSidebar.tsx @@ -0,0 +1,219 @@ +/** + * Copyright 2025 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 { + ApiAppointment, + ApiAppointmentBookingType, + ApiAppointmentType, + ApiConcern, + ApiCreateFollowUpProcedureResponse, + ApiStiProtectionProcedure, +} from "@eshg/sti-protection-api"; +import { Stack, Typography } from "@mui/joy"; +import { Formik, useFormikContext } from "formik"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; + +import { + useCreateStiFollowUpProcedureMutation, + useCreateStiFollowUpProcedureOptions, +} from "@/lib/businessModules/stiProtection/api/mutations/procedures"; +import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants"; +import { AppointmentForm } from "@/lib/businessModules/stiProtection/shared/procedure/AppointmentForm"; +import { SharePinModal } from "@/lib/businessModules/stiProtection/shared/procedure/SharePinModal"; +import { CONCERN_OPTIONS } from "@/lib/businessModules/stiProtection/shared/procedure/helpers"; +import { mapFollowUpProcedureFormToApi } from "@/lib/businessModules/stiProtection/shared/procedure/mappers"; +import { useFormWithSteps } from "@/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps"; +import { routes } from "@/lib/businessModules/stiProtection/shared/routes"; +import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; +import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; +import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; +import { SelectableCardsField } from "@/lib/shared/components/formFields/SelectableCardsField"; +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 { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; +import { useSidebarForm } from "@/lib/shared/hooks/useSidebarForm"; + +const steps = [ + { + title: "Folgevorgang anlegen", + subTitle: "Schritt 1 von 2", + fields: ({ + procedure, + }: Readonly<{ procedure: ApiStiProtectionProcedure }>) => ( + <CreateFollowUpProcedureTypeAndInfo procedure={procedure} /> + ), + }, + { + title: "Folgevorgang anlegen", + subTitle: "Schritt 2 von 2", + fields: () => <CreateFollowUpProcedureAppointmentForm />, + }, +]; + +const initialValues: CreateFollowUpProcedureForm = { + concern: "", + + appointmentBookingType: "", + blockAppointment: null, + customAppointmentDate: "", + customAppointmentDuration: "", +}; + +export interface CreateFollowUpProcedureForm { + concern?: ApiConcern | ""; + appointmentType?: ApiAppointmentType | "" | null; + + appointmentBookingType?: ApiAppointmentBookingType | ""; + blockAppointment?: null | ApiAppointment; + customAppointmentDate: string; + customAppointmentDuration: string; +} + +export function CreateFollowUpProcedureSidebar({ + procedure, +}: Readonly<{ procedure: ApiStiProtectionProcedure }>) { + const router = useRouter(); + const [isOpen, setIsOpen] = useSearchParam( + "create-follow-up-procedure", + "boolean", + ); + const [dataToShare, setDataToShare] = useState< + { pin: string; id: string } | undefined + >(); + + const snackbar = useSnackbar(); + const createFollowUpProcedureOptions = useCreateStiFollowUpProcedureOptions(); + const createFollowUpProcedure = useCreateStiFollowUpProcedureMutation({ + onSuccess: (data: ApiCreateFollowUpProcedureResponse) => { + setIsOpen(false); + snackbar.confirmation("Folgevorgang angelegt"); + setDataToShare({ pin: data.pin, id: data.procedureId }); + }, + }); + + function onFinalSubmit(newValues: CreateFollowUpProcedureForm) { + const mappedValues = mapFollowUpProcedureFormToApi(newValues); + return createFollowUpProcedure.mutateAsync({ + id: procedure.id, + data: mappedValues, + }); + } + + const { + Fields, + handleNext, + handlePrev, + changeToStep, + step, + isOnFirstStep, + isOnLastStep, + } = useFormWithSteps({ steps, onFinalSubmit }); + + function pinIsShared() { + if (dataToShare == null) { + return; + } + router.push(routes.procedures.byId(dataToShare.id).details); + setDataToShare(undefined); + } + + const { sidebarFormRef, handleClose } = useSidebarForm({ + onClose: () => { + setIsOpen(false); + changeToStep(0); + }, + }); + + return ( + <> + <Sidebar open={isOpen} onClose={handleClose}> + <Formik initialValues={initialValues} onSubmit={handleNext}> + {({ values }) => ( + <SidebarForm ref={sidebarFormRef}> + <ConfirmLeaveDirtyFormEffect + onSaveMutation={{ + mutationOptions: createFollowUpProcedureOptions, + variableSupplier: () => mapFollowUpProcedureFormToApi(values), + }} + /> + <SidebarContent title={step.title} subtitle={step.subTitle}> + <Fields procedure={procedure} /> + </SidebarContent> + <SidebarActions> + <MultiFormButtonBar + submitting={createFollowUpProcedure.isPending} + onCancel={handleClose} + onBack={isOnFirstStep ? undefined : handlePrev} + submitLabel={isOnLastStep ? "Folgevorgang anlegen" : "Weiter"} + /> + </SidebarActions> + </SidebarForm> + )} + </Formik> + </Sidebar> + <SharePinModal pinToShare={dataToShare?.pin} onShared={pinIsShared} /> + </> + ); +} + +function CreateFollowUpProcedureTypeAndInfo({ + procedure, +}: Readonly<{ procedure: ApiStiProtectionProcedure }>) { + const openAppointment = procedure.appointmentHistory.find( + (t) => t.appointmentStatus === "OPEN", + ); + + return ( + <> + <Typography level={"title-md"}>Art der Beratung</Typography> + <Stack gap={3}> + <SelectableCardsField + name="concern" + required="Bitte ein Anliegen auswählen" + options={CONCERN_OPTIONS} + /> + <Alert + color={"primary"} + message={ + <> + Ausschließlich folgende Daten werden übernommen: + <li>Angaben zur Person</li> + <li>Anamnese</li> + <li>Konsultation</li> + <li>Diagnose</li> + <li>Ergebnisse der Untersuchung</li> + <br /> + Der aktuelle Vorgang wird abgeschlossen und Termine werden + storniert. + </> + } + /> + {openAppointment ? ( + <Alert + color={"warning"} + message="Es existieren noch offene Termine, welche bei der Anlage eines Folgevorgangs storniert werden." + /> + ) : null} + </Stack> + </> + ); +} + +function CreateFollowUpProcedureAppointmentForm() { + const { getFieldMeta } = useFormikContext<CreateFollowUpProcedureForm>(); + const concern = getFieldMeta("concern").value as ApiConcern; + const title = `Termin auswählen für ${CONCERN_VALUES[concern]}`; + + return ( + <Stack gap={1}> + <Typography level={"title-md"}>{title}</Typography> + <AppointmentForm /> + </Stack> + ); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/EditPersonalDataSidebar.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/EditPersonalDataSidebar.tsx index 5a23bf0e5..d420116fe 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/EditPersonalDataSidebar.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/EditPersonalDataSidebar.tsx @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { GENDER_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { countryOptions } from "@eshg/lib-portal/helpers/countryOption"; import { ApiGender, ApiStiProtectionProcedure, UpdatePersonDetailsRequest, -} from "@eshg/employee-portal-api/stiProtection"; -import { GENDER_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; -import { countryOptions } from "@eshg/lib-portal/helpers/countryOption"; +} from "@eshg/sti-protection-api"; import { Formik } from "formik"; import { useUpdatePersonDetails } from "@/lib/businessModules/stiProtection/api/mutations/procedures"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/FinalProcedureActionPanel.tsx similarity index 68% rename from employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx rename to employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/FinalProcedureActionPanel.tsx index 2218229f8..ea094e6c5 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/CloseProcedurePanel.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/FinalProcedureActionPanel.tsx @@ -3,20 +3,22 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { Button } from "@mui/joy"; import { ReactEventHandler } from "react"; import { isProcedureOpen } from "@/lib/businessModules/stiProtection/shared/helpers"; import { ContentPanel } from "@/lib/shared/components/contentPanel/ContentPanel"; +import { useSearchParam } from "@/lib/shared/hooks/searchParams/useSearchParam"; import { CloseConfirmationDialog, ReopenConfirmationDialog, useCloseAndReopenProcedure, } from "./CloseAndReopenDialogs"; +import { CreateFollowUpProcedureSidebar } from "./CreateFollowUpProcedureSidebar"; -export function CloseAndReopenProcedurePanel({ +export function FinalProcedureActionPanel({ procedure, }: Readonly<{ procedure: ApiStiProtectionProcedure }>) { const { @@ -33,8 +35,14 @@ export function CloseAndReopenProcedurePanel({ ? CloseConfirmationDialog : ReopenConfirmationDialog; + const [_isOpen, setIsOpen] = useSearchParam( + "create-follow-up-procedure", + "boolean", + ); + return ( <ContentPanel> + <FollowUpButton onClick={() => setIsOpen(true)} /> <ActionButton onClick={() => requestFinalize(procedure)} /> <ConfirmationDialog open={isRequestingFinalize} @@ -42,6 +50,7 @@ export function CloseAndReopenProcedurePanel({ onConfirm={handleFinalizeProcedure} procedure={procedure} /> + <CreateFollowUpProcedureSidebar procedure={procedure} /> </ContentPanel> ); } @@ -65,3 +74,15 @@ function ReopenButton({ </Button> ); } + +function FollowUpButton({ + onClick, +}: { + onClick: ReactEventHandler<HTMLButtonElement>; +}) { + return ( + <Button onClick={onClick} variant="soft"> + Folgevorgang anlegen + </Button> + ); +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/PersonDetails.tsx index 3b8749b76..400cd8320 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 @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { Sheet, Stack } from "@mui/joy"; import { GENDER_VALUES } from "@/lib/businessModules/stiProtection/shared/constants"; 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 bbe605f01..e3edb70d8 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 @@ -5,16 +5,16 @@ "use client"; -import { ApiStiProtectionProcedure } from "@eshg/employee-portal-api/stiProtection"; +import { ApiStiProtectionProcedure } from "@eshg/sti-protection-api"; import { Grid, Stack } from "@mui/joy"; import { AdditionalDataSection } from "./AdditionalDataSection"; import { AnonIdentityDocumentCard } from "./AnonIdentityDocumentCard"; import { AppointmentDetails } from "./AppointmentDetails"; import { CheckPinSection } from "./CheckPinSection"; -import { CloseAndReopenProcedurePanel } from "./CloseProcedurePanel"; import { CreateAppointmentSidebar } from "./CreateAppointmentSidebar"; import { EditPersonalDataSidebar } from "./EditPersonalDataSidebar"; +import { FinalProcedureActionPanel } from "./FinalProcedureActionPanel"; import { PersonDetails } from "./PersonDetails"; import { WaitingRoomSection } from "./WaitingRoomSection"; @@ -40,7 +40,7 @@ export function ProcedureDetails({ <AdditionalDataSection procedure={procedure} /> <CheckPinSection procedure={procedure} /> <WaitingRoomSection procedure={procedure} /> - <CloseAndReopenProcedurePanel procedure={procedure} /> + <FinalProcedureActionPanel procedure={procedure} /> </Stack> </Grid> </Grid> diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/WaitingRoomSection.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/WaitingRoomSection.tsx index cdda8a173..6e2296090 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/WaitingRoomSection.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/details/WaitingRoomSection.tsx @@ -3,18 +3,18 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiStiProtectionProcedure, - ApiWaitingRoom, - ApiWaitingStatus, - UpdateWaitingRoomDetailsRequest, -} from "@eshg/employee-portal-api/stiProtection"; import { Row } from "@eshg/lib-portal/components/Row"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { + ApiStiProtectionProcedure, + ApiWaitingRoom, + ApiWaitingStatus, + UpdateWaitingRoomDetailsRequest, +} from "@eshg/sti-protection-api"; import { Button, Sheet } from "@mui/joy"; import { Formik, useFormikContext } from "formik"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/DiagnosisForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/DiagnosisForm.tsx index a7a9d38e0..ff9b6cbbe 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/DiagnosisForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/DiagnosisForm.tsx @@ -5,17 +5,17 @@ "use client"; -import { - ApiDiagnosis, - ApiStiProtectionProcedure, - ApiTestType, -} from "@eshg/employee-portal-api/stiProtection"; import { Row } from "@eshg/lib-portal/components/Row"; import { useIsFormDisabled } from "@eshg/lib-portal/components/form/DisabledFormContext"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { + ApiDiagnosis, + ApiStiProtectionProcedure, + ApiTestType, + ApiTextTemplateContext, +} from "@eshg/sti-protection-api"; import { Add, Delete, Edit } from "@mui/icons-material"; import { Button, IconButton, Sheet, Stack, Typography } from "@mui/joy"; import { @@ -27,17 +27,21 @@ import { } from "formik"; import { PropsWithChildren } from "react"; -import { useUpsertDiagnosis } from "@/lib/businessModules/stiProtection/api/mutations/diagnosis"; +import { + useUpsertDiagnosis, + useUpsertDiagnosisOptions, +} from "@/lib/businessModules/stiProtection/api/mutations/diagnosis"; import { SectionGrid } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/SectionGrid"; +import { TextareaFieldWithTextTemplates } from "@/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates"; import { SidecarFormLayout, SidecarSheet, } from "@/lib/businessModules/stiProtection/features/procedures/SidecarFormLayout"; import { TabStickyBottomButtonBar } from "@/lib/businessModules/stiProtection/features/procedures/TabStickyBottomButtonBar"; import { useOnCancelForm } from "@/lib/businessModules/stiProtection/shared/helpers"; +import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; import { CheckboxGroupField } from "@/lib/shared/components/formFields/CheckboxGroupField"; -import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; import { useIcd10Sidebar } from "./Icd10Sidebar"; import { @@ -57,19 +61,17 @@ export function DiagnosisForm({ procedure: ApiStiProtectionProcedure; diagnosis: ApiDiagnosis; }>) { - const snackbar = useSnackbar(); - const upsertDiagnosis = useUpsertDiagnosis(procedure.id, { - onSuccess: () => { - snackbar.confirmation("Die Diagnose wurde erfolgreich gespeichert."); - }, - onError: () => { - snackbar.error("Die Diagnose konnte nicht gespeichert werden."); - }, + const { id: procedureId } = procedure; + const upsertDiagnosisOptions = useUpsertDiagnosisOptions({ + procedureId, }); - + const upsertDiagnosis = useUpsertDiagnosis({ procedureId }); const onCancelForm = useOnCancelForm<DiagnosisFormData>(); - function handleCancel({ dirty, resetForm }: FormikProps<DiagnosisFormData>) { + function handleCancel({ + dirty, + resetForm, + }: Pick<FormikProps<DiagnosisFormData>, "dirty" | "resetForm">) { onCancelForm({ dirty, reset: resetForm, @@ -78,19 +80,38 @@ export function DiagnosisForm({ function onSubmit(values: DiagnosisFormData) { const diagnosis = mapFormToApi(values); - return upsertDiagnosis.mutateAsync(diagnosis); + return upsertDiagnosis.mutateAsync({ + diagnosis, + }); } return ( - <Formik initialValues={mapApiToForm(diagnosis)} onSubmit={onSubmit}> - {(formikProps) => ( + <Formik + initialValues={mapApiToForm(diagnosis)} + onSubmit={onSubmit} + enableReinitialize + > + {({ resetForm, dirty, values }) => ( <FormPlus sx={{ height: "100%" }}> + <ConfirmLeaveDirtyFormEffect + onSaveMutation={{ + mutationOptions: upsertDiagnosisOptions, + variableSupplier: () => ({ + procedureId, + diagnosis: mapFormToApi(values), + }), + }} + /> <SidecarFormLayout> <Sheet> <Stack gap={5}> <Typography level="h2">Diagnose</Typography> <SectionGrid defaultColumn={1}> - <TextareaField name="results" label="Ergebnisse" /> + <TextareaFieldWithTextTemplates + name="results" + label="Ergebnisse" + context={ApiTextTemplateContext.DiagnosisResult} + /> </SectionGrid> <FieldArray name="medications" render={MedicationsSection} /> <FindingsSection /> @@ -103,7 +124,11 @@ export function DiagnosisForm({ Zusatzinfos </Typography> <Stack rowGap={5}> - <TextareaField name="notes" label="Allgemeine Bemerkungen" /> + <TextareaFieldWithTextTemplates + name="notes" + label="Allgemeine Bemerkungen" + context={ApiTextTemplateContext.DiagnosisRemark} + /> <CheckboxField name="resultsShared" label="Ergebnis mitgeteilt" @@ -113,7 +138,7 @@ export function DiagnosisForm({ </SidecarFormLayout> <TabStickyBottomButtonBar procedure={procedure} - onCancel={() => handleCancel(formikProps)} + onCancel={() => handleCancel({ dirty, resetForm })} /> </FormPlus> )} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/helpers.ts index 216bae9c8..8e3a2e0d5 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/helpers.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/diagnosis/helpers.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; import { ApiDiagnosis, ApiIcd10Code, ApiTestType, -} from "@eshg/employee-portal-api/stiProtection"; -import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; -import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; -import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; +} from "@eshg/sti-protection-api"; import { formatDateTypeToISODate } from "@/lib/shared/helpers/dateTime"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestExamination.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestExamination.tsx index 538bdd499..cebaf74f0 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestExamination.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestExamination.tsx @@ -3,253 +3,40 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiLaboratoryTestExamination } from "@eshg/employee-portal-api/stiProtection"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { + ApiLaboratoryTestExamination, + ApiTextTemplateContext, +} from "@eshg/sti-protection-api"; import { Box, Divider, Grid, Sheet, Stack, Typography } from "@mui/joy"; -import { Formik, FormikState } from "formik"; +import { Formik } from "formik"; -import { useUpsertLaboratoryTest } from "@/lib/businessModules/stiProtection/api/mutations/examination"; +import { + useUpsertLaboratoryTest, + useUpsertLaboratoryTestOptions, +} from "@/lib/businessModules/stiProtection/api/mutations/examination"; +import { TextareaFieldWithTextTemplates } from "@/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates"; import { ExaminationStickyBottomButtonBar } from "@/lib/businessModules/stiProtection/features/procedures/examination/ExaminationStickyBottomButtonBar"; import { ExaminationTabNavPanel } from "@/lib/businessModules/stiProtection/features/procedures/examination/ExaminationTabNavPanel"; -import { - guardValue, - mapOptionalString, -} from "@/lib/businessModules/stiProtection/shared/helpers"; +import { useOnCancelForm } from "@/lib/businessModules/stiProtection/shared/helpers"; +import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; -import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; import { SidePanel } from "@/lib/shared/components/sidePanel/SidePanel"; import { SidePanelTitle } from "@/lib/shared/components/sidePanel/SidePanelTitle"; -import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; import { HepatitisLaboratoryTest, - HepatitisLaboratoryTestData, - LaboratoryTestData, LaboratoryTestSamples, - LaboratoryTestSamplesData, LaboratoryTestWithBooleanResult, - defaultHepatitisLaboratoryTestFormData, - defaultLaboratoryTestFormData, - defaultLaboratoryTestSamplesFormData, - mapApiHepatitisLaboratoryTestToFormData, - mapApiLaboratoryTestSamplesToFormData, - mapApiLaboratoryTestToFormData, - mapHepatitisLaboratoryTestFormDataToApi, - mapLaboratoryTestFormDataToApi, - mapLaboratoryTestSamplesFormDataToApi, } from "./LaboratoryTestTemplates"; - -export interface LaboratoryTestExaminationData { - sampleBarcode?: string; - generalRemarks?: string; - testsConducted?: boolean; - testsPayed?: boolean; - //Requested Tests - hivTestRequested?: boolean; - syphilisTestRequested?: boolean; - hepATestRequested?: boolean; - hepBTestRequested?: boolean; - hepCTestRequested?: boolean; - chlamydiaTestRequested?: boolean; - gonorrheaTestRequested?: boolean; - mycoplasmaTestRequested?: boolean; - cancerScreeningTestRequested?: boolean; - hpvTestRequested?: boolean; - mpoxTestRequested?: boolean; - otherTestRequested?: boolean; - //Data of Tests - hivTestData: LaboratoryTestData | null; - syphilisTestData: LaboratoryTestData | null; - hadSyphilis?: boolean; - hepATestData: HepatitisLaboratoryTestData | null; - hepBTestData: HepatitisLaboratoryTestData | null; - hepCTestData: LaboratoryTestData | null; - chlamydiaTestData: LaboratoryTestSamplesData | null; - gonorrheaTestData: LaboratoryTestSamplesData | null; - mycoplasmaTestData: LaboratoryTestSamplesData | null; - cancerScreeningTestData: LaboratoryTestData | null; - hpvTestData: LaboratoryTestData | null; - mpoxTestData: LaboratoryTestData | null; - otherTestName?: string; - otherTestData: LaboratoryTestData | null; -} - -function mapToFormValues( - responseData: ApiLaboratoryTestExamination, -): LaboratoryTestExaminationData { - return { - sampleBarcode: responseData.sampleBarcode ?? "", - generalRemarks: responseData.generalRemarks ?? "", - testsConducted: responseData.testsConducted ?? false, - testsPayed: responseData.testsPayed ?? false, - //Requested Tests - hivTestRequested: responseData.hivTestRequested ?? false, - syphilisTestRequested: responseData.syphilisTestRequested ?? false, - hepATestRequested: responseData.hepATestRequested ?? false, - hepBTestRequested: responseData.hepBTestRequested ?? false, - hepCTestRequested: responseData.hepCTestRequested ?? false, - chlamydiaTestRequested: responseData.chlamydiaTestRequested ?? false, - gonorrheaTestRequested: responseData.gonorrheaTestRequested ?? false, - mycoplasmaTestRequested: responseData.mycoplasmaTestRequested ?? false, - cancerScreeningTestRequested: - responseData.cancerScreeningTestRequested ?? false, - hpvTestRequested: responseData.hpvTestRequested ?? false, - mpoxTestRequested: responseData.mpoxTestRequested ?? false, - otherTestRequested: responseData.otherTestRequested ?? false, - //Data of Tests - hivTestData: mapApiLaboratoryTestToFormData(responseData.hivTestData), - syphilisTestData: mapApiLaboratoryTestToFormData( - responseData.syphilisTestData, - ), - hadSyphilis: responseData.hadSyphilis ?? false, - hepATestData: mapApiHepatitisLaboratoryTestToFormData( - responseData.hepATestData, - ), - hepBTestData: mapApiHepatitisLaboratoryTestToFormData( - responseData.hepBTestData, - ), - hepCTestData: mapApiLaboratoryTestToFormData(responseData.hepCTestData), - chlamydiaTestData: mapApiLaboratoryTestSamplesToFormData( - responseData.chlamydiaTestSamples, - ), - gonorrheaTestData: mapApiLaboratoryTestSamplesToFormData( - responseData.gonorrheaTestSamples, - ), - mycoplasmaTestData: mapApiLaboratoryTestSamplesToFormData( - responseData.mycoplasmaTestSamples, - ), - cancerScreeningTestData: mapApiLaboratoryTestToFormData( - responseData.cancerScreeningTestData, - ), - hpvTestData: mapApiLaboratoryTestToFormData(responseData.hpvTestData), - mpoxTestData: mapApiLaboratoryTestToFormData(responseData.mpoxTestData), - otherTestName: responseData.otherTestRequested - ? (responseData.otherTestName ?? "") - : "", - otherTestData: mapApiLaboratoryTestToFormData(responseData.otherTestData), - }; -} - -function defaultLaboratoryTestExaminationFormValues(): LaboratoryTestExaminationData { - return { - sampleBarcode: "", - generalRemarks: "", - testsConducted: false, - testsPayed: false, - //Requested Tests - hivTestRequested: false, - syphilisTestRequested: false, - hepATestRequested: false, - hepBTestRequested: false, - hepCTestRequested: false, - chlamydiaTestRequested: false, - gonorrheaTestRequested: false, - mycoplasmaTestRequested: false, - cancerScreeningTestRequested: false, - hpvTestRequested: false, - mpoxTestRequested: false, - otherTestRequested: false, - //Data of Tests - hivTestData: defaultLaboratoryTestFormData, - syphilisTestData: defaultLaboratoryTestFormData, - hadSyphilis: false, - hepATestData: defaultHepatitisLaboratoryTestFormData, - hepBTestData: defaultHepatitisLaboratoryTestFormData, - hepCTestData: defaultLaboratoryTestFormData, - chlamydiaTestData: defaultLaboratoryTestSamplesFormData, - gonorrheaTestData: defaultLaboratoryTestSamplesFormData, - mycoplasmaTestData: defaultLaboratoryTestSamplesFormData, - cancerScreeningTestData: defaultLaboratoryTestFormData, - hpvTestData: defaultLaboratoryTestFormData, - mpoxTestData: defaultLaboratoryTestFormData, - otherTestName: "", - otherTestData: defaultLaboratoryTestFormData, - }; -} - -function mapFormValuesToApi( - formData: LaboratoryTestExaminationData, -): ApiLaboratoryTestExamination { - return { - sampleBarcode: mapOptionalString(formData.sampleBarcode), - generalRemarks: mapOptionalString(formData.generalRemarks), - testsConducted: formData.testsConducted ?? false, - testsPayed: formData.testsPayed ?? false, - //Requested Tests - hivTestRequested: formData.hivTestRequested ?? false, - syphilisTestRequested: formData.syphilisTestRequested ?? false, - hepATestRequested: formData.hepATestRequested ?? false, - hepBTestRequested: formData.hepBTestRequested ?? false, - hepCTestRequested: formData.hepCTestRequested ?? false, - chlamydiaTestRequested: formData.chlamydiaTestRequested ?? false, - gonorrheaTestRequested: formData.gonorrheaTestRequested ?? false, - mycoplasmaTestRequested: formData.mycoplasmaTestRequested ?? false, - cancerScreeningTestRequested: - formData.cancerScreeningTestRequested ?? false, - hpvTestRequested: formData.hpvTestRequested ?? false, - mpoxTestRequested: formData.mpoxTestRequested ?? false, - otherTestRequested: formData.otherTestRequested ?? false, - //Data of Tests - hivTestData: guardValue( - formData.hivTestRequested, - mapLaboratoryTestFormDataToApi(formData.hivTestData), - ), - syphilisTestData: guardValue( - formData.syphilisTestRequested, - mapLaboratoryTestFormDataToApi(formData.syphilisTestData), - ), - hadSyphilis: guardValue( - formData.syphilisTestRequested, - formData.hadSyphilis, - ), - hepATestData: guardValue( - formData.hepATestRequested, - mapHepatitisLaboratoryTestFormDataToApi(formData.hepATestData), - ), - hepBTestData: guardValue( - formData.hepBTestRequested, - mapHepatitisLaboratoryTestFormDataToApi(formData.hepBTestData), - ), - hepCTestData: guardValue( - formData.hepCTestRequested, - mapLaboratoryTestFormDataToApi(formData.hepCTestData), - ), - chlamydiaTestSamples: guardValue( - formData.chlamydiaTestRequested, - mapLaboratoryTestSamplesFormDataToApi(formData.chlamydiaTestData), - ), - gonorrheaTestSamples: guardValue( - formData.gonorrheaTestRequested, - mapLaboratoryTestSamplesFormDataToApi(formData.gonorrheaTestData), - ), - mycoplasmaTestSamples: guardValue( - formData.mycoplasmaTestRequested, - mapLaboratoryTestSamplesFormDataToApi(formData.mycoplasmaTestData), - ), - cancerScreeningTestData: guardValue( - formData.cancerScreeningTestRequested, - mapLaboratoryTestFormDataToApi(formData.cancerScreeningTestData), - ), - hpvTestData: guardValue( - formData.hpvTestRequested, - mapLaboratoryTestFormDataToApi(formData.hpvTestData), - ), - mpoxTestData: guardValue( - formData.mpoxTestRequested, - mapLaboratoryTestFormDataToApi(formData.mpoxTestData), - ), - otherTestName: guardValue( - formData.otherTestRequested, - mapOptionalString(formData.otherTestName), - ), - otherTestData: guardValue( - formData.otherTestRequested, - mapLaboratoryTestFormDataToApi(formData.otherTestData), - ), - }; -} +import { + LaboratoryTestExaminationData, + defaultLaboratoryTestExaminationFormValues, + mapFormValuesToApi, + mapToFormValues, +} from "./helpers"; interface LaboratoryTestExaminationProps { procedureId: string; @@ -260,7 +47,11 @@ export function LaboratoryTestExamination( props: LaboratoryTestExaminationProps, ) { const { procedureId, laboratoryTestExamination: laboratoryTests } = props; - const upsertLaboratoryTests = useUpsertLaboratoryTest(procedureId); + const upsertLaboratoryTestOptions = useUpsertLaboratoryTestOptions({ + procedureId, + }); + const upsertLaboratoryTests = useUpsertLaboratoryTest({ procedureId }); + const onCancel = useOnCancelForm<LaboratoryTestExaminationData>(); function onSubmit(values: LaboratoryTestExaminationData) { return upsertLaboratoryTests.mutateAsync({ @@ -268,24 +59,6 @@ export function LaboratoryTestExamination( }); } - const { openCancelDialog } = useConfirmationDialog(); - - function onCancel( - dirty: boolean, - reset: ( - state?: Partial<FormikState<LaboratoryTestExaminationData>>, - ) => void, - ) { - if (!dirty) { - return; - } - openCancelDialog({ - onConfirm: () => { - reset(); - }, - }); - } - return ( <Formik initialValues={ @@ -296,8 +69,17 @@ export function LaboratoryTestExamination( onSubmit={onSubmit} enableReinitialize > - {({ dirty, resetForm, isSubmitting }) => ( + {({ resetForm, dirty, isSubmitting, values }) => ( <FormPlus sx={{ height: "100%", overflow: "hidden" }}> + <ConfirmLeaveDirtyFormEffect + onSaveMutation={{ + mutationOptions: upsertLaboratoryTestOptions, + variableSupplier: () => ({ + procedureId, + laboratoryTests: mapFormValuesToApi(values), + }), + }} + /> <Box sx={{ pt: 3, @@ -425,7 +207,7 @@ export function LaboratoryTestExamination( </Box> <ExaminationStickyBottomButtonBar isSubmitting={isSubmitting} - onClick={() => onCancel(dirty, resetForm)} + onClick={() => onCancel({ dirty, reset: resetForm })} /> </FormPlus> )} @@ -446,7 +228,11 @@ function ExaminationTabInfo() { > <Stack paddingTop={1}> <Typography>Allgemeine Bemerkung</Typography> - <TextareaField name="generalRemarks" minRows={4} /> + <TextareaFieldWithTextTemplates + name="generalRemarks" + minRows={4} + context={ApiTextTemplateContext.LaboratoryTestsRemark} + /> </Stack> <CheckboxField name="testsConducted" label={"Tests durchgeführt"} /> <CheckboxField name="testsPayed" label={"Tests bezahlt"} /> diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestTemplates.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestTemplates.tsx index ba3f34124..66d6adf9c 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestTemplates.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/LaboratoryTestTemplates.tsx @@ -3,201 +3,21 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiHepatitisLaboratoryTest, - ApiLaboratoryTest, - ApiLaboratoryTestSamples, -} from "@eshg/employee-portal-api/stiProtection"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { Divider, Grid, Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { useFormikContext } from "formik"; import { PropsWithChildren, ReactNode } from "react"; -import { - YesOrNoFieldData, - YesOrNoWithFollowUp, - mapBoolToYesOrNo, - mapYesOrNoToBool, -} from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; +import { YesOrNoWithFollowUp } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; import { SectionGrid, SubRow, } from "@/lib/businessModules/stiProtection/features/procedures/medicalHistory/SectionGrid"; -import { - areAllValuesUndefined, - guardValue, - mapOptionalBool, - mapOptionalString, -} from "@/lib/businessModules/stiProtection/shared/helpers"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; -import { LaboratoryTestExaminationData } from "./LaboratoryTestExamination"; - -export interface LaboratoryTestData { - value: string; - result: YesOrNoFieldData; - remark: string; -} - -export const defaultLaboratoryTestFormData = { - value: "", - result: null, - remark: "", -}; - -export function mapApiLaboratoryTestToFormData( - responseData: ApiLaboratoryTest | undefined, -): LaboratoryTestData { - if (responseData === undefined) { - return defaultLaboratoryTestFormData; - } - return { - value: responseData.value ?? "", - result: mapBoolToYesOrNo(responseData.result), - remark: responseData.remark ?? "", - }; -} - -export function mapLaboratoryTestFormDataToApi( - formData: LaboratoryTestData | null, -): ApiLaboratoryTest | undefined { - if (formData === null) { - return undefined; - } - - const mappedValues = { - value: mapOptionalString(formData.value), - result: mapYesOrNoToBool(formData.result ?? ""), - remark: mapOptionalString(formData.remark), - }; - - if (areAllValuesUndefined(mappedValues)) { - return undefined; - } else { - return mappedValues; - } -} - -export interface HepatitisLaboratoryTestData extends LaboratoryTestData { - infection?: boolean; - vaccineTitre?: boolean; -} - -export const defaultHepatitisLaboratoryTestFormData = { - infection: false, - vaccineTitre: false, - value: "", - result: null, - remark: "", -}; - -export function mapApiHepatitisLaboratoryTestToFormData( - responseData: ApiHepatitisLaboratoryTest | undefined, -): HepatitisLaboratoryTestData { - if (responseData === undefined) { - return defaultHepatitisLaboratoryTestFormData; - } - return { - infection: responseData.infection ?? false, - vaccineTitre: responseData.vaccineTitre ?? false, - value: responseData.value ?? "", - result: mapBoolToYesOrNo(responseData.result), - remark: responseData.remark ?? "", - }; -} - -export function mapHepatitisLaboratoryTestFormDataToApi( - formData: HepatitisLaboratoryTestData | null, -): ApiHepatitisLaboratoryTest | undefined { - if (formData === null) { - return undefined; - } - - const mappedValues = { - infection: mapOptionalBool(formData.infection), - vaccineTitre: mapOptionalBool(formData.vaccineTitre), - value: mapOptionalString(formData.value), - result: mapYesOrNoToBool(formData.result ?? ""), - remark: mapOptionalString(formData.remark), - }; - - if (areAllValuesUndefined(mappedValues)) { - return undefined; - } else { - return mappedValues; - } -} - -export interface LaboratoryTestSamplesData { - oralSampleRequested: boolean; - oralSampleData: LaboratoryTestData; - urethralSampleRequested: boolean; - urethralSampleData: LaboratoryTestData; - analSampleRequested: boolean; - analSampleData: LaboratoryTestData; -} - -export const defaultLaboratoryTestSamplesFormData = { - oralSampleRequested: false, - oralSampleData: defaultLaboratoryTestFormData, - urethralSampleRequested: false, - urethralSampleData: defaultLaboratoryTestFormData, - analSampleRequested: false, - analSampleData: defaultLaboratoryTestFormData, -}; - -export function mapApiLaboratoryTestSamplesToFormData( - responseData: ApiLaboratoryTestSamples | undefined, -): LaboratoryTestSamplesData { - if (responseData === undefined) { - return defaultLaboratoryTestSamplesFormData; - } - - return { - oralSampleRequested: responseData.oralSampleRequested ?? false, - oralSampleData: mapApiLaboratoryTestToFormData(responseData.oralSampleData), - urethralSampleRequested: responseData.urethralSampleRequested ?? false, - urethralSampleData: mapApiLaboratoryTestToFormData( - responseData.urethralSampleData, - ), - analSampleRequested: responseData.analSampleRequested ?? false, - analSampleData: mapApiLaboratoryTestToFormData(responseData.analSampleData), - }; -} - -export function mapLaboratoryTestSamplesFormDataToApi( - formData: LaboratoryTestSamplesData | null, -): ApiLaboratoryTestSamples | undefined { - if (formData === null) { - return undefined; - } - - const mappedValues = { - oralSampleRequested: mapOptionalBool(formData.oralSampleRequested), - oralSampleData: guardValue( - formData.oralSampleRequested, - mapLaboratoryTestFormDataToApi(formData.oralSampleData), - ), - urethralSampleRequested: mapOptionalBool(formData.urethralSampleRequested), - urethralSampleData: guardValue( - formData.urethralSampleRequested, - mapLaboratoryTestFormDataToApi(formData.urethralSampleData), - ), - analSampleRequested: mapOptionalBool(formData.analSampleRequested), - analSampleData: guardValue( - formData.analSampleRequested, - mapLaboratoryTestFormDataToApi(formData.analSampleData), - ), - }; - - if (areAllValuesUndefined(mappedValues)) { - return undefined; - } else { - return mappedValues; - } -} +import { LaboratoryTestExaminationData } from "./helpers"; export interface LaboratoryTestProps extends PropsWithChildren { testRequestedPath: string; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/helpers.ts new file mode 100644 index 000000000..22a34dc95 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/laboratoryTest/helpers.ts @@ -0,0 +1,397 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiHepatitisLaboratoryTest, + ApiLaboratoryTest, + ApiLaboratoryTestExamination, + ApiLaboratoryTestSamples, +} from "@eshg/sti-protection-api"; + +import { + YesOrNoFieldData, + mapBoolToYesOrNo, + mapYesOrNoToBool, +} from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; +import { + areAllValuesUndefined, + guardValue, + mapOptionalBool, + mapOptionalString, +} from "@/lib/businessModules/stiProtection/shared/helpers"; + +export interface LaboratoryTestData { + value: string; + result: YesOrNoFieldData; + remark: string; +} + +export const defaultLaboratoryTestFormData = { + value: "", + result: null, + remark: "", +}; + +export function mapApiLaboratoryTestToFormData( + responseData: ApiLaboratoryTest | undefined, +): LaboratoryTestData { + if (responseData === undefined) { + return defaultLaboratoryTestFormData; + } + return { + value: responseData.value ?? "", + result: mapBoolToYesOrNo(responseData.result), + remark: responseData.remark ?? "", + }; +} + +export function mapLaboratoryTestFormDataToApi( + formData: LaboratoryTestData | null, +): ApiLaboratoryTest | undefined { + if (formData === null) { + return undefined; + } + + const mappedValues = { + value: mapOptionalString(formData.value), + result: mapYesOrNoToBool(formData.result ?? ""), + remark: mapOptionalString(formData.remark), + }; + + if (areAllValuesUndefined(mappedValues)) { + return undefined; + } else { + return mappedValues; + } +} + +export interface HepatitisLaboratoryTestData extends LaboratoryTestData { + infection?: boolean; + vaccineTitre?: boolean; +} + +export const defaultHepatitisLaboratoryTestFormData = { + infection: false, + vaccineTitre: false, + value: "", + result: null, + remark: "", +}; + +export function mapApiHepatitisLaboratoryTestToFormData( + responseData: ApiHepatitisLaboratoryTest | undefined, +): HepatitisLaboratoryTestData { + if (responseData === undefined) { + return defaultHepatitisLaboratoryTestFormData; + } + return { + infection: responseData.infection ?? false, + vaccineTitre: responseData.vaccineTitre ?? false, + value: responseData.value ?? "", + result: mapBoolToYesOrNo(responseData.result), + remark: responseData.remark ?? "", + }; +} + +export function mapHepatitisLaboratoryTestFormDataToApi( + formData: HepatitisLaboratoryTestData | null, +): ApiHepatitisLaboratoryTest | undefined { + if (formData === null) { + return undefined; + } + + const mappedValues = { + infection: mapOptionalBool(formData.infection), + vaccineTitre: mapOptionalBool(formData.vaccineTitre), + value: mapOptionalString(formData.value), + result: mapYesOrNoToBool(formData.result ?? ""), + remark: mapOptionalString(formData.remark), + }; + + if (areAllValuesUndefined(mappedValues)) { + return undefined; + } else { + return mappedValues; + } +} + +export interface LaboratoryTestSamplesData { + oralSampleRequested: boolean; + oralSampleData: LaboratoryTestData; + urethralSampleRequested: boolean; + urethralSampleData: LaboratoryTestData; + analSampleRequested: boolean; + analSampleData: LaboratoryTestData; +} + +export const defaultLaboratoryTestSamplesFormData = { + oralSampleRequested: false, + oralSampleData: defaultLaboratoryTestFormData, + urethralSampleRequested: false, + urethralSampleData: defaultLaboratoryTestFormData, + analSampleRequested: false, + analSampleData: defaultLaboratoryTestFormData, +}; + +export function mapApiLaboratoryTestSamplesToFormData( + responseData: ApiLaboratoryTestSamples | undefined, +): LaboratoryTestSamplesData { + if (responseData === undefined) { + return defaultLaboratoryTestSamplesFormData; + } + + return { + oralSampleRequested: responseData.oralSampleRequested ?? false, + oralSampleData: mapApiLaboratoryTestToFormData(responseData.oralSampleData), + urethralSampleRequested: responseData.urethralSampleRequested ?? false, + urethralSampleData: mapApiLaboratoryTestToFormData( + responseData.urethralSampleData, + ), + analSampleRequested: responseData.analSampleRequested ?? false, + analSampleData: mapApiLaboratoryTestToFormData(responseData.analSampleData), + }; +} + +export function mapLaboratoryTestSamplesFormDataToApi( + formData: LaboratoryTestSamplesData | null, +): ApiLaboratoryTestSamples | undefined { + if (formData === null) { + return undefined; + } + + const mappedValues = { + oralSampleRequested: mapOptionalBool(formData.oralSampleRequested), + oralSampleData: guardValue( + formData.oralSampleRequested, + mapLaboratoryTestFormDataToApi(formData.oralSampleData), + ), + urethralSampleRequested: mapOptionalBool(formData.urethralSampleRequested), + urethralSampleData: guardValue( + formData.urethralSampleRequested, + mapLaboratoryTestFormDataToApi(formData.urethralSampleData), + ), + analSampleRequested: mapOptionalBool(formData.analSampleRequested), + analSampleData: guardValue( + formData.analSampleRequested, + mapLaboratoryTestFormDataToApi(formData.analSampleData), + ), + }; + + if (areAllValuesUndefined(mappedValues)) { + return undefined; + } else { + return mappedValues; + } +} + +export interface LaboratoryTestExaminationData { + sampleBarcode?: string; + generalRemarks?: string; + testsConducted?: boolean; + testsPayed?: boolean; + //Requested Tests + hivTestRequested?: boolean; + syphilisTestRequested?: boolean; + hepATestRequested?: boolean; + hepBTestRequested?: boolean; + hepCTestRequested?: boolean; + chlamydiaTestRequested?: boolean; + gonorrheaTestRequested?: boolean; + mycoplasmaTestRequested?: boolean; + cancerScreeningTestRequested?: boolean; + hpvTestRequested?: boolean; + mpoxTestRequested?: boolean; + otherTestRequested?: boolean; + //Data of Tests + hivTestData: LaboratoryTestData | null; + syphilisTestData: LaboratoryTestData | null; + hadSyphilis?: boolean; + hepATestData: HepatitisLaboratoryTestData | null; + hepBTestData: HepatitisLaboratoryTestData | null; + hepCTestData: LaboratoryTestData | null; + chlamydiaTestData: LaboratoryTestSamplesData | null; + gonorrheaTestData: LaboratoryTestSamplesData | null; + mycoplasmaTestData: LaboratoryTestSamplesData | null; + cancerScreeningTestData: LaboratoryTestData | null; + hpvTestData: LaboratoryTestData | null; + mpoxTestData: LaboratoryTestData | null; + otherTestName?: string; + otherTestData: LaboratoryTestData | null; +} + +export function mapToFormValues( + responseData: ApiLaboratoryTestExamination, +): LaboratoryTestExaminationData { + return { + sampleBarcode: responseData.sampleBarcode ?? "", + generalRemarks: responseData.generalRemarks ?? "", + testsConducted: responseData.testsConducted ?? false, + testsPayed: responseData.testsPayed ?? false, + //Requested Tests + hivTestRequested: responseData.hivTestRequested ?? false, + syphilisTestRequested: responseData.syphilisTestRequested ?? false, + hepATestRequested: responseData.hepATestRequested ?? false, + hepBTestRequested: responseData.hepBTestRequested ?? false, + hepCTestRequested: responseData.hepCTestRequested ?? false, + chlamydiaTestRequested: responseData.chlamydiaTestRequested ?? false, + gonorrheaTestRequested: responseData.gonorrheaTestRequested ?? false, + mycoplasmaTestRequested: responseData.mycoplasmaTestRequested ?? false, + cancerScreeningTestRequested: + responseData.cancerScreeningTestRequested ?? false, + hpvTestRequested: responseData.hpvTestRequested ?? false, + mpoxTestRequested: responseData.mpoxTestRequested ?? false, + otherTestRequested: responseData.otherTestRequested ?? false, + //Data of Tests + hivTestData: mapApiLaboratoryTestToFormData(responseData.hivTestData), + syphilisTestData: mapApiLaboratoryTestToFormData( + responseData.syphilisTestData, + ), + hadSyphilis: responseData.hadSyphilis ?? false, + hepATestData: mapApiHepatitisLaboratoryTestToFormData( + responseData.hepATestData, + ), + hepBTestData: mapApiHepatitisLaboratoryTestToFormData( + responseData.hepBTestData, + ), + hepCTestData: mapApiLaboratoryTestToFormData(responseData.hepCTestData), + chlamydiaTestData: mapApiLaboratoryTestSamplesToFormData( + responseData.chlamydiaTestSamples, + ), + gonorrheaTestData: mapApiLaboratoryTestSamplesToFormData( + responseData.gonorrheaTestSamples, + ), + mycoplasmaTestData: mapApiLaboratoryTestSamplesToFormData( + responseData.mycoplasmaTestSamples, + ), + cancerScreeningTestData: mapApiLaboratoryTestToFormData( + responseData.cancerScreeningTestData, + ), + hpvTestData: mapApiLaboratoryTestToFormData(responseData.hpvTestData), + mpoxTestData: mapApiLaboratoryTestToFormData(responseData.mpoxTestData), + otherTestName: responseData.otherTestRequested + ? (responseData.otherTestName ?? "") + : "", + otherTestData: mapApiLaboratoryTestToFormData(responseData.otherTestData), + }; +} + +export function defaultLaboratoryTestExaminationFormValues(): LaboratoryTestExaminationData { + return { + sampleBarcode: "", + generalRemarks: "", + testsConducted: false, + testsPayed: false, + //Requested Tests + hivTestRequested: false, + syphilisTestRequested: false, + hepATestRequested: false, + hepBTestRequested: false, + hepCTestRequested: false, + chlamydiaTestRequested: false, + gonorrheaTestRequested: false, + mycoplasmaTestRequested: false, + cancerScreeningTestRequested: false, + hpvTestRequested: false, + mpoxTestRequested: false, + otherTestRequested: false, + //Data of Tests + hivTestData: defaultLaboratoryTestFormData, + syphilisTestData: defaultLaboratoryTestFormData, + hadSyphilis: false, + hepATestData: defaultHepatitisLaboratoryTestFormData, + hepBTestData: defaultHepatitisLaboratoryTestFormData, + hepCTestData: defaultLaboratoryTestFormData, + chlamydiaTestData: defaultLaboratoryTestSamplesFormData, + gonorrheaTestData: defaultLaboratoryTestSamplesFormData, + mycoplasmaTestData: defaultLaboratoryTestSamplesFormData, + cancerScreeningTestData: defaultLaboratoryTestFormData, + hpvTestData: defaultLaboratoryTestFormData, + mpoxTestData: defaultLaboratoryTestFormData, + otherTestName: "", + otherTestData: defaultLaboratoryTestFormData, + }; +} + +export function mapFormValuesToApi( + formData: LaboratoryTestExaminationData, +): ApiLaboratoryTestExamination { + return { + sampleBarcode: mapOptionalString(formData.sampleBarcode), + generalRemarks: mapOptionalString(formData.generalRemarks), + testsConducted: formData.testsConducted ?? false, + testsPayed: formData.testsPayed ?? false, + //Requested Tests + hivTestRequested: formData.hivTestRequested ?? false, + syphilisTestRequested: formData.syphilisTestRequested ?? false, + hepATestRequested: formData.hepATestRequested ?? false, + hepBTestRequested: formData.hepBTestRequested ?? false, + hepCTestRequested: formData.hepCTestRequested ?? false, + chlamydiaTestRequested: formData.chlamydiaTestRequested ?? false, + gonorrheaTestRequested: formData.gonorrheaTestRequested ?? false, + mycoplasmaTestRequested: formData.mycoplasmaTestRequested ?? false, + cancerScreeningTestRequested: + formData.cancerScreeningTestRequested ?? false, + hpvTestRequested: formData.hpvTestRequested ?? false, + mpoxTestRequested: formData.mpoxTestRequested ?? false, + otherTestRequested: formData.otherTestRequested ?? false, + //Data of Tests + hivTestData: guardValue( + formData.hivTestRequested, + mapLaboratoryTestFormDataToApi(formData.hivTestData), + ), + syphilisTestData: guardValue( + formData.syphilisTestRequested, + mapLaboratoryTestFormDataToApi(formData.syphilisTestData), + ), + hadSyphilis: guardValue( + formData.syphilisTestRequested, + formData.hadSyphilis, + ), + hepATestData: guardValue( + formData.hepATestRequested, + mapHepatitisLaboratoryTestFormDataToApi(formData.hepATestData), + ), + hepBTestData: guardValue( + formData.hepBTestRequested, + mapHepatitisLaboratoryTestFormDataToApi(formData.hepBTestData), + ), + hepCTestData: guardValue( + formData.hepCTestRequested, + mapLaboratoryTestFormDataToApi(formData.hepCTestData), + ), + chlamydiaTestSamples: guardValue( + formData.chlamydiaTestRequested, + mapLaboratoryTestSamplesFormDataToApi(formData.chlamydiaTestData), + ), + gonorrheaTestSamples: guardValue( + formData.gonorrheaTestRequested, + mapLaboratoryTestSamplesFormDataToApi(formData.gonorrheaTestData), + ), + mycoplasmaTestSamples: guardValue( + formData.mycoplasmaTestRequested, + mapLaboratoryTestSamplesFormDataToApi(formData.mycoplasmaTestData), + ), + cancerScreeningTestData: guardValue( + formData.cancerScreeningTestRequested, + mapLaboratoryTestFormDataToApi(formData.cancerScreeningTestData), + ), + hpvTestData: guardValue( + formData.hpvTestRequested, + mapLaboratoryTestFormDataToApi(formData.hpvTestData), + ), + mpoxTestData: guardValue( + formData.mpoxTestRequested, + mapLaboratoryTestFormDataToApi(formData.mpoxTestData), + ), + otherTestName: guardValue( + formData.otherTestRequested, + mapOptionalString(formData.otherTestName), + ), + otherTestData: guardValue( + formData.otherTestRequested, + mapLaboratoryTestFormDataToApi(formData.otherTestData), + ), + }; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestExamination.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestExamination.tsx index 88c5ab7ee..c62b38b01 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestExamination.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestExamination.tsx @@ -3,126 +3,38 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiRapidTestExamination } from "@eshg/employee-portal-api/stiProtection"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; +import { + ApiRapidTestExamination, + ApiTextTemplateContext, +} from "@eshg/sti-protection-api"; import { Box, Divider, Grid, Sheet, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; -import { useUpsertRapidTest } from "@/lib/businessModules/stiProtection/api/mutations/examination"; +import { + useUpsertRapidTestOptions, + useUpsertRapidTests, +} from "@/lib/businessModules/stiProtection/api/mutations/examination"; +import { TextareaFieldWithTextTemplates } from "@/lib/businessModules/stiProtection/components/textTemplates/TextareaFieldWithTextTemplates"; import { ExaminationStickyBottomButtonBar } from "@/lib/businessModules/stiProtection/features/procedures/examination/ExaminationStickyBottomButtonBar"; import { ExaminationTabNavPanel } from "@/lib/businessModules/stiProtection/features/procedures/examination/ExaminationTabNavPanel"; import { useOnCancelForm } from "@/lib/businessModules/stiProtection/shared/helpers"; +import { ConfirmLeaveDirtyFormEffect } from "@/lib/shared/components/form/ConfirmLeaveDirtyFormEffect"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; -import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; import { SidePanel } from "@/lib/shared/components/sidePanel/SidePanel"; import { SidePanelTitle } from "@/lib/shared/components/sidePanel/SidePanelTitle"; import { - RapidTestData, RapidTestWithBooleanResult, RapidTestWithTextResult, RapidTestWithUnitStringResult, - mapRapidTestToApi, - mapRapidTestToForm, } from "./RapidTestTemplates"; - -export interface RapidTestExaminationData { - hivTestRequested: boolean; - hivTestData: RapidTestData | null; - syphilisTestRequested: boolean; - syphilisTestData: RapidTestData | null; - ultrasoundTestRequested: boolean; - ultrasoundTestResult: string; - pregnancyTestRequested: boolean; - pregnancyTestData: RapidTestData | null; - bloodPressureTestRequested: boolean; - bloodPressureTestResult: string; - pulseTestRequested: boolean; - pulseTestResult: string; - urineTestRequested: boolean; - urineTestResult: string; - generalRemarks: string; - testsPayed: boolean; -} - -function mapToFormValues( - responseData: ApiRapidTestExamination, -): RapidTestExaminationData { - return { - hivTestRequested: responseData.hivRequested, - hivTestData: mapRapidTestToForm(responseData.hivData), - syphilisTestRequested: responseData.syphilisRequested, - syphilisTestData: mapRapidTestToForm(responseData.syphilisData), - ultrasoundTestRequested: responseData.ultrasoundRequested, - ultrasoundTestResult: responseData.ultrasoundData ?? "", - pregnancyTestRequested: responseData.pregnancyTestRequested, - pregnancyTestData: mapRapidTestToForm(responseData.pregnancyTestData), - bloodPressureTestRequested: responseData.bloodPressureRequested, - bloodPressureTestResult: responseData.bloodPressureData ?? "", - pulseTestRequested: responseData.pulseRequested, - pulseTestResult: responseData.pulseData ?? "", - urineTestRequested: responseData.urinalysisRequested, - urineTestResult: responseData.urinalysisData ?? "", - generalRemarks: responseData.generalComments ?? "", - testsPayed: responseData.testsPayed, - }; -} - -function defaultRapidTestExaminationFormValues(): RapidTestExaminationData { - return { - hivTestRequested: false, - hivTestData: mapRapidTestToForm(), - syphilisTestRequested: false, - syphilisTestData: mapRapidTestToForm(), - ultrasoundTestRequested: false, - ultrasoundTestResult: "", - pregnancyTestRequested: false, - pregnancyTestData: mapRapidTestToForm(), - bloodPressureTestRequested: false, - bloodPressureTestResult: "", - pulseTestRequested: false, - pulseTestResult: "", - urineTestRequested: false, - urineTestResult: "", - generalRemarks: "", - testsPayed: false, - }; -} - -function mapFormValuesToApi( - values: RapidTestExaminationData, -): ApiRapidTestExamination { - return { - generalComments: values.generalRemarks ?? undefined, - testsPayed: values.testsPayed, - hivRequested: values.hivTestRequested, - syphilisRequested: values.syphilisTestRequested, - pregnancyTestRequested: values.pregnancyTestRequested, - ultrasoundRequested: values.ultrasoundTestRequested, - bloodPressureRequested: values.bloodPressureTestRequested, - pulseRequested: values.pulseTestRequested, - urinalysisRequested: values.urineTestRequested, - hivData: values.hivTestRequested - ? mapRapidTestToApi(values.hivTestData) - : undefined, - syphilisData: values.syphilisTestRequested - ? mapRapidTestToApi(values.syphilisTestData) - : undefined, - pregnancyTestData: values.pregnancyTestRequested - ? mapRapidTestToApi(values.pregnancyTestData) - : undefined, - ultrasoundData: values.ultrasoundTestRequested - ? values.ultrasoundTestResult - : undefined, - bloodPressureData: values.bloodPressureTestRequested - ? values.bloodPressureTestResult - : undefined, - pulseData: values.pulseTestRequested ? values.pulseTestResult : undefined, - urinalysisData: values.urineTestRequested - ? values.urineTestResult - : undefined, - }; -} +import { + RapidTestExaminationData, + defaultRapidTestExaminationFormValues, + mapFormValuesToApi, + mapToFormValues, +} from "./helpers"; interface RapidTestExaminationProps { procedureId: string; @@ -131,7 +43,9 @@ interface RapidTestExaminationProps { export function RapidTestExamination(props: RapidTestExaminationProps) { const { procedureId, rapidTestExamination: rapidTests } = props; - const upsertRapidTests = useUpsertRapidTest(procedureId); + const upsertRapidTestOptions = useUpsertRapidTestOptions({ procedureId }); + const upsertRapidTests = useUpsertRapidTests({ procedureId }); + const onCancel = useOnCancelForm<RapidTestExaminationData>(); function onSubmit(values: RapidTestExaminationData) { return upsertRapidTests.mutateAsync({ @@ -139,8 +53,6 @@ export function RapidTestExamination(props: RapidTestExaminationProps) { }); } - const onCancel = useOnCancelForm<RapidTestExaminationData>(); - return ( <Formik initialValues={ @@ -151,8 +63,17 @@ export function RapidTestExamination(props: RapidTestExaminationProps) { onSubmit={onSubmit} enableReinitialize > - {({ dirty, resetForm, isSubmitting }) => ( + {({ dirty, resetForm, isSubmitting, values }) => ( <FormPlus sx={{ height: "100%", overflow: "hidden" }}> + <ConfirmLeaveDirtyFormEffect + onSaveMutation={{ + mutationOptions: upsertRapidTestOptions, + variableSupplier: () => ({ + procedureId, + rapidTests: mapFormValuesToApi(values), + }), + }} + /> <Box sx={{ pt: 3, @@ -254,7 +175,11 @@ function ExaminationTabInfo() { > <Stack paddingTop={1}> <Typography>Allgemeine Bemerkung</Typography> - <TextareaField name="generalRemarks" minRows={4} /> + <TextareaFieldWithTextTemplates + name="generalRemarks" + minRows={4} + context={ApiTextTemplateContext.RapidTestsRemark} + /> </Stack> <CheckboxField name="testsPayed" label="Tests bezahlt" /> </Stack> diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestTemplates.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestTemplates.tsx index 593ed00c0..cd5a37e91 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestTemplates.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/RapidTestTemplates.tsx @@ -3,61 +3,18 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiRapidTestData } from "@eshg/employee-portal-api/stiProtection"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; -import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { isEmptyString } from "@eshg/lib-portal/helpers/guards"; import { Grid, Stack } from "@mui/joy"; import { useFormikContext } from "formik"; import { PropsWithChildren } from "react"; -import { - YesOrNoFieldData, - YesOrNoWithFollowUp, - mapBoolToYesOrNo, - mapYesOrNoToBool, -} from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; +import { YesOrNoWithFollowUp } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; import { TextareaField } from "@/lib/shared/components/formFields/TextareaField"; -import { RapidTestExaminationData } from "./RapidTestExamination"; - -export interface RapidTestData { - number?: string; - result: YesOrNoFieldData; -} - -export function mapRapidTestToForm(testData?: ApiRapidTestData): RapidTestData { - if (testData == undefined) { - return { - number: "", - result: null, - }; - } - return { - number: testData.number, - result: mapBoolToYesOrNo(testData.result), - }; -} - -export function mapRapidTestToApi( - formData: RapidTestData | null, -): ApiRapidTestData | undefined { - if (formData === null) { - return undefined; - } - - const resultValue = mapYesOrNoToBool(formData.result); - if (resultValue === undefined) { - return undefined; - } - - return { - number: mapOptionalValue(formData.number?.trim()), - result: !!resultValue, - }; -} +import { RapidTestExaminationData } from "./helpers"; export interface RapidTestProps extends PropsWithChildren { name: string; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/helpers.ts new file mode 100644 index 000000000..2eff53305 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/examination/rapidTest/helpers.ts @@ -0,0 +1,150 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +import { + ApiRapidTestData, + ApiRapidTestExamination, +} from "@eshg/sti-protection-api"; + +import { + YesOrNoFieldData, + mapBoolToYesOrNo, + mapYesOrNoToBool, +} from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; + +export interface RapidTestData { + number?: string; + result: YesOrNoFieldData; +} + +export interface RapidTestExaminationData { + hivTestRequested: boolean; + hivTestData: RapidTestData | null; + syphilisTestRequested: boolean; + syphilisTestData: RapidTestData | null; + ultrasoundTestRequested: boolean; + ultrasoundTestResult: string; + pregnancyTestRequested: boolean; + pregnancyTestData: RapidTestData | null; + bloodPressureTestRequested: boolean; + bloodPressureTestResult: string; + pulseTestRequested: boolean; + pulseTestResult: string; + urineTestRequested: boolean; + urineTestResult: string; + generalRemarks: string; + testsPayed: boolean; +} + +export function mapRapidTestToForm(testData?: ApiRapidTestData): RapidTestData { + if (testData == undefined) { + return { + number: "", + result: null, + }; + } + return { + number: testData.number, + result: mapBoolToYesOrNo(testData.result), + }; +} + +export function mapRapidTestToApi( + formData: RapidTestData | null, +): ApiRapidTestData | undefined { + if (formData === null) { + return undefined; + } + + const resultValue = mapYesOrNoToBool(formData.result); + if (resultValue === undefined) { + return undefined; + } + + return { + number: mapOptionalValue(formData.number?.trim()), + result: resultValue, + }; +} + +export function mapToFormValues( + responseData: ApiRapidTestExamination, +): RapidTestExaminationData { + return { + hivTestRequested: responseData.hivRequested, + hivTestData: mapRapidTestToForm(responseData.hivData), + syphilisTestRequested: responseData.syphilisRequested, + syphilisTestData: mapRapidTestToForm(responseData.syphilisData), + ultrasoundTestRequested: responseData.ultrasoundRequested, + ultrasoundTestResult: responseData.ultrasoundData ?? "", + pregnancyTestRequested: responseData.pregnancyTestRequested, + pregnancyTestData: mapRapidTestToForm(responseData.pregnancyTestData), + bloodPressureTestRequested: responseData.bloodPressureRequested, + bloodPressureTestResult: responseData.bloodPressureData ?? "", + pulseTestRequested: responseData.pulseRequested, + pulseTestResult: responseData.pulseData ?? "", + urineTestRequested: responseData.urinalysisRequested, + urineTestResult: responseData.urinalysisData ?? "", + generalRemarks: responseData.generalComments ?? "", + testsPayed: responseData.testsPayed, + }; +} + +export function defaultRapidTestExaminationFormValues(): RapidTestExaminationData { + return { + hivTestRequested: false, + hivTestData: mapRapidTestToForm(), + syphilisTestRequested: false, + syphilisTestData: mapRapidTestToForm(), + ultrasoundTestRequested: false, + ultrasoundTestResult: "", + pregnancyTestRequested: false, + pregnancyTestData: mapRapidTestToForm(), + bloodPressureTestRequested: false, + bloodPressureTestResult: "", + pulseTestRequested: false, + pulseTestResult: "", + urineTestRequested: false, + urineTestResult: "", + generalRemarks: "", + testsPayed: false, + }; +} + +export function mapFormValuesToApi( + values: RapidTestExaminationData, +): ApiRapidTestExamination { + return { + generalComments: values.generalRemarks ?? undefined, + testsPayed: values.testsPayed, + hivRequested: values.hivTestRequested, + syphilisRequested: values.syphilisTestRequested, + pregnancyTestRequested: values.pregnancyTestRequested, + ultrasoundRequested: values.ultrasoundTestRequested, + bloodPressureRequested: values.bloodPressureTestRequested, + pulseRequested: values.pulseTestRequested, + urinalysisRequested: values.urineTestRequested, + hivData: values.hivTestRequested + ? mapRapidTestToApi(values.hivTestData) + : undefined, + syphilisData: values.syphilisTestRequested + ? mapRapidTestToApi(values.syphilisTestData) + : undefined, + pregnancyTestData: values.pregnancyTestRequested + ? mapRapidTestToApi(values.pregnancyTestData) + : undefined, + ultrasoundData: values.ultrasoundTestRequested + ? values.ultrasoundTestResult + : undefined, + bloodPressureData: values.bloodPressureTestRequested + ? values.bloodPressureTestResult + : undefined, + pulseData: values.pulseTestRequested ? values.pulseTestResult : undefined, + urinalysisData: values.urineTestRequested + ? values.urineTestResult + : undefined, + }; +} 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 2657b8b32..6add93d84 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 @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { MonthAndYear } from "@eshg/lib-portal/components/formFields/MonthAndYearFields"; import { ApiConcern, ApiCreateMedicalHistoryRequest, @@ -16,8 +17,7 @@ import { ApiSexWorkLocation, ApiSexualOrientation, ApiVaccination, -} from "@eshg/employee-portal-api/stiProtection"; -import { MonthAndYear } from "@eshg/lib-portal/components/formFields/MonthAndYearFields"; +} from "@eshg/sti-protection-api"; import { YesOrNoFieldData } from "@/lib/businessModules/stiProtection/components/procedures/procedureDetails/YesOrNoWithFollowUp"; 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 ebb156ae1..dc34fd52c 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 @@ -5,15 +5,15 @@ "use client"; -import { - ApiConcern, - ApiGetMedicalHistory200Response, - ApiStiProtectionProcedure, -} from "@eshg/employee-portal-api/stiProtection"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { HorizontalField } from "@eshg/lib-portal/components/formFields/HorizontalField"; import { InternalLinkButton } from "@eshg/lib-portal/components/navigation/InternalLinkButton"; +import { + ApiConcern, + ApiGetMedicalHistory200Response, + ApiStiProtectionProcedure, +} from "@eshg/sti-protection-api"; import Print from "@mui/icons-material/Print"; import { Button, Divider, Sheet, Typography, styled } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; @@ -108,7 +108,7 @@ export function MedicalHistoryForm({ setOpenFile(true); } - function onSubmit(values: MedicalHistoryFormData) { + async function onSubmit(values: MedicalHistoryFormData) { return upsertMedicalHistory.mutateAsync( mapFormValuesToApi(stiProcedure, values), ); @@ -122,6 +122,7 @@ export function MedicalHistoryForm({ : defaultMedicalHistoryFormValues() } onSubmit={onSubmit} + enableReinitialize > {({ isSubmitting, values }) => ( <FormPlus> 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 index c3949a033..80779c9a8 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/helpers.ts @@ -3,6 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { + MonthAndYear, + mapMonthAndYear, +} from "@eshg/lib-portal/components/formFields/MonthAndYearFields"; +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ApiCreateMedicalHistoryRequest, ApiExamination, @@ -14,12 +19,7 @@ import { ApiSexWorkMedicalHistory, ApiSexWorkRiskContact, ApiStiProtectionProcedure, -} from "@eshg/employee-portal-api/stiProtection"; -import { - MonthAndYear, - mapMonthAndYear, -} from "@eshg/lib-portal/components/formFields/MonthAndYearFields"; -import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +} from "@eshg/sti-protection-api"; import { mapBoolToYesOrNo, diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/options.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/options.ts index 5d8915339..bccc076fa 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/options.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/medicalHistory/options.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { ApiGender, ApiPartnerRiskFactors, @@ -12,8 +13,7 @@ import { ApiSexWorkLocation, ApiSexualOrientation, ApiVaccination, -} from "@eshg/employee-portal-api/stiProtection"; -import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; +} from "@eshg/sti-protection-api"; import { sexualContactNames as sexualContactGenderNames, diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/translations.ts b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/translations.ts index 14465e885..500b75b74 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/translations.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/features/procedures/translations.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiWaitingStatus } from "@eshg/employee-portal-api/stiProtection"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { EnumMap } from "@eshg/lib-portal/types/helpers"; +import { ApiWaitingStatus } from "@eshg/sti-protection-api"; export const WAITING_STATUS_VALUES: EnumMap<ApiWaitingStatus> = { [ApiWaitingStatus.WaitingForConsultation]: "Wartet auf Beratung", diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/StatusChip.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/StatusChip.tsx index 65af32d73..cbe72ca59 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/StatusChip.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/StatusChip.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiWaitingStatus } from "@eshg/employee-portal-api/stiProtection"; +import { ApiWaitingStatus } from "@eshg/sti-protection-api"; import { Chip, ChipProps } from "@mui/joy"; import { WAITING_STATUS_VALUES } from "@/lib/businessModules/stiProtection/features/procedures/translations"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/WaitingRoomTable.tsx b/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/WaitingRoomTable.tsx index 7101d7a5e..4615807f9 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/WaitingRoomTable.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/features/waitingRoom/WaitingRoomTable.tsx @@ -5,11 +5,11 @@ "use client"; +import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; import { ApiWaitingRoomProcedure, ApiWaitingRoomSortKey, -} from "@eshg/employee-portal-api/stiProtection"; -import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; +} from "@eshg/sti-protection-api"; import { ColumnSort, createColumnHelper } from "@tanstack/react-table"; import { differenceInMinutes } from "date-fns"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/constants.ts b/employee-portal/src/lib/businessModules/stiProtection/shared/constants.ts index 19d7f01c2..1bb9ce0c4 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/shared/constants.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/constants.ts @@ -3,18 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { EnumMap } from "@eshg/lib-portal/types/helpers"; import { ApiAppointmentStatus, ApiAppointmentType, ApiConcern, ApiExamination, ApiGender, + ApiLabStatus, ApiProcedureStatus, ApiProcedureType, ApiSexualOrientation, ApiTaskType, -} from "@eshg/employee-portal-api/stiProtection"; -import { EnumMap } from "@eshg/lib-portal/types/helpers"; +} from "@eshg/sti-protection-api"; import { DefaultColorPalette } from "@mui/joy/styles/types"; export const procedureTypes = [ApiProcedureType.StiProtection]; @@ -112,3 +113,9 @@ export const examinableIllnessNames = { hiv: "HIV", syphilis: "Syphilis (Lues)", } as const satisfies Record<ExaminableIllnesses, string>; + +export const LAB_STATUS_VALUES: EnumMap<ApiLabStatus> = { + [ApiLabStatus.Open]: "Offen", + [ApiLabStatus.InProgress]: "In Bearbeitung", + [ApiLabStatus.Closed]: "Geschlossen", +}; diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts b/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts index 5639a3ce6..40551378b 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/helpers.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; import { ApiAppointmentHistoryEntry, ApiAppointmentType, ApiConcern, ApiStiProtectionProcedure, ApiStiProtectionProcedureOverview, -} from "@eshg/employee-portal-api/stiProtection"; -import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; +} from "@eshg/sti-protection-api"; import { FormikState } from "formik"; import { useConfirmationDialog } from "@/lib/shared/hooks/useConfirmationDialog"; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/AppointmentForm.tsx similarity index 87% rename from employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx rename to employee-portal/src/lib/businessModules/stiProtection/shared/procedure/AppointmentForm.tsx index 62541b25a..cf1e60281 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AppointmentForm.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/AppointmentForm.tsx @@ -3,11 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiAppointmentBookingType, - ApiAppointmentType, - ApiConcern, -} from "@eshg/employee-portal-api/stiProtection"; import { Row } from "@eshg/lib-portal/components/Row"; import { NumberField } from "@eshg/lib-portal/components/formFields/NumberField"; import { @@ -17,6 +12,12 @@ import { import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ifDefined } from "@eshg/lib-portal/helpers/ifDefined"; +import { + ApiAppointment, + ApiAppointmentBookingType, + ApiAppointmentType, + ApiConcern, +} from "@eshg/sti-protection-api"; import { UnfoldMore } from "@mui/icons-material"; import { Box, Button, FormLabel, Stack, Typography } from "@mui/joy"; import { addMinutes, startOfHour } from "date-fns"; @@ -29,7 +30,6 @@ import { RadioSheets, } from "@/lib/businessModules/stiProtection/components/RadioSheets"; import { appointmentOptionsByConcern } from "@/lib/businessModules/stiProtection/components/appointmentBlocks/options"; -import { CreateAppointmentForm } from "@/lib/businessModules/stiProtection/features/procedures/details/CreateAppointmentSidebar"; import { concernToAppointmentType } from "@/lib/businessModules/stiProtection/shared/helpers"; import { DateTimeField } from "@/lib/shared/components/formFields/DateTimeField"; import { @@ -37,13 +37,25 @@ import { validateTodayOrFutureDate, } from "@/lib/shared/helpers/validators"; -import { AddNewProcedureForm } from "./AddNewProcedureSidebar"; +export interface CreateAppointmentForm { + appointmentType?: ApiAppointmentType | "" | null; + appointmentBookingType?: ApiAppointmentBookingType | ""; + blockAppointment?: null | ApiAppointment; + concern?: ApiConcern | "RESULTS_REVIEW" | ""; + customAppointmentDate: string; + customAppointmentDuration: string; +} -export type CombinedAppointmentForm = Partial< - AddNewProcedureForm & CreateAppointmentForm ->; +export const initialValues: CreateAppointmentForm = { + appointmentType: null, + appointmentBookingType: "", + blockAppointment: null, + concern: "", + customAppointmentDate: "", + customAppointmentDuration: "", +}; -function ConnectedAppointmentPicker({ +function ConnectedAppointmentPicker<TForm extends CreateAppointmentForm>({ name, appointmentType, }: { @@ -52,7 +64,7 @@ function ConnectedAppointmentPicker({ }) { const { values: { blockAppointment }, - } = useFormikContext<CombinedAppointmentForm>(); + } = useFormikContext<TForm>(); const initialMonth = blockAppointment?.start ?? null; const [currentMonth, setCurrentMonth] = useState(initialMonth ?? new Date()); @@ -85,14 +97,14 @@ function ConnectedAppointmentPicker({ ); } -export function AppointmentForm({ +export function AppointmentForm<TForm extends CreateAppointmentForm>({ startingConcern, editAppointmentType, }: Readonly<{ startingConcern?: ApiConcern; editAppointmentType?: ApiAppointmentType; }>) { - const { values } = useFormikContext<CombinedAppointmentForm>(); + const { values } = useFormikContext<TForm>(); const customSectionSelected = values.appointmentBookingType === ApiAppointmentBookingType.UserDefined; @@ -160,8 +172,8 @@ export function AppointmentForm({ ); } -function CustomAppointmentQuickButtons() { - const { setFieldValue } = useFormikContext<AddNewProcedureForm>(); +function CustomAppointmentQuickButtons<TForm>() { + const { setFieldValue } = useFormikContext<TForm>(); function setCustomAppointment(inMinutes: number) { const now = new Date(); const roundTo = 5; diff --git a/employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SharePinModal.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/SharePinModal.tsx similarity index 100% rename from employee-portal/src/lib/businessModules/stiProtection/features/procedures/addNewProcedure/SharePinModal.tsx rename to employee-portal/src/lib/businessModules/stiProtection/shared/procedure/SharePinModal.tsx diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/helpers.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/helpers.tsx new file mode 100644 index 000000000..df8cd048b --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/helpers.tsx @@ -0,0 +1,15 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ApiConcern } from "@eshg/sti-protection-api"; + +import { CONCERN_VALUES } from "@/lib/businessModules/stiProtection/shared/constants"; + +export const CONCERN_OPTIONS = Object.entries(CONCERN_VALUES).map( + ([value, label]) => ({ + content: <b>{label}</b>, + value: value as ApiConcern, + }), +); diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/mappers.ts b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/mappers.ts new file mode 100644 index 000000000..fd9235590 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/mappers.ts @@ -0,0 +1,94 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { GENDER_OPTIONS } from "@eshg/lib-portal/components/formFields/constants"; +import { countryOptions } from "@eshg/lib-portal/helpers/countryOption"; +import { isNonEmptyString } from "@eshg/lib-portal/helpers/guards"; +import { + ApiAppointmentBookingType, + ApiCreateFollowUpProcedureRequest, + ApiCreateProcedureRequest, + ApiGender, +} from "@eshg/sti-protection-api"; +import { differenceInMinutes } from "date-fns/differenceInMinutes"; + +import { + AddNewProcedureForm, + CombinedAppointmentForm, +} from "@/lib/businessModules/stiProtection/features/procedures/addNewProcedure/AddNewProcedureSidebar"; +import { CreateFollowUpProcedureForm } from "@/lib/businessModules/stiProtection/features/procedures/details/CreateFollowUpProcedureSidebar"; +import { + deleteUndefined, + optionalInt, +} from "@/lib/businessModules/stiProtection/shared/helpers"; + +import { CONCERN_OPTIONS } from "./helpers"; + +export function mapFollowUpProcedureFormToApi( + form: CreateFollowUpProcedureForm, +): ApiCreateFollowUpProcedureRequest { + if (!form.appointmentBookingType) { + throw new Error("Appointment booking type must be defined"); + } + + const isCustomAppointment = + form.appointmentBookingType === ApiAppointmentBookingType.UserDefined; + + const appointmentStart = isCustomAppointment + ? new Date(form.customAppointmentDate) + : form.blockAppointment?.start; + + if (!appointmentStart) { + throw new Error("Appointment start must be defined"); + } + + const blockAppointmentEnd = form.blockAppointment?.end; + if (!isCustomAppointment && blockAppointmentEnd == null) { + throw new Error("Appointment end must be defined"); + } + + return deleteUndefined({ + appointmentBookingType: form.appointmentBookingType, + concern: CONCERN_OPTIONS.find((t) => t.value === form.concern)?.value, + durationInMinutes: isCustomAppointment + ? optionalInt(form.customAppointmentDuration) + : differenceInMinutes(blockAppointmentEnd!, appointmentStart), + appointmentStart, + }); +} + +export function mapProcedureFormToApi( + form: AddNewProcedureForm, +): ApiCreateProcedureRequest { + if (!form.yearOfBirth) { + throw new Error("Year of birth must be defined"); + } + if (!form.appointmentBookingType) { + throw new Error("Appointment booking type must be defined"); + } + + return deleteUndefined({ + ...mapFollowUpProcedureFormToApi(form), + countryOfBirth: countryOptions().find( + (t) => t.value === form.countryOfBirth, + )?.value, + gender: GENDER_OPTIONS.find((t) => t.value === form.gender)?.value as + | ApiGender + | undefined, + inGermanySince: optionalInt(form.inGermanySince), + yearOfBirth: parseInt(form.yearOfBirth, 10), + }); +} + +export function getAppointmentDate(form: CombinedAppointmentForm) { + const customAppointmentDate = isNonEmptyString(form.customAppointmentDate) + ? new Date(form.customAppointmentDate) + : undefined; + const date = + form.appointmentBookingType === ApiAppointmentBookingType.AppointmentBlock + ? form.blockAppointment?.start + : customAppointmentDate; + return date ?? undefined; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps.ts b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps.ts new file mode 100644 index 000000000..f5d977987 --- /dev/null +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/procedure/useFormWithSteps.ts @@ -0,0 +1,59 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useReducer } from "react"; + +export interface Step<TProps, TForm> { + title: string; + subTitle: string; + fields: (props: TProps) => JSX.Element; + validate?: (form: TForm) => Partial<Record<keyof TForm, string>> | undefined; +} + +export interface useFormWithStepsArgs<TForm, TStep> { + steps: Step<TStep, TForm>[]; + onFinalSubmit: (form: TForm) => Promise<unknown>; +} + +export function useFormWithSteps<TForm, TStep>( + args: useFormWithStepsArgs<TForm, TStep>, +) { + const { steps, onFinalSubmit } = args; + + const lastStepIndex = steps.length - 1; + const [stepIndex, changeToStep] = useReducer( + (_index: number, newIndex: number) => + Math.max(Math.min(newIndex, lastStepIndex), 0), + 0, + ); + + const step = steps[stepIndex]!; + + const isOnFirstStep = stepIndex === 0; + const isOnLastStep = stepIndex === lastStepIndex; + + function handleNext(newValues: TForm) { + if (isOnLastStep) { + return onFinalSubmit(newValues); + } + changeToStep(stepIndex + 1); + } + + function handlePrev() { + changeToStep(stepIndex - 1); + } + + const Fields = step.fields; + + return { + Fields, + handleNext, + handlePrev, + changeToStep, + step, + isOnFirstStep, + isOnLastStep, + }; +} diff --git a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx index a53edb901..b50b99f69 100644 --- a/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx +++ b/employee-portal/src/lib/businessModules/stiProtection/shared/sideNavigationItem.tsx @@ -41,6 +41,11 @@ const defaultSubItems: SideNavigationSubItem[] = [ href: routes.appointmentDefinition, accessCheck: hasUserRole(ApiUserRole.StiProtectionUser), }, + { + name: "Textvorlagen", + href: routes.textTemplates, + accessCheck: hasUserRole(ApiUserRole.StiProtectionUser), + }, ]; export function useSideNavigationItems( diff --git a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/archiving.ts b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/archiving.ts index 43dc08ccd..4fe72d51c 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/api/queries/archiving.ts +++ b/employee-portal/src/lib/businessModules/travelMedicine/api/queries/archiving.ts @@ -6,7 +6,7 @@ import { GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { useArchivingApi } from "@/lib/businessModules/travelMedicine/api/clients"; import { archivingApiQueryKey } from "@/lib/businessModules/travelMedicine/api/queries/queryKeys"; diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx index 00315b134..7a05f3316 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/appointmentBlocks/appointmentBlocksGroupForm/AppointmentBlockGroupForm.tsx @@ -24,7 +24,6 @@ import { AppointmentStaffSelection } from "@/lib/shared/components/appointmentBl import { validateAppointmentBlock } from "@/lib/shared/components/appointmentBlocks/validateAppointmentBlock"; import { FormButtonBar } from "@/lib/shared/components/form/FormButtonBar"; import { FormSheet } from "@/lib/shared/components/form/FormSheet"; -import { fullName } from "@/lib/shared/components/users/userFormatter"; import { validateFieldArray } from "@/lib/shared/helpers/validators"; const DEFAULT_PARALLEL_EXAMINATIONS = 1; @@ -93,13 +92,15 @@ export function AppointmentBlockGroupForm( ) { const snackbar = useSnackbar(); const physicianOptions = props.allPhysicians.map((option) => ({ - value: option.userId, - label: fullName(option), + userId: option.userId, + firstName: option.firstName, + lastName: option.lastName, })); const medicalAssistantsOptions = props.allMedicalAssistants.map((option) => ({ - value: option.userId, - label: fullName(option), + userId: option.userId, + firstName: option.firstName, + lastName: option.lastName, })); return ( 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 be5e23576..81926acb5 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/VaccinationConsultationsOverviewTable.tsx @@ -5,13 +5,13 @@ "use client"; -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; import { SelectOptions } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { isDateString, toDateString, toUtcDate, } from "@eshg/lib-portal/helpers/dateTime"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { ApiProcedureStatus } from "@eshg/travel-medicine-api"; import { KeyboardArrowLeftOutlined, 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 index 4c006782a..5e8916407 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/PatientDetails.tsx @@ -11,7 +11,6 @@ import { } from "@eshg/lib-portal/components/formFields/constants"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; -import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { ApiGender, ApiPatient, @@ -23,8 +22,8 @@ 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 { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm } from "@/lib/shared/components/form/SidebarForm"; import { SidebarActions } from "@/lib/shared/components/sidebar/SidebarActions"; @@ -44,9 +43,6 @@ export interface PatientDetailsProps { } export function PatientDetails(props: Readonly<PatientDetailsProps>) { - const fieldName = createFieldNameMapper< - ApiGetReferencePersonResponse | ApiPatient - >(); const person = instanceOfApiGetReferencePersonResponse(props.person) ? props.person : { @@ -96,61 +92,39 @@ export function PatientDetails(props: Readonly<PatientDetailsProps>) { <SidebarContent title={props.title} subtitle="Ausgewählte Person"> <Stack gap={2}> <DetailsRow> - <DetailsCell - name={fieldName("salutation")} + <DetailsItem label={"Anrede"} value={SALUTATION_VALUES[person.salutation]} - flexGrow /> - <DetailsCell - name={fieldName("title")} + <DetailsItem 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 - /> + <DetailsItem label={"Vorname"} value={person.firstName} /> + <DetailsItem label={"Name"} value={person.lastName} avoidWrap /> </DetailsRow> <DetailsRow> - <DetailsCell - name={fieldName("dateOfBirth")} + <DetailsItem label={"Geburtsdatum"} value={formatDate(person.dateOfBirth)} - flexGrow /> - <DetailsCell - name={fieldName("gender")} + <DetailsItem label={"Geschlecht"} value={GENDER_VALUES[person.gender]} - flexGrow /> </DetailsRow> - <DetailsCell - name={fieldName("nameAtBirth")} + <DetailsItem label={"Geburtsname"} value={props.person.nameAtBirth} /> - <DetailsCell - name={fieldName("placeOfBirth")} + <DetailsItem label={"Geburtsort"} value={props.person.placeOfBirth} /> - <DetailsCell - name={fieldName("countryOfBirth")} + <DetailsItem label={"Geburtsland"} value={ isDefined(props.person.countryOfBirth) @@ -170,17 +144,15 @@ export function PatientDetails(props: Readonly<PatientDetailsProps>) { <> <Divider /> {person.emailAddresses.map((email, index) => ( - <DetailsCell + <DetailsItem key={`${email}-${index}`} - name={fieldName("emailAddresses") + "." + index} label={"E-Mail-Adresse"} value={email} /> ))} {person.phoneNumbers.map((phoneNumber, index) => ( - <DetailsCell + <DetailsItem key={`${phoneNumber}-${index}`} - name={fieldName("phoneNumbers") + "." + index} label={"Telefonnummer"} value={phoneNumber} /> @@ -197,9 +169,8 @@ export function PatientDetails(props: Readonly<PatientDetailsProps>) { </Divider> {isDefined(props.initialPatient.emailAddresses) && props.initialPatient.emailAddresses.map((email, index) => ( - <DetailsCell + <DetailsItem key={`initialPatient-${email}-${index}`} - name={fieldName("emailAddresses") + "." + index} label={"E-Mail-Adresse"} value={email} /> @@ -207,9 +178,8 @@ export function PatientDetails(props: Readonly<PatientDetailsProps>) { {isDefined(props.initialPatient.phoneNumbers) && props.initialPatient.phoneNumbers.map( (phoneNumber, index) => ( - <DetailsCell + <DetailsItem key={`initialPatient-${phoneNumber}-${index}`} - name={fieldName("phoneNumbers") + "." + index} label={"Telefonnummer"} value={phoneNumber} /> diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureCreatedByTile.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureCreatedByTile.tsx index 1b06ab941..6e1ef546c 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureCreatedByTile.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/ProcedureCreatedByTile.tsx @@ -3,13 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import "@eshg/travel-medicine-api"; import { ApiCreatedByUserType } from "@eshg/travel-medicine-api"; import { Grid } from "@mui/joy"; import { translateCreatedByUserType } from "@/lib/businessModules/travelMedicine/components/appointmentTypes/translations"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; interface ProcedureOriginProps { initialValues: ProcedureOriginValues; @@ -21,22 +20,19 @@ interface ProcedureOriginValues { export function ProcedureCreatedByTile(props: Readonly<ProcedureOriginProps>) { return ( - <> - <DetailsSection - data-testid="procedure" - title="Vorgangsdaten" - canEdit={false} - > - <Grid xs={12} pl={0} py={0}> - <DetailsCell - name="createdBy" - label={"Vorgang erstellt von:"} - value={translateCreatedByUserType( - props.initialValues.createdByUserType, - )} - /> - </Grid> - </DetailsSection> - </> + <DetailsSection + data-testid="procedure" + title="Vorgangsdaten" + canEdit={false} + > + <Grid xs={12} pl={0} py={0}> + <DetailsItem + label={"Vorgang erstellt von:"} + value={translateCreatedByUserType( + props.initialValues.createdByUserType, + )} + /> + </Grid> + </DetailsSection> ); } diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx index 4a0b37104..4ab936cbb 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/TravelDataTile.tsx @@ -17,9 +17,9 @@ import { TRAVEL_TIME_UNITS, TRAVEL_TYPES, } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/translations"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow"; import { DetailsSection } from "@/lib/shared/components/detailsSection/DetailsSection"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; interface TravelDataTileProps { initialValues: CreateProcedureValues; @@ -30,74 +30,67 @@ export function TravelDataTile(procedure: Readonly<TravelDataTileProps>) { const travelDataSidebar = useTravelDataSidebar(); return ( - <> - <DetailsSection - data-testid="travelData" - title="Reisedaten" - onEdit={() => travelDataSidebar.open(procedure)} - canEdit={!procedure.isProcedureClosed} - > - <Stack direction={{ xxs: "column", md: "row" }} gap={3}> - <Stack sx={{ flexGrow: 1, maxWidth: "calc(100%/2)" }} gap={1}> + <DetailsSection + data-testid="travelData" + title="Reisedaten" + onEdit={() => travelDataSidebar.open(procedure)} + canEdit={!procedure.isProcedureClosed} + > + <Stack direction={{ xxs: "column", md: "row" }} gap={3}> + <Stack sx={{ flexGrow: 1, maxWidth: "calc(100%/2)" }} gap={1}> + <DetailsRow> + <DetailsItem + label="Reiseart" + value={ + procedure.initialValues.travelType && + TRAVEL_TYPES[procedure.initialValues.travelType] + } + /> + {procedure.initialValues.travelType !== ApiTravelType.NoTravel && ( + <DetailsItem + label="Reiseziele" + value={ + isEmpty(procedure.initialValues.travelDestinations) + ? "-" + : procedure.initialValues.travelDestinations + .map((cc) => translateCountry(cc)) + .join(", ") + } + /> + )} + </DetailsRow> + </Stack> + + <Stack sx={{ flexGrow: 1, maxWidth: "calc(100%/2)" }} gap={1}> + {procedure.initialValues.travelType !== ApiTravelType.NoTravel ? ( <DetailsRow> - <DetailsCell - name="travelType" - label="Reiseart" + <DetailsItem + label="Reisebeginn" value={ - procedure.initialValues.travelType && - TRAVEL_TYPES[procedure.initialValues.travelType] + procedure.initialValues.travelStartDate + ? formatDate( + new Date(procedure.initialValues.travelStartDate), + ) + : "-" + } + /> + <DetailsItem + label="Reisedauer" + value={ + procedure.initialValues.travelTimeAmount + ? procedure.initialValues.travelTimeAmount + + " " + + (procedure.initialValues.travelTimeUnit && + TRAVEL_TIME_UNITS[ + procedure.initialValues.travelTimeUnit + ]) + : "-" } /> - {procedure.initialValues.travelType !== - ApiTravelType.NoTravel && ( - <DetailsCell - name="travelDestinations" - label="Reiseziele" - value={ - isEmpty(procedure.initialValues.travelDestinations) - ? "-" - : procedure.initialValues.travelDestinations - .map((cc) => translateCountry(cc)) - .join(", ") - } - /> - )} </DetailsRow> - </Stack> - - <Stack sx={{ flexGrow: 1, maxWidth: "calc(100%/2)" }} gap={1}> - {procedure.initialValues.travelType !== ApiTravelType.NoTravel ? ( - <DetailsRow> - <DetailsCell - name="travelStartDate" - label="Reisebeginn" - value={ - procedure.initialValues.travelStartDate - ? formatDate( - new Date(procedure.initialValues.travelStartDate), - ) - : "-" - } - /> - <DetailsCell - name="travelTimeAmount" - label="Reisedauer" - value={ - procedure.initialValues.travelTimeAmount - ? procedure.initialValues.travelTimeAmount + - " " + - (procedure.initialValues.travelTimeUnit && - TRAVEL_TIME_UNITS[ - procedure.initialValues.travelTimeUnit - ]) - : "-" - } - /> - </DetailsRow> - ) : null} - </Stack> + ) : null} </Stack> - </DetailsSection> - </> + </Stack> + </DetailsSection> ); } diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/InformationStatementForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/InformationStatementForm.tsx index 030f00f88..c6410d397 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/InformationStatementForm.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/InformationStatementForm.tsx @@ -20,7 +20,6 @@ import { createDiseaseOptions, createInformationStatementTemplateOptions, } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers"; -import { SelectionOption } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm, @@ -46,6 +45,11 @@ interface InformationStatementFormProps { submitLabel: string; } +export interface SelectionOption { + label: string; + value: string; +} + export function InformationStatementForm( props: Readonly<InformationStatementFormProps>, ) { diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServiceAppliedForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServiceAppliedForm.tsx index c8a6c212a..07cad58e8 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServiceAppliedForm.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServiceAppliedForm.tsx @@ -9,7 +9,7 @@ import { Formik } from "formik"; import { Ref } from "react"; import { AppliedByFields } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AppliedByFields"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm, @@ -51,8 +51,7 @@ export function OtherServiceAppliedForm( <Stack direction="column" gap={2} data-testid="otherServiceApplied"> <Sheet> <Stack direction="column" gap={2}> - <DetailsCell - name="serviceTypeDescription" + <DetailsItem label="Leistungsart" value={props.initialValues.serviceTypeDescription} /> diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServicesFields.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServicesFields.tsx index 59f5bb194..de89d61b9 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServicesFields.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/OtherServicesFields.tsx @@ -12,7 +12,7 @@ import { ServicesRequest, } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AddServicePlanForm"; import { createOtherServicesTemplateOptions } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { LOCALE_OPTION, formatCurrency } from "@/lib/shared/helpers/numbers"; interface OtherServicesFieldsProps { @@ -82,8 +82,7 @@ export function OtherServicesFields({ await getFeeForTemplate(index, templateId, setFieldValue); }} /> - <DetailsCell - name={`services.${index}.fee`} + <DetailsItem label="Preis" value={formatCurrency(val.fee, { localOption: LOCALE_OPTION.manual, diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/ServiceAppliedForm.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/ServiceAppliedForm.tsx index 922f670e7..0d8b819a8 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/ServiceAppliedForm.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/ServiceAppliedForm.tsx @@ -10,7 +10,7 @@ import { Formik } from "formik"; import { Ref } from "react"; import { AppliedByFields } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/AppliedByFields"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { MultiFormButtonBar } from "@/lib/shared/components/form/MultiFormButtonBar"; import { SidebarForm, @@ -53,13 +53,11 @@ export function ServiceAppliedForm(props: Readonly<ServiceAppliedFormProps>) { <Stack direction="column" gap={2} data-testid="serviceApplied"> <Sheet> <Stack direction="column" gap={2}> - <DetailsCell - name="vaccinationInfo" + <DetailsItem label="Impfung" value={props.initialValues.vaccinationInfo} /> - <DetailsCell - name="vaccineName" + <DetailsItem label="Impfstoff" value={props.initialValues.vaccineName} /> diff --git a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/VaccinationFields.tsx b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/VaccinationFields.tsx index 01ac3b347..2bc05ae69 100644 --- a/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/VaccinationFields.tsx +++ b/employee-portal/src/lib/businessModules/travelMedicine/components/vaccinationConsultations/baseData/sidebars/sidebarForms/VaccinationFields.tsx @@ -23,7 +23,7 @@ import { createVaccinesOptions, } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/helpers"; import { VACCINATION_TYPE } from "@/lib/businessModules/travelMedicine/components/vaccinationConsultations/shared/options"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { CheckboxField } from "@/lib/shared/components/formFields/CheckboxField"; import { LOCALE_OPTION, formatCurrency } from "@/lib/shared/helpers/numbers"; import { validatePositiveInteger } from "@/lib/shared/helpers/validators"; @@ -108,8 +108,7 @@ export function VaccinationFields({ setOffset(getVaccineOffsets(vaccineId)); }} /> - <DetailsCell - name={`services.${index}.fee`} + <DetailsItem label="Preis" value={formatCurrency(val.fee, { localOption: LOCALE_OPTION.manual, diff --git a/employee-portal/src/lib/opendata/api/clients.ts b/employee-portal/src/lib/opendata/api/clients.ts index f6016fe76..40407f7ab 100644 --- a/employee-portal/src/lib/opendata/api/clients.ts +++ b/employee-portal/src/lib/opendata/api/clients.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Configuration, OpenDataApi } from "@eshg/employee-portal-api/opendata"; import { useApiConfiguration } from "@eshg/lib-portal/api/ApiProvider"; +import { Configuration, OpenDataApi } from "@eshg/opendata-api"; function useConfiguration() { const configurationParameters = useApiConfiguration( diff --git a/employee-portal/src/lib/opendata/components/EditEntrySidebarContent.tsx b/employee-portal/src/lib/opendata/components/EditEntrySidebarContent.tsx index 0813bdf4c..a6ba044fc 100644 --- a/employee-portal/src/lib/opendata/components/EditEntrySidebarContent.tsx +++ b/employee-portal/src/lib/opendata/components/EditEntrySidebarContent.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiVersion } from "@eshg/employee-portal-api/opendata"; import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { toDateString } from "@eshg/lib-portal/helpers/dateTime"; +import { ApiVersion } from "@eshg/opendata-api"; import { Button } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/opendata/components/EntryDetailsSidebar.tsx b/employee-portal/src/lib/opendata/components/EntryDetailsSidebar.tsx index 864bc2d16..91328da05 100644 --- a/employee-portal/src/lib/opendata/components/EntryDetailsSidebar.tsx +++ b/employee-portal/src/lib/opendata/components/EntryDetailsSidebar.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiVersion } from "@eshg/employee-portal-api/opendata"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiVersion } from "@eshg/opendata-api"; import { Button, Chip, Divider, Stack, Typography } from "@mui/joy"; import { useState } from "react"; import { isEmpty } from "remeda"; diff --git a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx index d930fbf73..b7bea8b11 100644 --- a/employee-portal/src/lib/opendata/components/OpenDataTable.tsx +++ b/employee-portal/src/lib/opendata/components/OpenDataTable.tsx @@ -5,11 +5,11 @@ "use client"; -import { ApiResource, ApiVersion } from "@eshg/employee-portal-api/opendata"; import { parseOptionalString, parseReadonlyPageParams, } from "@eshg/lib-portal/helpers/searchParams"; +import { ApiResource, ApiVersion } from "@eshg/opendata-api"; import Add from "@mui/icons-material/Add"; import { Button, Stack } from "@mui/joy"; import { useSearchParams } from "next/navigation"; diff --git a/employee-portal/src/lib/opendata/components/VersionFileCard.tsx b/employee-portal/src/lib/opendata/components/VersionFileCard.tsx index fc866e174..5649db33b 100644 --- a/employee-portal/src/lib/opendata/components/VersionFileCard.tsx +++ b/employee-portal/src/lib/opendata/components/VersionFileCard.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiVersion } from "@eshg/employee-portal-api/opendata"; import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +import { ApiVersion } from "@eshg/opendata-api"; import FileDownloadOutlined from "@mui/icons-material/FileDownloadOutlined"; import { useOpenDataApi } from "@/lib/opendata/api/clients"; diff --git a/employee-portal/src/lib/opendata/components/openDataColumns.tsx b/employee-portal/src/lib/opendata/components/openDataColumns.tsx index bd42b94ad..2bd48e4dc 100644 --- a/employee-portal/src/lib/opendata/components/openDataColumns.tsx +++ b/employee-portal/src/lib/opendata/components/openDataColumns.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiResource, ApiVersion } from "@eshg/employee-portal-api/opendata"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiResource, ApiVersion } from "@eshg/opendata-api"; import Add from "@mui/icons-material/Add"; import DeleteOutlined from "@mui/icons-material/DeleteOutlined"; import { IconButton, Stack } from "@mui/joy"; diff --git a/employee-portal/src/lib/opendata/helper.ts b/employee-portal/src/lib/opendata/helper.ts index 294610363..df4fa4dc4 100644 --- a/employee-portal/src/lib/opendata/helper.ts +++ b/employee-portal/src/lib/opendata/helper.ts @@ -3,11 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiBusinessModule, - ApiVersion, -} from "@eshg/employee-portal-api/opendata"; import { ConfirmationDialogOptions } from "@eshg/lib-portal/components/confirmationDialog/ConfirmationDialogProvider"; +import { ApiBusinessModule, ApiVersion } from "@eshg/opendata-api"; import { buildOptionsFromBusinessModules } from "@/lib/shared/components/procedures/helper"; diff --git a/employee-portal/src/lib/opendata/hooks/useOpenDataFilterSettings.ts b/employee-portal/src/lib/opendata/hooks/useOpenDataFilterSettings.ts index f00307821..6ae51e529 100644 --- a/employee-portal/src/lib/opendata/hooks/useOpenDataFilterSettings.ts +++ b/employee-portal/src/lib/opendata/hooks/useOpenDataFilterSettings.ts @@ -7,7 +7,7 @@ import { ApiBusinessModule } from "@eshg/base-api"; import { ApiOpenDataFileType, GetOpenDocumentsRequest, -} from "@eshg/employee-portal-api/opendata"; +} from "@eshg/opendata-api"; import { openDataFileTypes } from "@/lib/opendata/constants"; import { buildOpenDataBusinessModuleOptions } from "@/lib/opendata/helper"; diff --git a/employee-portal/src/lib/opendata/mutations/opendata.ts b/employee-portal/src/lib/opendata/mutations/opendata.ts index 7bb020e29..e6c2f67f4 100644 --- a/employee-portal/src/lib/opendata/mutations/opendata.ts +++ b/employee-portal/src/lib/opendata/mutations/opendata.ts @@ -3,12 +3,12 @@ * 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 { ApiUpdateVersionMetaDataRequest, CreateOpenDocumentRequest, -} from "@eshg/employee-portal-api/opendata"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/opendata-api"; import { useOpenDataApi } from "@/lib/opendata/api/clients"; diff --git a/employee-portal/src/lib/opendata/queries/opendata.ts b/employee-portal/src/lib/opendata/queries/opendata.ts index 067da6445..4dcd3cc62 100644 --- a/employee-portal/src/lib/opendata/queries/opendata.ts +++ b/employee-portal/src/lib/opendata/queries/opendata.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { GetOpenDocumentsRequest } from "@eshg/employee-portal-api/opendata"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +import { GetOpenDocumentsRequest } from "@eshg/opendata-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useOpenDataApi } from "@/lib/opendata/api/clients"; diff --git a/employee-portal/src/lib/shared/api/clients.ts b/employee-portal/src/lib/shared/api/clients.ts index dd61115a6..ec34d5668 100644 --- a/employee-portal/src/lib/shared/api/clients.ts +++ b/employee-portal/src/lib/shared/api/clients.ts @@ -4,19 +4,19 @@ */ import { ConfigurationParameters } from "@eshg/base-api"; +import { + ApiConfiguration, + useApiConfiguration, +} from "@eshg/lib-portal/api/ApiProvider"; import { ApiBusinessModule, - Configuration as BusinessProceduresConfiguration, GdprValidationTaskApi, -} from "@eshg/employee-portal-api/businessProcedures"; + Configuration as LibProceduresConfiguration, +} from "@eshg/lib-procedures-api"; import { Configuration as LibStatisticsConfiguration, StatisticsProcedureReferenceApi, -} from "@eshg/employee-portal-api/libStatistics"; -import { - ApiConfiguration, - useApiConfiguration, -} from "@eshg/lib-portal/api/ApiProvider"; +} from "@eshg/lib-statistics-api"; type ConfigurationConstructor<TConfiguration> = new ( params: ConfigurationParameters, @@ -49,7 +49,7 @@ export function useConfigurationByBusinessModule<TConfiguration>( export function useGdprValidationTaskApi(businessModule: ApiBusinessModule) { const configuration = useConfigurationByBusinessModule( businessModule, - BusinessProceduresConfiguration, + LibProceduresConfiguration, ); return new GdprValidationTaskApi(configuration); } diff --git a/employee-portal/src/lib/shared/api/mutations/archiving.ts b/employee-portal/src/lib/shared/api/mutations/archiving.ts index d98ecc547..d7ccc2cdf 100644 --- a/employee-portal/src/lib/shared/api/mutations/archiving.ts +++ b/employee-portal/src/lib/shared/api/mutations/archiving.ts @@ -3,12 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - ApiArchivingRelevance, - ArchivingApi, -} from "@eshg/employee-portal-api/businessProcedures"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiArchivingRelevance, ArchivingApi } from "@eshg/lib-procedures-api"; import { join } from "@/lib/shared/helpers/strings"; diff --git a/employee-portal/src/lib/shared/api/mutations/gdpr.ts b/employee-portal/src/lib/shared/api/mutations/gdpr.ts index 7d5400381..bbe96928b 100644 --- a/employee-portal/src/lib/shared/api/mutations/gdpr.ts +++ b/employee-portal/src/lib/shared/api/mutations/gdpr.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { GdprValidationTaskApiInterface } from "@eshg/employee-portal-api/businessProcedures"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { GdprValidationTaskApiInterface } from "@eshg/lib-procedures-api"; export function useAddDownloadPackage(taskApi: GdprValidationTaskApiInterface) { const snackbar = useSnackbar(); diff --git a/employee-portal/src/lib/shared/api/mutations/inboxProcedures.ts b/employee-portal/src/lib/shared/api/mutations/inboxProcedures.ts index 6ba57645a..0586c41d8 100644 --- a/employee-portal/src/lib/shared/api/mutations/inboxProcedures.ts +++ b/employee-portal/src/lib/shared/api/mutations/inboxProcedures.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { ApiCreateInboxProcedureRequest, ApiInboxProcedure, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +} from "@eshg/lib-procedures-api"; export function useCreateInboxProcedureTemplate( useInboxProcedureApi: () => { diff --git a/employee-portal/src/lib/shared/api/mutations/libEditor.ts b/employee-portal/src/lib/shared/api/mutations/libEditor.ts index e66e7248e..9b854fb49 100644 --- a/employee-portal/src/lib/shared/api/mutations/libEditor.ts +++ b/employee-portal/src/lib/shared/api/mutations/libEditor.ts @@ -8,7 +8,7 @@ import { EditorApiInterface, InsertEditorElementRequest, UpdateEditorElementRequest, -} from "@eshg/employee-portal-api/libEditor"; +} from "@eshg/lib-editor-api"; import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; diff --git a/employee-portal/src/lib/shared/api/queries/archiving.ts b/employee-portal/src/lib/shared/api/queries/archiving.ts index 0239e7ffd..d0d8b3591 100644 --- a/employee-portal/src/lib/shared/api/queries/archiving.ts +++ b/employee-portal/src/lib/shared/api/queries/archiving.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { type QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { ArchivingApi, GetArchivableProceduresRequest, GetRelevantArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; -import { type QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/lib-procedures-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { mapArchivableProceduresResponse } from "@/lib/shared/components/archiving/api/models/archivableProcedure"; diff --git a/employee-portal/src/lib/shared/api/queries/gdpr.ts b/employee-portal/src/lib/shared/api/queries/gdpr.ts index 683a6cae9..d31f9322e 100644 --- a/employee-portal/src/lib/shared/api/queries/gdpr.ts +++ b/employee-portal/src/lib/shared/api/queries/gdpr.ts @@ -4,6 +4,9 @@ */ import { ApiBaseFeature } from "@eshg/base-api"; +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +import { PortalErrorCode } from "@eshg/lib-portal/errorHandling/PortalErrorCode"; +import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers"; import { ApiBusinessModule, ApiGdprDownloadPackageInfo, @@ -11,10 +14,7 @@ import { ApiGetGdprNotificationBannerResponse, GdprValidationTaskApiInterface, GetAllGdprValidationTasksRequest, -} from "@eshg/employee-portal-api/businessProcedures"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; -import { PortalErrorCode } from "@eshg/lib-portal/errorHandling/PortalErrorCode"; -import { resolveError } from "@eshg/lib-portal/errorHandling/errorResolvers"; +} from "@eshg/lib-procedures-api"; import { queryOptions, useSuspenseQueries } from "@tanstack/react-query"; import assert from "assert"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/shared/api/queries/inboxProcedures.ts b/employee-portal/src/lib/shared/api/queries/inboxProcedures.ts index b1f099f6b..89c049fba 100644 --- a/employee-portal/src/lib/shared/api/queries/inboxProcedures.ts +++ b/employee-portal/src/lib/shared/api/queries/inboxProcedures.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { InboxProcedureApi } from "@eshg/employee-portal-api/businessProcedures"; import { type QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; +import { InboxProcedureApi } from "@eshg/lib-procedures-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useSearchParams } from "next/navigation"; diff --git a/employee-portal/src/lib/shared/api/queries/statisticsProcedureReference.ts b/employee-portal/src/lib/shared/api/queries/statisticsProcedureReference.ts index 0eb067bf8..70c583402 100644 --- a/employee-portal/src/lib/shared/api/queries/statisticsProcedureReference.ts +++ b/employee-portal/src/lib/shared/api/queries/statisticsProcedureReference.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; import { ApiGetProcedureIdsRequest, StatisticsProcedureReferenceApiInterface, -} from "@eshg/employee-portal-api/libStatistics"; +} from "@eshg/lib-statistics-api"; import { queryOptions } from "@tanstack/react-query"; import { gdprValidationTaskApiQueryKey } from "@/lib/baseModule/api/queries/apiQueryKey"; diff --git a/employee-portal/src/lib/shared/api/queries/tasks.ts b/employee-portal/src/lib/shared/api/queries/tasks.ts index 9cd9d24aa..f7118711a 100644 --- a/employee-portal/src/lib/shared/api/queries/tasks.ts +++ b/employee-portal/src/lib/shared/api/queries/tasks.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; import { ApiGetTaskByUserResponse, ApiResponse, GetTasksByAssigneeRequest, -} from "@eshg/employee-portal-api/businessProcedures"; -import { unwrapRawResponse } from "@eshg/lib-portal/api/unwrapRawResponse"; +} from "@eshg/lib-procedures-api"; import { queryOptions } from "@tanstack/react-query"; import { useSearchParams } from "next/navigation"; diff --git a/employee-portal/src/lib/shared/components/FileCard.tsx b/employee-portal/src/lib/shared/components/FileCard.tsx index 52481f0b8..e02438dd2 100644 --- a/employee-portal/src/lib/shared/components/FileCard.tsx +++ b/employee-portal/src/lib/shared/components/FileCard.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiFileType } from "@eshg/employee-portal-api/businessProcedures"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { formatFileSize } from "@eshg/lib-portal/helpers/file"; +import { ApiFileType } from "@eshg/lib-procedures-api"; import AlternateEmailOutlinedIcon from "@mui/icons-material/AlternateEmailOutlined"; import AudioFileOutlinedIcon from "@mui/icons-material/AudioFileOutlined"; import ImageOutlinedIcon from "@mui/icons-material/ImageOutlined"; diff --git a/employee-portal/src/lib/shared/components/address/BaseAddressDetails.tsx b/employee-portal/src/lib/shared/components/address/BaseAddressDetails.tsx index 9e4a3ae4c..ebaaa1cc2 100644 --- a/employee-portal/src/lib/shared/components/address/BaseAddressDetails.tsx +++ b/employee-portal/src/lib/shared/components/address/BaseAddressDetails.tsx @@ -7,9 +7,9 @@ import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; import { SxProps } from "@mui/joy/styles/types/theme"; import { isNonNullish } from "remeda"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; import { BaseAddress, isDomesticAddress, @@ -25,53 +25,30 @@ export function BaseAddressDetails({ address, sx }: BaseAddressDetailsProps) { return ( <DetailsColumn sx={sx}> {isNonNullish(address.differentName) && ( - <DetailsCell - label={"Abweichender Empfänger"} + <DetailsItem + label="Abweichender Empfänger" value={address.differentName} - name={"differentName"} /> )} {isPostboxAddress(address) && ( - <DetailsCell - label={"Postfachnummer"} - value={address.postbox} - name={"postbox"} - /> + <DetailsItem label="Postfachnummer" value={address.postbox} /> )} {isDomesticAddress(address) && ( <> - <DetailsCell - label={"Straße und Haus Nr."} + <DetailsItem + label="Straße und Haus Nr." value={[address.street, address.houseNumber].join(" ").trim()} - name={"streetAndHousenumber"} /> {isNonNullish(address.addressAddition) && ( - <DetailsCell - label={"Adresszusatz"} - value={address.addressAddition} - name={"addressAddition"} - /> + <DetailsItem label="Adresszusatz" value={address.addressAddition} /> )} </> )} <DetailsRow> - <DetailsCell - label={"Postleitzahl"} - value={address.postalCode} - name={"postalCode"} - /> - <DetailsCell - label={"Ort"} - value={address.city} - name={"city"} - avoidWrap - /> + <DetailsItem label="Postleitzahl" value={address.postalCode} /> + <DetailsItem label="Ort" value={address.city} avoidWrap /> </DetailsRow> - <DetailsCell - label={"Land"} - value={translateCountry(address.country)} - name={"country"} - /> + <DetailsItem label="Land" value={translateCountry(address.country)} /> </DetailsColumn> ); } diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffField.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffField.tsx index e2bf5c004..76f20899d 100644 --- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffField.tsx +++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffField.tsx @@ -7,30 +7,20 @@ import { BaseField, useBaseField, } from "@eshg/lib-portal/components/formFields/BaseField"; -import { SelectOption } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { FieldProps } from "@eshg/lib-portal/types/form"; import Close from "@mui/icons-material/Close"; -import { - Autocomplete, - AutocompleteProps, - Chip, - ChipProps, - Tooltip, -} from "@mui/joy"; -import { isString } from "remeda"; +import { Autocomplete, Chip, ChipProps, Tooltip } from "@mui/joy"; -import { SelectionOption } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffSelection"; +import { fullName } from "@/lib/shared/components/users/userFormatter"; -type JoyUiSelectValue = AutocompleteProps< - SelectionOption, - true, - boolean, - true ->["value"]; -type SelectFieldValue = NonNullable<JoyUiSelectValue>; +export interface StaffUser { + userId: string; + firstName: string; + lastName: string; +} -interface AppointmentStaffFieldProps extends FieldProps<SelectFieldValue> { - options: SelectionOption[]; +interface AppointmentStaffFieldProps extends FieldProps<string[]> { + options: StaffUser[]; placeholder?: string; blockedStaff: string[]; freeStaff: string[]; @@ -41,21 +31,20 @@ export function AppointmentStaffField( ) { const field = useBaseField(props); - function setValue(newValue: (string | SelectOption)[]) { - const labelNames = newValue.map((v) => (isString(v) ? v : v.value)); - void field.helpers.setValue(labelNames); + function setValue(newUserIds: string[]) { + void field.helpers.setValue(newUserIds); } - function getAvailability(item: SelectionOption): { + function getAvailability(item: StaffUser): { title: string; color: ChipProps["color"]; } { - if (props.freeStaff.includes(item.value)) { + if (props.freeStaff.includes(item.userId)) { return { title: "Es gibt keine Konflikte.", color: "success", }; - } else if (props.blockedStaff.includes(item.value)) { + } else if (props.blockedStaff.includes(item.userId)) { return { title: "Es gibt mindestens einen Terminkonflikt.", color: "danger", @@ -77,28 +66,37 @@ export function AppointmentStaffField( > <Autocomplete multiple - freeSolo + autoHighlight clearOnBlur selectOnFocus filterSelectedOptions name={props.name} + value={props.options.filter((option) => + field.input.value.includes(option.userId), + )} disableClearable={field.required} onChange={(_, newValue) => { - setValue(newValue); + setValue(newValue.map((user) => user.userId)); }} filterOptions={(options, params) => { const { inputValue } = params; - return options.filter( - (opt) => - opt.label.toLowerCase().includes(inputValue.toLowerCase()) && - !field.input.value.includes(opt.label), - ); + return options.filter((opt) => { + const optionName = fullName(opt); + const selectedUserIds = field.input.value; + const matchesOption = optionName + .toLowerCase() + .includes(inputValue.toLowerCase()); + const matchesSelectedUser = selectedUserIds.some( + (userId) => opt.userId === userId, + ); + return matchesOption && !matchesSelectedUser; + }); }} onBlur={field.input.onBlur} placeholder={props.placeholder} options={props.options} - getOptionLabel={(value) => (isString(value) ? value : value.label)} - getOptionKey={(value) => (isString(value) ? value : value.value)} + getOptionLabel={(value) => fullName(value)} + getOptionKey={(value) => value.userId} renderTags={(options, getTagProps) => options.map((item, index) => ( <Tooltip title={getAvailability(item).title} key={index}> @@ -109,9 +107,9 @@ export function AppointmentStaffField( endDecorator={<Close fontSize="sm" />} sx={{ minWidth: 0 }} {...getTagProps({ index })} - key={item.value} + key={item.userId} > - {item.label} + {fullName(item)} </Chip> </Tooltip> )) diff --git a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx index 93e7781a5..8cf97e565 100644 --- a/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx +++ b/employee-portal/src/lib/shared/components/appointmentBlocks/AppointmentStaffSelection.tsx @@ -6,7 +6,10 @@ import { Button, Grid } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; -import { AppointmentStaffField } from "@/lib/shared/components/appointmentBlocks/AppointmentStaffField"; +import { + AppointmentStaffField, + StaffUser, +} from "@/lib/shared/components/appointmentBlocks/AppointmentStaffField"; import { FormGroupGrid } from "@/lib/shared/components/form/FormGroupGrid"; export const BUTTON_STYLES: SxProps = { @@ -14,19 +17,14 @@ export const BUTTON_STYLES: SxProps = { }; export interface AppointmentStaffSelectionProps { - physicianOptions: SelectionOption[]; - medicalAssistantOptions?: SelectionOption[]; - consultantOptions?: SelectionOption[]; + physicianOptions: StaffUser[]; + medicalAssistantOptions?: StaffUser[]; + consultantOptions?: StaffUser[]; blockedStaff: string[]; freeStaff: string[]; validateAvailability: () => void; } -export interface SelectionOption { - label: string; - value: string; -} - export function AppointmentStaffSelection( props: Readonly<AppointmentStaffSelectionProps>, ) { diff --git a/employee-portal/src/lib/shared/components/archiving/ArchiveView.tsx b/employee-portal/src/lib/shared/components/archiving/ArchiveView.tsx index c26c17433..840527f94 100644 --- a/employee-portal/src/lib/shared/components/archiving/ArchiveView.tsx +++ b/employee-portal/src/lib/shared/components/archiving/ArchiveView.tsx @@ -4,7 +4,7 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { ApiProcedureType } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiProcedureType } from "@eshg/lib-procedures-api"; import { UseBulkUpdateProceduresArchivingRelevance } from "@/lib/shared/api/mutations/archiving"; import { diff --git a/employee-portal/src/lib/shared/components/archiving/api/models/archivableProcedure.ts b/employee-portal/src/lib/shared/components/archiving/api/models/archivableProcedure.ts index ca13ff425..ee148efd4 100644 --- a/employee-portal/src/lib/shared/components/archiving/api/models/archivableProcedure.ts +++ b/employee-portal/src/lib/shared/components/archiving/api/models/archivableProcedure.ts @@ -6,7 +6,7 @@ import { ApiGetArchivableProceduresResponse, ApiProcedure, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; export type ArchivableProcedure = ApiProcedure & { id: string }; diff --git a/employee-portal/src/lib/shared/components/archiving/components/ArchivingRelevanceChip.tsx b/employee-portal/src/lib/shared/components/archiving/components/ArchivingRelevanceChip.tsx index 5be48068e..7b6476aac 100644 --- a/employee-portal/src/lib/shared/components/archiving/components/ArchivingRelevanceChip.tsx +++ b/employee-portal/src/lib/shared/components/archiving/components/ArchivingRelevanceChip.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiArchivingRelevance } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiArchivingRelevance } from "@eshg/lib-procedures-api"; import Chip, { ChipProps } from "@mui/joy/Chip"; import { archivingRelevanceNames } from "@/lib/shared/components/archiving/constants"; diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/ArchiveAdminTable.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/ArchiveAdminTable.tsx index efcfa8cf4..f84944810 100644 --- a/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/ArchiveAdminTable.tsx +++ b/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/ArchiveAdminTable.tsx @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +import { formatFileSize } from "@eshg/lib-portal/helpers/file"; import { ApiArchivingRelevance, ApiGetRelevantArchivableProceduresSortBy, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useFileDownload } from "@eshg/lib-portal/api/files/download"; -import { formatFileSize } from "@eshg/lib-portal/helpers/file"; +} from "@eshg/lib-procedures-api"; import { DeleteOutlined, DownloadOutlined } from "@mui/icons-material"; import { Button } from "@mui/joy"; diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/archiveAdminTableColumns.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/archiveAdminTableColumns.tsx index 6f9839b4d..58956ece2 100644 --- a/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/archiveAdminTableColumns.tsx +++ b/employee-portal/src/lib/shared/components/archiving/components/archiveAdminView/archiveAdminTableColumns.tsx @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - ApiGetRelevantArchivableProceduresSortBy, - ApiProcedure, -} from "@eshg/employee-portal-api/businessProcedures"; import { formatDate, formatDateTime, } from "@eshg/lib-portal/formatters/dateTime"; +import { + ApiGetRelevantArchivableProceduresSortBy, + ApiProcedure, +} from "@eshg/lib-procedures-api"; import { createColumnHelper } from "@tanstack/react-table"; const columnHelper = createColumnHelper<ApiProcedure>(); 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 43cd2ce74..aab0b9c5b 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 @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiGetArchivableProceduresSortBy } from "@eshg/employee-portal-api/businessProcedures"; import { Alert } from "@eshg/lib-portal/components/Alert"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiGetArchivableProceduresSortBy } from "@eshg/lib-procedures-api"; import { endOfMonth, isAfter, startOfYear } from "date-fns"; import { ArchiveViewProps } from "@/lib/shared/components/archiving/ArchiveView"; diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx index 549372abf..31e9fa5f7 100644 --- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx +++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/ArchiveTableTitle.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiArchivingRelevance } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiArchivingRelevance } from "@eshg/lib-procedures-api"; import { DeleteOutlined, Inventory2Outlined } from "@mui/icons-material"; import { Divider, Typography } from "@mui/joy"; import { RowSelectionState } from "@tanstack/react-table"; diff --git a/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx b/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx index 7eed4580d..a8755767f 100644 --- a/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx +++ b/employee-portal/src/lib/shared/components/archiving/components/archiveView/archiveTableColumns.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiGetArchivableProceduresSortBy } from "@eshg/employee-portal-api/businessProcedures"; import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiGetArchivableProceduresSortBy } from "@eshg/lib-procedures-api"; import { createColumnHelper } from "@tanstack/react-table"; import { ArchivableProcedure } from "@/lib/shared/components/archiving/api/models/archivableProcedure"; diff --git a/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveAdminFilterSettings.ts b/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveAdminFilterSettings.ts index 8a12b9b3b..9e659ae52 100644 --- a/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveAdminFilterSettings.ts +++ b/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveAdminFilterSettings.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { GetRelevantArchivableProceduresRequest } from "@eshg/employee-portal-api/businessProcedures"; +import { GetRelevantArchivableProceduresRequest } from "@eshg/lib-procedures-api"; import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; import { FilterValue } from "@/lib/shared/components/filterSettings/models/FilterValue"; diff --git a/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveFilterSettings.ts b/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveFilterSettings.ts index d7c400585..c4fb1b781 100644 --- a/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveFilterSettings.ts +++ b/employee-portal/src/lib/shared/components/archiving/hooks/useArchiveFilterSettings.ts @@ -8,7 +8,7 @@ import { ApiGetArchivingConfigurationResponse, ApiProcedureType, GetArchivableProceduresRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { archivingRelevanceNames } from "@/lib/shared/components/archiving/constants"; import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; diff --git a/employee-portal/src/lib/shared/components/auditlog/constants.ts b/employee-portal/src/lib/shared/components/auditlog/constants.ts index 865cf3200..b48ecc84f 100644 --- a/employee-portal/src/lib/shared/components/auditlog/constants.ts +++ b/employee-portal/src/lib/shared/components/auditlog/constants.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ApiAuditLogSource } from "@eshg/auditlog-api"; import { ApiBusinessModule } from "@eshg/base-api"; -import { ApiAuditLogSource } from "@eshg/employee-portal-api/auditlog/models"; import { businessModuleNames } from "@/lib/shared/components/procedures/constants"; diff --git a/employee-portal/src/lib/shared/components/centralFile/display/CentralFileFacilityDetails.tsx b/employee-portal/src/lib/shared/components/centralFile/display/CentralFileFacilityDetails.tsx index 66af588a8..484fc0019 100644 --- a/employee-portal/src/lib/shared/components/centralFile/display/CentralFileFacilityDetails.tsx +++ b/employee-portal/src/lib/shared/components/centralFile/display/CentralFileFacilityDetails.tsx @@ -3,16 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { ReactNode } from "react"; import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider"; import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; -import { ExternalLinkDetailsCell } from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { ExternalLinkDetailsItem } from "@/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem"; import { BaseAddress } from "@/lib/shared/helpers/address"; export interface CentralFileFacility { @@ -22,8 +21,6 @@ export interface CentralFileFacility { readonly phoneNumbers?: string[]; } -const fieldName = createFieldNameMapper<CentralFileFacility>(); - export interface CentralFileFacilityDetailsProps<T> { readonly facility: T; readonly columnSx?: SxProps; @@ -46,11 +43,7 @@ export function CentralFileFacilityDetails<T extends CentralFileFacility>( width="100%" > <DetailsColumn sx={props.columnSx}> - <DetailsCell - name={fieldName("name")} - label={"Name"} - value={facility.name} - /> + <DetailsItem label="Name" value={facility.name} /> {props.children} </DetailsColumn> {facility.contactAddress && ( @@ -62,19 +55,17 @@ export function CentralFileFacilityDetails<T extends CentralFileFacility>( {emailAddresses.length + phoneNumbers.length > 0 && ( <DetailsColumn sx={props.columnSx}> {emailAddresses.map((email, index) => ( - <ExternalLinkDetailsCell + <ExternalLinkDetailsItem key={`${email}.${index}`} - name={`emailAddress.${index}`} - label={"E-Mail-Adresse"} + label="E-Mail-Adresse" value={email} href={(value) => `mailto:${value}`} /> ))} {phoneNumbers.map((phoneNumber, index) => ( - <DetailsCell + <DetailsItem key={`${phoneNumber}.${index}`} - name={`phoneNumber.${index}`} - label={"Telefonnummer"} + label="Telefonnummer" value={phoneNumber} /> ))} diff --git a/employee-portal/src/lib/shared/components/centralFile/display/CentralFilePersonDetails.tsx b/employee-portal/src/lib/shared/components/centralFile/display/CentralFilePersonDetails.tsx index b92933257..ef2fb966b 100644 --- a/employee-portal/src/lib/shared/components/centralFile/display/CentralFilePersonDetails.tsx +++ b/employee-portal/src/lib/shared/components/centralFile/display/CentralFilePersonDetails.tsx @@ -11,7 +11,6 @@ import { import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { translateCountry } from "@eshg/lib-portal/helpers/countryOption"; import { calculateAge } from "@eshg/lib-portal/helpers/dateTime"; -import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; import { Stack } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { isDefined } from "remeda"; @@ -19,10 +18,10 @@ import { isDefined } from "remeda"; import { GENDER_VALUES } from "@/lib/businessModules/schoolEntry/features/procedures/translations"; import { ResponsiveDivider } from "@/lib/shared/components/ResponsiveDivider"; import { BaseAddressDetails } from "@/lib/shared/components/address/BaseAddressDetails"; -import { DetailsCell } from "@/lib/shared/components/detailsSection/DetailsCell"; import { DetailsColumn } from "@/lib/shared/components/detailsSection/DetailsColumn"; import { DetailsRow } from "@/lib/shared/components/detailsSection/DetailsRow"; -import { ExternalLinkDetailsCell } from "@/lib/shared/components/detailsSection/ExternalLinkDetailsCell"; +import { DetailsItem } from "@/lib/shared/components/detailsSection/items/DetailsItem"; +import { ExternalLinkDetailsItem } from "@/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem"; import { BaseAddress } from "@/lib/shared/helpers/address"; export interface CentralFilePerson { @@ -40,8 +39,6 @@ export interface CentralFilePerson { readonly contactAddress?: BaseAddress; } -const fieldName = createFieldNameMapper<CentralFilePerson>(); - export interface CentralFilePersonDetailsProps { readonly person: CentralFilePerson; readonly columnSx?: SxProps; @@ -65,65 +62,52 @@ export function CentralFilePersonDetails(props: CentralFilePersonDetailsProps) { {(isDefined(person.salutation) || isDefined(person.title)) && ( <DetailsRow> {isDefined(person.salutation) && ( - <DetailsCell - name={fieldName("salutation")} + <DetailsItem label={PERSON_FIELD_NAME.salutation} value={SALUTATION_VALUES[person.salutation]} /> )} - <DetailsCell - name={fieldName("title")} - label={PERSON_FIELD_NAME.title} - value={person.title} - /> + <DetailsItem label={PERSON_FIELD_NAME.title} value={person.title} /> </DetailsRow> )} - <DetailsCell - name={fieldName("firstName")} + <DetailsItem label={PERSON_FIELD_NAME.firstName} value={person.firstName} /> - <DetailsCell - name={fieldName("lastName")} + <DetailsItem label={PERSON_FIELD_NAME.lastName} value={person.lastName} /> <DetailsRow> - <DetailsCell - name={fieldName("dateOfBirth")} + <DetailsItem label={PERSON_FIELD_NAME.dateOfBirth} value={formatDate(person.dateOfBirth)} /> {props.showAge && ( - <DetailsCell - name="currentAge" + <DetailsItem label="Alter" value={calculateAge(person.dateOfBirth)} /> )} {isDefined(person.gender) && ( - <DetailsCell - name={fieldName("gender")} + <DetailsItem label={PERSON_FIELD_NAME.gender} value={GENDER_VALUES[person.gender]} /> )} </DetailsRow> - <DetailsCell - name={fieldName("nameAtBirth")} + <DetailsItem label={PERSON_FIELD_NAME.nameAtBirth} value={person.nameAtBirth} /> {(person.placeOfBirth ?? person.countryOfBirth) && ( <DetailsRow> - <DetailsCell - name={fieldName("placeOfBirth")} + <DetailsItem label={PERSON_FIELD_NAME.placeOfBirth} value={person.placeOfBirth} /> {person.countryOfBirth && ( - <DetailsCell - name={fieldName("countryOfBirth")} + <DetailsItem label={PERSON_FIELD_NAME.countryOfBirth} value={translateCountry(person.countryOfBirth)} /> @@ -140,18 +124,16 @@ export function CentralFilePersonDetails(props: CentralFilePersonDetailsProps) { {emailAddresses.length + phoneNumbers.length > 0 && ( <DetailsColumn sx={props.columnSx}> {emailAddresses.map((email, index) => ( - <ExternalLinkDetailsCell + <ExternalLinkDetailsItem key={`${email}.${index}`} - name={`emailAddress.${index}`} label={PERSON_FIELD_NAME.emailAddresses} value={email} href={(value) => `mailto:${value}`} /> ))} {phoneNumbers.map((phoneNumber, index) => ( - <DetailsCell + <DetailsItem key={`${phoneNumber}.${index}`} - name={`phoneNumber.${index}`} label={PERSON_FIELD_NAME.phoneNumbers} value={phoneNumber} /> diff --git a/employee-portal/src/lib/shared/components/centralFile/sync/SyncBarrier.tsx b/employee-portal/src/lib/shared/components/centralFile/sync/SyncBarrier.tsx index 463ed8a9e..778222d4f 100644 --- a/employee-portal/src/lib/shared/components/centralFile/sync/SyncBarrier.tsx +++ b/employee-portal/src/lib/shared/components/centralFile/sync/SyncBarrier.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiFacilitySync } from "@eshg/employee-portal-api/officialMedicalService"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { ApiFacilitySync } from "@eshg/official-medical-service-api"; import { useRouter } from "next/navigation"; import { PersonDetails } from "@/lib/businessModules/schoolEntry/api/models/Person"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentDisplay.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentDisplay.tsx index 306702f9a..f479fe318 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentDisplay.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentDisplay.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEditorBodyElementsInner } from "@eshg/employee-portal-api/libEditor"; +import { ApiEditorBodyElementsInner } from "@eshg/lib-editor-api"; import { Box } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; import { MouseEventHandler, PropsWithChildren } from "react"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx index 0c149a677..937e6a56b 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentEditor.tsx @@ -10,7 +10,7 @@ import { ApiEditorBodyElementsInner, ApiUpdateEditorRequest, EditorApiInterface, -} from "@eshg/employee-portal-api/libEditor"; +} from "@eshg/lib-editor-api"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { Box } from "@mui/joy"; import { useEffect, useRef, useState } from "react"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElement.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElement.tsx index 4ea800345..2a2b073ab 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElement.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElement.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEditorBodyElementsInner } from "@eshg/employee-portal-api/libEditor"; +import { ApiEditorBodyElementsInner } from "@eshg/lib-editor-api"; import { Divider, Stack, Typography } from "@mui/joy"; import { ContentElementAudios } from "@/lib/shared/components/contentEditor/ContentElementAudio"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementAudio.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementAudio.tsx index b07307b5a..5ad2d6e4a 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementAudio.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementAudio.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEditorElementAudios } from "@eshg/employee-portal-api/libEditor"; +import { ApiEditorElementAudios } from "@eshg/lib-editor-api"; import { OpenInNew } from "@mui/icons-material"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementFullTextEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementFullTextEditor.tsx index 636644db5..6c555f031 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementFullTextEditor.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementFullTextEditor.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUpdateEditorRequest } from "@eshg/employee-portal-api/libEditor"; +import { ApiUpdateEditorRequest } from "@eshg/lib-editor-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { Divider, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementImages.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementImages.tsx index cec0cde50..197c40e05 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementImages.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementImages.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiFileType } from "@eshg/employee-portal-api/businessProcedures"; -import { ApiEditorElementImages } from "@eshg/employee-portal-api/libEditor"; +import { ApiEditorElementImages } from "@eshg/lib-editor-api"; +import { ApiFileType } from "@eshg/lib-procedures-api"; import { OpenInNew } from "@mui/icons-material"; import { Stack, Typography } from "@mui/joy"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementProperties.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementProperties.tsx index dd6ba6a33..c0d7334b3 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementProperties.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementProperties.tsx @@ -6,7 +6,7 @@ import { ApiEditorBodyElementsInner, ApiUpdateEditorRequest, -} from "@eshg/employee-portal-api/libEditor"; +} from "@eshg/lib-editor-api"; import { Typography } from "@mui/joy"; import { ContentElementFullTextEditor } from "@/lib/shared/components/contentEditor/ContentElementFullTextEditor"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementPropertySheet.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementPropertySheet.tsx index 774f4cda4..f8c73217a 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementPropertySheet.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementPropertySheet.tsx @@ -6,7 +6,7 @@ import { ApiEditorBodyElementsInner, ApiUpdateEditorRequest, -} from "@eshg/employee-portal-api/libEditor"; +} from "@eshg/lib-editor-api"; import { Divider, Typography } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementQA.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementQA.tsx index a1b93bbc9..96bfa8096 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementQA.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementQA.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiEditorElementQA } from "@eshg/employee-portal-api/libEditor"; +import { ApiEditorElementQA } from "@eshg/lib-editor-api"; import { Checkbox, FormControl, diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementQAEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementQAEditor.tsx index 65b78c35e..ae8c0f8d0 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementQAEditor.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementQAEditor.tsx @@ -7,7 +7,7 @@ import { ApiEditorElementAnswer, ApiEditorElementQA, ApiUpdateEditorRequest, -} from "@eshg/employee-portal-api/libEditor"; +} from "@eshg/lib-editor-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import CheckBox from "@mui/icons-material/CheckBox"; import CheckBoxOutlineBlank from "@mui/icons-material/CheckBoxOutlineBlank"; diff --git a/employee-portal/src/lib/shared/components/contentEditor/ContentElementTextEditor.tsx b/employee-portal/src/lib/shared/components/contentEditor/ContentElementTextEditor.tsx index a8ab3191f..eb7fd17a7 100644 --- a/employee-portal/src/lib/shared/components/contentEditor/ContentElementTextEditor.tsx +++ b/employee-portal/src/lib/shared/components/contentEditor/ContentElementTextEditor.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUpdateEditorRequest } from "@eshg/employee-portal-api/libEditor"; +import { ApiUpdateEditorRequest } from "@eshg/lib-editor-api"; import { FormPlus } from "@eshg/lib-portal/components/form/FormPlus"; import { Divider, Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; diff --git a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx index 81439bab1..0f202ee83 100644 --- a/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx +++ b/employee-portal/src/lib/shared/components/detailsSection/DetailsCell.tsx @@ -22,6 +22,11 @@ export interface DetailsCellProps { valueSx?: SxProps; } +/** + * @deprecated This component is very old and messy. Please migrate and start using the newer DetailsItem instead of DetailsCell. + * The new component is based on composition and is more adaptable to situations through `slotProps`. + * We removed the `name` prop, to properly enforce using the labels for locators in tests. + */ export function DetailsCellWrapped(props: DetailsCellProps) { return ( <DetailsCell @@ -40,6 +45,11 @@ export function DetailsCellWrapped(props: DetailsCellProps) { ); } +/** + * @deprecated This component is very old and messy. Please migrate and start using the newer DetailsItem instead of DetailsCell. + * The new component is based on composition and is more adaptable to situations through `slotProps`. + * We removed the `name` prop, to properly enforce using the labels for locators in tests. + */ export function DetailsCell({ name: givenName, label, diff --git a/employee-portal/src/lib/shared/components/detailsSection/items/DetailsItem.tsx b/employee-portal/src/lib/shared/components/detailsSection/items/DetailsItem.tsx new file mode 100644 index 000000000..81af4283f --- /dev/null +++ b/employee-portal/src/lib/shared/components/detailsSection/items/DetailsItem.tsx @@ -0,0 +1,96 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Stack, StackProps, Typography, TypographyProps } from "@mui/joy"; +import { ComponentType, useId } from "react"; +import { isString } from "remeda"; + +export interface DetailsItemProps<TLabelProps, TValueProps> { + label: TypographyProps["children"]; + value: TypographyProps["children"] | undefined; + + avoidWrap?: boolean; + + slots?: { + label?: ComponentType<TLabelProps>; + value?: ComponentType<TValueProps>; + }; + slotProps?: { + label?: Omit<TLabelProps, "children">; + value?: Omit<TValueProps, "children">; + root?: StackProps; + }; +} + +export function DetailsItem< + TLabelProps = TypographyProps, + TValueProps = TypographyProps, +>(props: DetailsItemProps<TLabelProps, TValueProps>) { + const LabelComponent = props.slots?.label ?? DetailsItemLabel; + const DefaultValueComponent = props.avoidWrap + ? DetailsValueAvoidWrap + : DetailsItemValue; + const ValueComponent = props.slots?.value ?? DefaultValueComponent; + + const labelProps = props.slotProps?.label; + const valueProps = props.slotProps?.value; + + const isValueEmpty = + props.value === undefined || + (isString(props.value) && props.value.trim() === ""); + + const id = useId(); + + if (isValueEmpty) { + return null; + } + + return ( + <Stack gap={0.25} {...props.slotProps?.root}> + <LabelComponent {...(labelProps as TLabelProps)} id={id}> + {props.label} + </LabelComponent> + <ValueComponent {...(valueProps as TValueProps)} aria-labelledby={id}> + {props.value} + </ValueComponent> + </Stack> + ); +} + +export function DetailsItemLabel({ sx, ...props }: TypographyProps) { + return ( + <Typography + level="body-sm" + textColor="text.secondary" + noWrap + sx={{ + width: "fit-content", + maxWidth: "100%", + ...sx, + }} + {...props} + /> + ); +} + +export function DetailsItemValue({ sx, ...props }: TypographyProps) { + return ( + <Typography + level="title-md" + sx={{ + hyphens: "auto", + textWrap: "pretty", + wordBreak: "normal", + overflowWrap: "anywhere", + ...sx, + }} + {...props} + /> + ); +} + +export function DetailsValueAvoidWrap({ sx, ...props }: TypographyProps) { + return <DetailsItemValue sx={{ width: "fit-content", ...sx }} {...props} />; +} diff --git a/employee-portal/src/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem.tsx b/employee-portal/src/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem.tsx new file mode 100644 index 000000000..83c1ea9e3 --- /dev/null +++ b/employee-portal/src/lib/shared/components/detailsSection/items/ExternalLinkDetailsItem.tsx @@ -0,0 +1,69 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ExternalLink } from "@eshg/lib-portal/components/navigation/ExternalLink"; +import { TypographyProps } from "@mui/joy"; +import { ComponentProps } from "react"; +import { isString } from "remeda"; + +import { + DetailsItem, + DetailsItemProps, + DetailsItemValue, +} from "@/lib/shared/components/detailsSection/items/DetailsItem"; + +type ReducedItemProps<TLabelProps> = Omit< + DetailsItemProps< + TLabelProps, + ComponentProps<typeof DetailsValueExternalLink> + >, + "value" +>; + +interface ExternalLinkDetailsItemProps<TLabelProps> + extends ReducedItemProps<TLabelProps> { + value: string | undefined; + href: (value: string) => string; +} + +export function ExternalLinkDetailsItem<TLabelProps = TypographyProps>( + props: ExternalLinkDetailsItemProps<TLabelProps>, +) { + return ( + <DetailsItem + {...props} + slots={{ + ...props.slots, + value: DetailsValueExternalLink, + }} + slotProps={{ + ...props.slotProps, + value: { + href: props.href, + ...props.slotProps?.value, + }, + }} + /> + ); +} + +function DetailsValueExternalLink({ + children, + href, + ...props +}: TypographyProps & { + href: (value: string) => string; +}) { + return ( + <DetailsItemValue {...props}> + <ExternalLink + href={isString(children) ? href(children) : undefined} + sx={{ wordBreak: "break-word", hyphens: "manual" }} + > + {children} + </ExternalLink> + </DetailsItemValue> + ); +} diff --git a/employee-portal/src/lib/shared/components/filterSettings/FilterSettings.tsx b/employee-portal/src/lib/shared/components/filterSettings/FilterSettings.tsx index fc1cb4ee8..24c1e080d 100644 --- a/employee-portal/src/lib/shared/components/filterSettings/FilterSettings.tsx +++ b/employee-portal/src/lib/shared/components/filterSettings/FilterSettings.tsx @@ -17,6 +17,7 @@ import { FilterTemplates, FilterTemplatesProps, } from "@/lib/shared/components/filterSettings/FilterTemplates"; +import { TextFilter } from "@/lib/shared/components/filterSettings/TextFilter"; import { YearFilter } from "@/lib/shared/components/filterSettings/YearFilter"; import { FilterDefinition } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; import { FilterDraftValue } from "@/lib/shared/components/filterSettings/models/FilterValue"; @@ -170,6 +171,19 @@ export function FilterSettings(props: FilterSettingsProps) { } /> ); + case "Text": + return ( + <TextFilter + definition={item.filterDefinition} + value={findValueByDefinition( + props.draftValues, + item.filterDefinition, + )} + onChange={(value) => + props.onDraftValueChange(item.filterDefinition.key, value) + } + /> + ); } }} /> diff --git a/employee-portal/src/lib/shared/components/filterSettings/SearchInstitutionFilter.tsx b/employee-portal/src/lib/shared/components/filterSettings/SearchInstitutionFilter.tsx index 187d86553..ed9e9ae59 100644 --- a/employee-portal/src/lib/shared/components/filterSettings/SearchInstitutionFilter.tsx +++ b/employee-portal/src/lib/shared/components/filterSettings/SearchInstitutionFilter.tsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { SearchOutlined } from "@mui/icons-material"; import { Autocomplete } from "@mui/joy"; import { useState } from "react"; import { identity } from "remeda"; @@ -17,6 +16,7 @@ import { mapContactToSelectOption } from "@/lib/shared/helpers/selectOptionMappe interface SearchInstitutionFilterProps { institutionId: string | undefined; onChange: (institutionId: string | undefined) => void; + placeholder: string; } export function SearchInstitutionFilter(props: SearchInstitutionFilterProps) { @@ -41,8 +41,7 @@ export function SearchInstitutionFilter(props: SearchInstitutionFilterProps) { inputValue={institutionName} options={institutionOptions} filterOptions={identity()} - placeholder="Schule suchen" - endDecorator={<SearchOutlined />} + placeholder={props.placeholder} loading={searchInstitutions.isLoading} onInputChange={(_, newInputValue) => setInstitutionName(newInputValue)} onChange={(_event, value) => { diff --git a/employee-portal/src/lib/shared/components/filterSettings/TextFilter.tsx b/employee-portal/src/lib/shared/components/filterSettings/TextFilter.tsx new file mode 100644 index 000000000..002b5b480 --- /dev/null +++ b/employee-portal/src/lib/shared/components/filterSettings/TextFilter.tsx @@ -0,0 +1,40 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Input } from "@mui/joy"; + +import { + TextFilterDefinition, + TextFilterValue, +} from "@/lib/shared/components/filterSettings/models/TextFilter"; + +export interface TextFilterProps { + definition: TextFilterDefinition; + value: TextFilterValue | null; + onChange: (value: TextFilterValue | null) => void; +} + +export function TextFilter(props: TextFilterProps) { + function handleChange(optionValue: string) { + props.onChange( + optionValue + ? { + type: "Text", + key: props.definition.key, + value: optionValue, + } + : null, + ); + } + + return ( + <Input + type="text" + value={props.value?.value ?? ""} + onChange={(event) => handleChange(event.target.value)} + sx={{ width: "100%" }} + /> + ); +} diff --git a/employee-portal/src/lib/shared/components/filterSettings/models/FilterDefinition.ts b/employee-portal/src/lib/shared/components/filterSettings/models/FilterDefinition.ts index 767c1ddb5..d08a85861 100644 --- a/employee-portal/src/lib/shared/components/filterSettings/models/FilterDefinition.ts +++ b/employee-portal/src/lib/shared/components/filterSettings/models/FilterDefinition.ts @@ -7,6 +7,7 @@ import { DateComparisonFilterDefinition } from "@/lib/shared/components/filterSe import { DateSpanFilterDefinition } from "@/lib/shared/components/filterSettings/models/DateSpanFilter"; import { EnumFilterDefinition } from "@/lib/shared/components/filterSettings/models/EnumFilter"; import { NumberFilterDefinition } from "@/lib/shared/components/filterSettings/models/NumberFilter"; +import { TextFilterDefinition } from "@/lib/shared/components/filterSettings/models/TextFilter"; import { YearFilterDefinition } from "@/lib/shared/components/filterSettings/models/YearFilter"; import { DateFilterDefinition } from "./DateFilter"; @@ -26,4 +27,5 @@ export type FilterDefinition = | DateSpanFilterDefinition | DateComparisonFilterDefinition | NumberFilterDefinition - | YearFilterDefinition; + | YearFilterDefinition + | TextFilterDefinition; diff --git a/employee-portal/src/lib/shared/components/filterSettings/models/FilterValue.ts b/employee-portal/src/lib/shared/components/filterSettings/models/FilterValue.ts index 9c109b258..8d4cc6e15 100644 --- a/employee-portal/src/lib/shared/components/filterSettings/models/FilterValue.ts +++ b/employee-portal/src/lib/shared/components/filterSettings/models/FilterValue.ts @@ -12,6 +12,7 @@ import { NumberFilterDraftValue, NumberFilterValue, } from "@/lib/shared/components/filterSettings/models/NumberFilter"; +import { TextFilterValue } from "@/lib/shared/components/filterSettings/models/TextFilter"; import { YearFilterValue } from "@/lib/shared/components/filterSettings/models/YearFilter"; import { DateFilterValue } from "./DateFilter"; @@ -29,7 +30,8 @@ export type FilterValue = | EnumFilterValue | EnumSingleFilterValue | NumberFilterValue - | YearFilterValue; + | YearFilterValue + | TextFilterValue; export type FilterDraftValue = | DateFilterValue @@ -38,4 +40,5 @@ export type FilterDraftValue = | EnumFilterValue | EnumSingleFilterValue | NumberFilterDraftValue - | YearFilterValue; + | YearFilterValue + | TextFilterValue; diff --git a/employee-portal/src/lib/shared/components/filterSettings/models/TextFilter.ts b/employee-portal/src/lib/shared/components/filterSettings/models/TextFilter.ts new file mode 100644 index 000000000..0ddc8014d --- /dev/null +++ b/employee-portal/src/lib/shared/components/filterSettings/models/TextFilter.ts @@ -0,0 +1,16 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FilterDefinitionBase } from "@/lib/shared/components/filterSettings/models/FilterDefinition"; +import { FilterValueBase } from "@/lib/shared/components/filterSettings/models/FilterValue"; + +export interface TextFilterDefinition extends FilterDefinitionBase { + type: "Text"; +} + +export interface TextFilterValue extends FilterValueBase { + type: "Text"; + value: string; +} diff --git a/employee-portal/src/lib/shared/components/filterSettings/useSearchParamStateProvider.ts b/employee-portal/src/lib/shared/components/filterSettings/useSearchParamStateProvider.ts index 7c3838b2f..6703b1c24 100644 --- a/employee-portal/src/lib/shared/components/filterSettings/useSearchParamStateProvider.ts +++ b/employee-portal/src/lib/shared/components/filterSettings/useSearchParamStateProvider.ts @@ -94,6 +94,8 @@ export function activeValueToParamValues(activeValue: FilterValue): string[] { return [activeValue.startDate, activeValue.endDate].filter( (value) => value !== undefined, ); + case "Text": + return [activeValue.value]; } } diff --git a/employee-portal/src/lib/shared/components/form/MultiFormButtonBar.tsx b/employee-portal/src/lib/shared/components/form/MultiFormButtonBar.tsx index 33bcbb627..0910d2394 100644 --- a/employee-portal/src/lib/shared/components/form/MultiFormButtonBar.tsx +++ b/employee-portal/src/lib/shared/components/form/MultiFormButtonBar.tsx @@ -13,6 +13,7 @@ interface MultiFormButtonBarProps { onCancel?: () => void; onBack?: () => void; onDelete?: () => void; + onReject?: () => void; submitting: boolean; submitLabel: string | undefined; } @@ -43,6 +44,11 @@ export function MultiFormButtonBar(props: MultiFormButtonBarProps) { Entfernen </Button> )} + {isDefined(props.onReject) && ( + <Button variant="plain" color="danger" onClick={props.onReject}> + Ablehnen + </Button> + )} {isDefined(props.submitLabel) && ( <SubmitButton submitting={props.submitting} diff --git a/employee-portal/src/lib/shared/components/formDialog/FormDialog.tsx b/employee-portal/src/lib/shared/components/formDialog/FormDialog.tsx index cf912d11a..89117e7b7 100644 --- a/employee-portal/src/lib/shared/components/formDialog/FormDialog.tsx +++ b/employee-portal/src/lib/shared/components/formDialog/FormDialog.tsx @@ -37,7 +37,7 @@ export function FormDialog<T extends FormikValues>({ return ( <BaseModal modalTitle={title} color={color} open={open} onClose={onClose}> <Formik initialValues={initialValues} onSubmit={onSubmit}> - {({ isSubmitting, handleSubmit }) => { + {({ isSubmitting }) => { return ( <FormPlus> <Typography textColor="text.secondary">{description}</Typography> @@ -54,10 +54,10 @@ export function FormDialog<T extends FormikValues>({ {cancelLabel} </Button> <Button + type="submit" size="sm" color={color} loading={isSubmitting} - onClick={() => handleSubmit()} loadingPosition={"start"} data-testid="formDialogConfirm" > diff --git a/employee-portal/src/lib/shared/components/formFields/CheckboxField.tsx b/employee-portal/src/lib/shared/components/formFields/CheckboxField.tsx index 53d071327..94051b478 100644 --- a/employee-portal/src/lib/shared/components/formFields/CheckboxField.tsx +++ b/employee-portal/src/lib/shared/components/formFields/CheckboxField.tsx @@ -8,8 +8,8 @@ import { useBaseField } from "@eshg/lib-portal/components/formFields/BaseField"; import { FieldProps } from "@eshg/lib-portal/types/form"; import { Checkbox, CheckboxProps, FormControl, FormHelperText } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; -import { useFormikContext } from "formik"; -import { ChangeEventHandler } from "react"; +import { FormikHandlers, useFormikContext } from "formik"; +import { ChangeEventHandler, memo } from "react"; import { isString } from "remeda"; export interface CheckboxFieldProps extends FieldProps<boolean> { @@ -22,10 +22,9 @@ export interface CheckboxFieldProps extends FieldProps<boolean> { "aria-label"?: string; } -function isChecked( - value: string | number | readonly string[], - representingValue?: string, -): boolean { +type ValueType = boolean | number | string | readonly string[]; + +function isChecked(value: ValueType, representingValue?: string): boolean { if (value == null) { return false; } @@ -37,12 +36,11 @@ function isChecked( } export function CheckboxField(props: CheckboxFieldProps) { - const disabled = useIsFormDisabled() || props.disabled; const { input: field, required, meta, - } = useBaseField<number | string | readonly string[]>({ + } = useBaseField<ValueType>({ name: props.name, required: props.required, type: "checkbox", @@ -53,34 +51,70 @@ export function CheckboxField(props: CheckboxFieldProps) { return props.required; }, }); + const { isValid } = useFormikContext(); + + return ( + <MemoizedCheckboxField + {...props} + fieldName={field.name} + fieldValue={field.value} + fieldOnChange={field.onChange} + fieldOnBlur={field.onBlur} + fieldRequired={required} + metaError={meta.error} + isValid={isValid} + /> + ); +} + +interface InnerCheckboxFieldProps extends CheckboxFieldProps { + fieldName: string; + fieldValue: ValueType; + fieldOnChange: FormikHandlers["handleChange"]; + fieldOnBlur: FormikHandlers["handleBlur"]; + fieldRequired: boolean; + metaError?: string; + isValid: boolean; +} + +const MemoizedCheckboxField = memo(function InnerCheckboxField({ + fieldName, + fieldValue, + fieldOnChange, + fieldOnBlur, + fieldRequired, + metaError, + isValid, + ...props +}: InnerCheckboxFieldProps) { + const disabled = useIsFormDisabled() || props.disabled; const starLabel = isString(props.label) && props.required ? `${props.label} *` : props.label; - const { isValid } = useFormikContext(); - const hasValidationError = !!meta.error && !isValid; + const hasValidationError = !!metaError && !isValid; // Often checkbox helper text is shown for a whole group, // only show for single checkbox if the checkbox is "required" const showHelperText = hasValidationError && props.required != null; return ( - <FormControl error={hasValidationError} required={required}> + <FormControl error={hasValidationError} required={fieldRequired}> <Checkbox - name={field.name} + name={fieldName} onChange={(event) => { - field.onChange(event); + fieldOnChange(event); if (props.onChange != null) { props.onChange(event); } }} - onBlur={field.onBlur} + onBlur={fieldOnBlur} label={starLabel} disabled={disabled} - checked={isChecked(field.value, props.representingValue)} + checked={isChecked(fieldValue, props.representingValue)} value={props.representingValue ?? "true"} size={props.size} - required={required} + required={fieldRequired} variant={props.variant} sx={props.sx} slotProps={{ @@ -92,4 +126,4 @@ export function CheckboxField(props: CheckboxFieldProps) { ) : null} </FormControl> ); -} +}); diff --git a/employee-portal/src/lib/shared/components/formFields/SearchContactField.tsx b/employee-portal/src/lib/shared/components/formFields/SearchContactField.tsx index 487ac9a6b..66974b389 100644 --- a/employee-portal/src/lib/shared/components/formFields/SearchContactField.tsx +++ b/employee-portal/src/lib/shared/components/formFields/SearchContactField.tsx @@ -4,7 +4,6 @@ */ import { ApiContactCategory } from "@eshg/base-api"; -import { SearchOutlined } from "@mui/icons-material"; import { contactCategoryNames } from "@/lib/baseModule/shared/translations"; import { SelectContactField } from "@/lib/shared/components/formFields/SelectContactField"; @@ -34,7 +33,6 @@ export function SearchContactField(props: SearchContactFieldProps) { categories={new Set([props.category])} placeholder={`${translatedCategory} suchen`} required={requiredMessage[props.category]} - endDecorator={<SearchOutlined />} /> ); } diff --git a/employee-portal/src/lib/shared/components/formFields/SearchMultipleContactsField.tsx b/employee-portal/src/lib/shared/components/formFields/SearchMultipleContactsField.tsx index b16f0b9b2..af173f022 100644 --- a/employee-portal/src/lib/shared/components/formFields/SearchMultipleContactsField.tsx +++ b/employee-portal/src/lib/shared/components/formFields/SearchMultipleContactsField.tsx @@ -5,7 +5,6 @@ import { ApiContactCategory } from "@eshg/base-api"; import { SingleAutocompleteField } from "@eshg/lib-portal/components/formFields/autocomplete/SingleAutocompleteField"; -import { SearchOutlined } from "@mui/icons-material"; import { useState } from "react"; import { useSearchContacts } from "@/lib/baseModule/api/queries/contacts"; @@ -35,7 +34,6 @@ export function SearchMultipleContactsField(props: SearchContactFieldProps) { required={`Bitte ein/e ${categories} angeben.`} options={options} placeholder={`${categories} suchen`} - endDecorator={<SearchOutlined />} loading={searchContacts.isLoading} onInputChange={(_, newInputValue) => setContactName(newInputValue)} disableFiltering diff --git a/employee-portal/src/lib/shared/components/formFields/SelectContactField.tsx b/employee-portal/src/lib/shared/components/formFields/SelectContactField.tsx index 31961102c..83cb27f70 100644 --- a/employee-portal/src/lib/shared/components/formFields/SelectContactField.tsx +++ b/employee-portal/src/lib/shared/components/formFields/SelectContactField.tsx @@ -23,6 +23,7 @@ interface SelectContactFieldProps { required?: string; placeholder?: string; endDecorator?: ReactNode; + disabled?: boolean; } export function SelectContactField(props: SelectContactFieldProps) { @@ -42,6 +43,7 @@ export function SelectContactField(props: SelectContactFieldProps) { required={props.required} placeholder={props.placeholder} endDecorator={props.endDecorator} + disabled={props.disabled} /> ); } diff --git a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx index c379a37cf..5b4145c04 100644 --- a/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx +++ b/employee-portal/src/lib/shared/components/formFields/TextareaField.tsx @@ -27,6 +27,7 @@ export interface TextareaFieldProps extends ValidationRules<string> { "data-testid"?: string; "aria-label"?: string; slotProps?: TextareaProps["slotProps"]; + className?: string; } export function TextareaField(props: TextareaFieldProps) { @@ -53,6 +54,7 @@ export function TextareaField(props: TextareaFieldProps) { error={field.error} sx={props.sx} disabled={disabled} + className={props.className} > <Textarea aria-labelledby={props["label-id"]} diff --git a/employee-portal/src/lib/shared/components/gdpr/useGdprValidationTasksAlert.ts b/employee-portal/src/lib/shared/components/gdpr/useGdprValidationTasksAlert.ts index 7883c2134..21b9f579c 100644 --- a/employee-portal/src/lib/shared/components/gdpr/useGdprValidationTasksAlert.ts +++ b/employee-portal/src/lib/shared/components/gdpr/useGdprValidationTasksAlert.ts @@ -5,11 +5,11 @@ "use client"; +import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext"; import { ApiBusinessModule, ApiGetGdprNotificationBannerResponse, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useControlledAlert } from "@eshg/lib-portal/errorHandling/AlertContext"; +} from "@eshg/lib-procedures-api"; import { isPast } from "date-fns"; import { routes } from "@/lib/baseModule/shared/routes"; @@ -19,23 +19,27 @@ export function useGdprValidationTasksAlert({ banner, businessModule, }: { - banner: ApiGetGdprNotificationBannerResponse; + banner: ApiGetGdprNotificationBannerResponse | undefined; businessModule: ApiBusinessModule; }) { - const { openValidationTasksCount, earliestDueDate } = banner; + let numberOfTasksLine = ""; + let deadlineLine = ""; - const numberOfTasksLine = - openValidationTasksCount === 1 - ? `Es liegt eine DSGVO-Anfrage vor.` - : `Es liegen ${openValidationTasksCount} DSGVO-Anfragen vor.`; - const deadlineLine = - earliestDueDate === undefined || isPast(earliestDueDate) - ? "Sie müssen diese sofort bearbeiten." - : `Sie haben noch ${formatDurationFromNowUntil(earliestDueDate)} Zeit, diese zu bearbeiten.`; + if (banner) { + const { openValidationTasksCount, earliestDueDate } = banner; + numberOfTasksLine = + openValidationTasksCount === 1 + ? `Es liegt eine DSGVO-Anfrage vor.` + : `Es liegen ${openValidationTasksCount} DSGVO-Anfragen vor.`; + deadlineLine = + earliestDueDate === undefined || isPast(earliestDueDate) + ? "Sie müssen diese sofort bearbeiten." + : `Sie haben noch ${formatDurationFromNowUntil(earliestDueDate)} Zeit, diese zu bearbeiten.`; + } useControlledAlert({ type: "warning", - open: openValidationTasksCount > 0, + open: !!banner && banner.openValidationTasksCount > 0, message: `${numberOfTasksLine} ${deadlineLine}`, action: { text: "Anfragen Prüfen", diff --git a/employee-portal/src/lib/shared/components/icons/AddTextTemplate.tsx b/employee-portal/src/lib/shared/components/icons/AddTextTemplate.tsx index bbb346599..dc27e4415 100644 --- a/employee-portal/src/lib/shared/components/icons/AddTextTemplate.tsx +++ b/employee-portal/src/lib/shared/components/icons/AddTextTemplate.tsx @@ -19,13 +19,13 @@ export function AddTextTemplate(props: SvgIconProps) { fillRule="evenodd" clipRule="evenodd" d="M17.2368 13.3759H15.307V15.3057H13.3772V17.2355H15.307V19.1654H17.2368V17.2355H19.1667V15.3057H17.2368V13.3759Z" - fill="#0B6BCB" + fill="currentColor" /> <path fillRule="evenodd" clipRule="evenodd" d="M10.9498 1.29941L14.8321 5.18167C15.1374 5.48698 15.307 5.89782 15.307 6.32374V10.9862C13.0267 11.2124 11.2137 13.0254 10.9875 15.3057H2.44277C1.55701 15.3057 0.833328 14.582 0.833328 13.6963V2.44147C0.833328 1.55572 1.55701 0.832031 2.44277 0.832031H9.8153C10.2412 0.832031 10.6521 1.00164 10.9498 1.29941ZM9.51754 4.04715H4.04845V5.65659H9.51754V4.04715ZM4.04845 12.0906H9.51754V10.4812H4.04845V12.0906ZM4.04845 8.87171H11.4474V7.26604H4.04845V8.87171Z" - fill="#0B6BCB" + fill="currentColor" /> </svg> </SvgIcon> diff --git a/employee-portal/src/lib/shared/components/infoTile/InformationSheet.tsx b/employee-portal/src/lib/shared/components/infoTile/InformationSheet.tsx index 87c29060d..8843009aa 100644 --- a/employee-portal/src/lib/shared/components/infoTile/InformationSheet.tsx +++ b/employee-portal/src/lib/shared/components/infoTile/InformationSheet.tsx @@ -11,7 +11,11 @@ export function InformationSheet({ sx, dataTestId, ...props -}: PropsWithChildren<SheetProps & { dataTestId?: string }>) { +}: PropsWithChildren< + SheetProps & { + dataTestId?: string; + } +>) { return ( <Sheet sx={{ @@ -24,7 +28,13 @@ export function InformationSheet({ data-testid={dataTestId} {...props} > - <Stack gap={2} sx={{ flexGrow: 1, width: "100%" }}> + <Stack + gap={2} + sx={{ + flexGrow: 1, + width: "100%", + }} + > {children} </Stack> </Sheet> diff --git a/employee-portal/src/lib/shared/components/legacyPersonSidebar/LegacyPersonSidebar.tsx b/employee-portal/src/lib/shared/components/legacyPersonSidebar/LegacyPersonSidebar.tsx index e155d73ea..436077a1d 100644 --- a/employee-portal/src/lib/shared/components/legacyPersonSidebar/LegacyPersonSidebar.tsx +++ b/employee-portal/src/lib/shared/components/legacyPersonSidebar/LegacyPersonSidebar.tsx @@ -90,7 +90,7 @@ export function LegacyPersonSidebar({ const { openCancelDialog } = useConfirmationDialog(); const sidebarPersonFormRef = useRef<SidebarFormHandle>(null); const sidebarSearchFormRef = useRef<SidebarFormHandle>(null); - const searchTitle = searchFormTitle ? searchFormTitle : personFormTitle; + const searchTitle = searchFormTitle ?? personFormTitle; function resetAndCloseForm() { onClose(); diff --git a/employee-portal/src/lib/shared/components/legacyPersonSidebar/form/LegacyBasePersonForm.tsx b/employee-portal/src/lib/shared/components/legacyPersonSidebar/form/LegacyBasePersonForm.tsx index 3a3dfc4d4..9a69ef8ff 100644 --- a/employee-portal/src/lib/shared/components/legacyPersonSidebar/form/LegacyBasePersonForm.tsx +++ b/employee-portal/src/lib/shared/components/legacyPersonSidebar/form/LegacyBasePersonForm.tsx @@ -13,7 +13,10 @@ import { TITLE_OPTIONS, TITLE_VALUES, } from "@eshg/lib-portal/components/formFields/constants"; -import { validateLength } from "@eshg/lib-portal/helpers/validators"; +import { + validateDateOfBirth, + validateLength, +} from "@eshg/lib-portal/helpers/validators"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { Grid, Stack } from "@mui/joy"; @@ -116,6 +119,7 @@ export function LegacyBasePersonForm({ label="Geburtsdatum" required="Bitte ein Geburtsdatum angeben." disabled={disabledFields?.includes("dateOfBirth")} + validate={validateDateOfBirth} /> </Grid> {!hiddenFields?.includes("gender") && ( diff --git a/employee-portal/src/lib/shared/components/legacyPersonSidebar/search/LegacyPersonSearchForm.tsx b/employee-portal/src/lib/shared/components/legacyPersonSidebar/search/LegacyPersonSearchForm.tsx index c5a08f2c6..17c7abf68 100644 --- a/employee-portal/src/lib/shared/components/legacyPersonSidebar/search/LegacyPersonSearchForm.tsx +++ b/employee-portal/src/lib/shared/components/legacyPersonSidebar/search/LegacyPersonSearchForm.tsx @@ -6,6 +6,7 @@ import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; +import { validateDateOfBirth } from "@eshg/lib-portal/helpers/validators"; import SearchIcon from "@mui/icons-material/Search"; import { Stack, Typography } from "@mui/joy"; import { Formik } from "formik"; @@ -72,6 +73,7 @@ export function LegacyPersonSearchForm({ name="dateOfBirth" label="Geburtsdatum" required="Bitte ein Geburtsdatum angeben." + validate={validateDateOfBirth} /> <SubmitButton submitting={loading === true || isSubmitting} diff --git a/employee-portal/src/lib/shared/components/personSearch/PersonSearchForm.tsx b/employee-portal/src/lib/shared/components/personSearch/PersonSearchForm.tsx index ba5192fcf..f47b395a7 100644 --- a/employee-portal/src/lib/shared/components/personSearch/PersonSearchForm.tsx +++ b/employee-portal/src/lib/shared/components/personSearch/PersonSearchForm.tsx @@ -7,6 +7,7 @@ import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { toUtcDate } from "@eshg/lib-portal/helpers/dateTime"; +import { validateDateOfBirth } from "@eshg/lib-portal/helpers/validators"; import { Close, InsertLinkOutlined, @@ -100,6 +101,7 @@ export function PersonSearchForm(props: PersonSearchFormProps) { name="dateOfBirth" label="Geburtsdatum" required="Bitte Geburtsdatum eingeben" + validate={validateDateOfBirth} /> <Button diff --git a/employee-portal/src/lib/shared/components/personSidebar/form/DefaultPersonForm.tsx b/employee-portal/src/lib/shared/components/personSidebar/form/DefaultPersonForm.tsx index 2c1451f77..2631fa572 100644 --- a/employee-portal/src/lib/shared/components/personSidebar/form/DefaultPersonForm.tsx +++ b/employee-portal/src/lib/shared/components/personSidebar/form/DefaultPersonForm.tsx @@ -20,7 +20,10 @@ import { TITLE_OPTIONS, } from "@eshg/lib-portal/components/formFields/constants"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; -import { validateLength } from "@eshg/lib-portal/helpers/validators"; +import { + validateDateOfBirth, + validateLength, +} from "@eshg/lib-portal/helpers/validators"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import { Box, Divider, Grid, IconButton, Stack, Typography } from "@mui/joy"; @@ -134,6 +137,7 @@ export function DefaultPersonForm<TValues extends DefaultPersonFormValues>( ? "Bitte ein Geburtsdatum angeben" : undefined } + validate={validateDateOfBirth} /> </Grid> <Grid xxs> diff --git a/employee-portal/src/lib/shared/components/personSidebar/helpers.ts b/employee-portal/src/lib/shared/components/personSidebar/helpers.ts index afdac432d..6f7fd035d 100644 --- a/employee-portal/src/lib/shared/components/personSidebar/helpers.ts +++ b/employee-portal/src/lib/shared/components/personSidebar/helpers.ts @@ -23,6 +23,7 @@ import { import { DefaultPersonFormValues } from "@/lib/shared/components/personSidebar/form/DefaultPersonForm"; export function normalizeListInputs(input: string[] | undefined): string[] { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing return input === undefined || input.length === 0 ? [""] : input; } diff --git a/employee-portal/src/lib/shared/components/personSidebar/search/DefaultSearchPersonFormFields.tsx b/employee-portal/src/lib/shared/components/personSidebar/search/DefaultSearchPersonFormFields.tsx index a67cde186..1ab111571 100644 --- a/employee-portal/src/lib/shared/components/personSidebar/search/DefaultSearchPersonFormFields.tsx +++ b/employee-portal/src/lib/shared/components/personSidebar/search/DefaultSearchPersonFormFields.tsx @@ -6,7 +6,10 @@ import { DateField } from "@eshg/lib-portal/components/formFields/DateField"; import { InputField } from "@eshg/lib-portal/components/formFields/InputField"; import { createFieldNameMapper } from "@eshg/lib-portal/helpers/form"; -import { validateLength } from "@eshg/lib-portal/helpers/validators"; +import { + validateDateOfBirth, + validateLength, +} from "@eshg/lib-portal/helpers/validators"; import { SearchPersonFormValues } from "@/lib/shared/components/personSidebar/search/SearchPersonSidebar"; @@ -31,6 +34,7 @@ export function DefaultSearchPersonFormFields() { name={fieldName("dateOfBirth")} label="Geburtsdatum" required="Bitte ein Geburtsdatum angeben." + validate={validateDateOfBirth} /> </> ); diff --git a/employee-portal/src/lib/shared/components/procedures/helper.ts b/employee-portal/src/lib/shared/components/procedures/helper.ts index d8f6b4596..7e77761d7 100644 --- a/employee-portal/src/lib/shared/components/procedures/helper.ts +++ b/employee-portal/src/lib/shared/components/procedures/helper.ts @@ -13,7 +13,7 @@ import { import { ApiManualProgressEntryType, ApiProgressEntryClass, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { ReadonlyURLSearchParams } from "next/navigation"; import { EnumFilterValue } from "@/lib/shared/components/filterSettings/models/EnumFilter"; diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureDetailsSidebar.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureDetailsSidebar.tsx index 8ea3f17f7..211d95a11 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureDetailsSidebar.tsx +++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureDetailsSidebar.tsx @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { SALUTATION_VALUES } from "@eshg/lib-portal/components/formFields/constants"; +import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; import { ApiGetInboxProcedureResponse, ApiInboxProcedureAddress, ApiInboxProcedureStatus, -} from "@eshg/employee-portal-api/businessProcedures"; -import { SALUTATION_VALUES } from "@eshg/lib-portal/components/formFields/constants"; -import { formatDate } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/lib-procedures-api"; import { Button, Divider, Stack, Typography } from "@mui/joy"; import { ReactElement, useState } from "react"; diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureStatusChip.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureStatusChip.tsx index 9db3b6408..77045d7fe 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureStatusChip.tsx +++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProcedureStatusChip.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiInboxProcedureStatus } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiInboxProcedureStatus } from "@eshg/lib-procedures-api"; import Chip from "@mui/joy/Chip"; import { statusColors, statusNames } from "./constants"; diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresPage.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresPage.tsx index f88a91741..3b132ec63 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresPage.tsx +++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresPage.tsx @@ -5,7 +5,7 @@ "use client"; -import { ApiProcedureType } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiProcedureType } from "@eshg/lib-procedures-api"; import { UseFetchInboxProcedure, 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 92cb1e4b5..1c64fbcfd 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx +++ b/employee-portal/src/lib/shared/components/procedures/inbox/InboxProceduresTable.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiProcedureType } from "@eshg/employee-portal-api/businessProcedures"; import { optionsFromRecord } from "@eshg/lib-portal/components/formFields/SelectOptions"; +import { ApiProcedureType } from "@eshg/lib-procedures-api"; import { Stack } from "@mui/joy"; import { diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/columns.tsx b/employee-portal/src/lib/shared/components/procedures/inbox/columns.tsx index 31a699b09..c45534bc4 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/columns.tsx +++ b/employee-portal/src/lib/shared/components/procedures/inbox/columns.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiInboxProcedure } from "@eshg/employee-portal-api/businessProcedures"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiInboxProcedure } from "@eshg/lib-procedures-api"; import { createColumnHelper } from "@tanstack/react-table"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/constants.ts b/employee-portal/src/lib/shared/components/procedures/inbox/constants.ts index d74daa5df..f7256aade 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/constants.ts +++ b/employee-portal/src/lib/shared/components/procedures/inbox/constants.ts @@ -8,7 +8,7 @@ import { ApiInboxProcedureStatus, ApiInboxProgressEntryType, ApiTitle, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { ChipProps } from "@mui/joy/Chip"; export const statusNames = { diff --git a/employee-portal/src/lib/shared/components/procedures/inbox/mutations/useCloseInboxProcedureStatusTemplate.ts b/employee-portal/src/lib/shared/components/procedures/inbox/mutations/useCloseInboxProcedureStatusTemplate.ts index cb1d8872f..26916e397 100644 --- a/employee-portal/src/lib/shared/components/procedures/inbox/mutations/useCloseInboxProcedureStatusTemplate.ts +++ b/employee-portal/src/lib/shared/components/procedures/inbox/mutations/useCloseInboxProcedureStatusTemplate.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiInboxProcedureStatus, InboxProcedureApi, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/lib-procedures-api"; type UseCloseInboxProcedureResult = ReturnType< typeof useCloseInboxProcedureTemplate diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/EntryFile.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/EntryFile.tsx index 576c6c092..8506621f3 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/EntryFile.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/EntryFile.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiInboxProgressEntryFileReference } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiInboxProgressEntryFileReference } from "@eshg/lib-procedures-api"; import { Box } from "@mui/joy"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/FileCardWithActions.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/FileCardWithActions.tsx index 7a8e21de5..af190d938 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/FileCardWithActions.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/FileCardWithActions.tsx @@ -5,8 +5,8 @@ "use client"; -import { ApiAbstractFile } from "@eshg/employee-portal-api/businessProcedures"; import { useFileDownload } from "@eshg/lib-portal/api/files/download"; +import { ApiAbstractFile } from "@eshg/lib-procedures-api"; import DeleteIcon from "@mui/icons-material/Delete"; import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/FileOrDeletionNote.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/FileOrDeletionNote.tsx index ec99d92a3..766e67c39 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/FileOrDeletionNote.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/FileOrDeletionNote.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiInboxProgressEntryFileReference } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiInboxProgressEntryFileReference } from "@eshg/lib-procedures-api"; import { Typography } from "@mui/joy"; import { FileCardWithActions } from "./FileCardWithActions"; 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 4df1805f1..ceed181be 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 @@ -5,8 +5,8 @@ "use client"; -import { ApiProcedureStatus } from "@eshg/employee-portal-api/businessProcedures"; import { RequiresChildren } from "@eshg/lib-portal/types/react"; +import { ApiProcedureStatus } from "@eshg/lib-procedures-api"; import { createContext, useContext, useState } from "react"; import { useHasUserRoleCheck } from "@/lib/shared/hooks/useAccessControl"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/SortSelect.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/SortSelect.tsx index 4dc669ded..49b5447d0 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/SortSelect.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/SortSelect.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiProgressEntrySortOrder } from "@eshg/employee-portal-api/businessProcedures"; import { SelectOptions } from "@eshg/lib-portal/components/formFields/SelectOptions"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; +import { ApiProgressEntrySortOrder } from "@eshg/lib-procedures-api"; import { Select } from "@mui/joy"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/buildTimelineEntryProps.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/buildTimelineEntryProps.tsx index 47f81abbf..d150ba8cf 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/buildTimelineEntryProps.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/buildTimelineEntryProps.tsx @@ -3,15 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { ApiGetProgressEntriesResponseProgressEntriesInner, ApiManualProgressEntry, ApiProcessedInboxProgressEntry, ApiSystemProgressEntry, ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; -import { ButtonLink } from "@eshg/lib-portal/components/buttons/ButtonLink"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/lib-procedures-api"; import CheckIcon from "@mui/icons-material/Check"; import MailOutlinedIcon from "@mui/icons-material/MailOutlined"; import { Sheet, Stack, Typography } from "@mui/joy"; 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 6d6b673e0..392457b02 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 @@ -4,12 +4,12 @@ */ import { systemProgressEntryTypeTitles as dentalSystemProgressEntryTypeTitles } from "@eshg/dental/shared/progressEntries"; +import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; import { ApiInboxProgressEntryType, ApiManualProgressEntryType, ApiProgressEntryClass, -} from "@eshg/employee-portal-api/businessProcedures"; -import { FileType } from "@eshg/lib-portal/components/formFields/file/FileType"; +} from "@eshg/lib-procedures-api"; import { CallOutlined, DescriptionOutlined, diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/helper.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/helper.ts index 9664b83ce..55efbe8d8 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/helper.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/helper.ts @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; import { ApiManualProgressEntryType, ApiProgressEntry, ApiSystemProgressEntry, ApiTriggerType, ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; -import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +} from "@eshg/lib-procedures-api"; import { isDefined, isEmpty } from "remeda"; import { manualProgressEntryFileTypes } from "@/lib/shared/components/procedures/progress-entries/constants"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useHasEditRights.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useHasEditRights.tsx index 35c576526..92ee715f7 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useHasEditRights.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useHasEditRights.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiManualProgressEntry } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiManualProgressEntry } from "@eshg/lib-procedures-api"; import { useGetSelfUser } from "@/lib/baseModule/api/queries/users"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useProgressEntriesFilterSettings.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useProgressEntriesFilterSettings.ts index e6fd0db1a..77fe5d3cd 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useProgressEntriesFilterSettings.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/hooks/useProgressEntriesFilterSettings.ts @@ -4,7 +4,7 @@ */ import { ApiUser } from "@eshg/base-api"; -import { ApiProgressEntryClassFromJSON } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiProgressEntryClassFromJSON } from "@eshg/lib-procedures-api"; import { Dispatch, SetStateAction, startTransition } from "react"; import { EnumFilterDefinition } from "@/lib/shared/components/filterSettings/models/EnumFilter"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/mapper.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/mapper.ts index b44baadd9..729acfe5d 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/mapper.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/mapper.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { ApiAbstractFile, ApiCreateManualProgressEntryRequest, @@ -11,8 +12,7 @@ import { ApiManualProgressEntryType, ApiPatchManualProgressEntryRequest, ApiUpdateFileMetaDataRequest, -} from "@eshg/employee-portal-api/businessProcedures"; -import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; +} from "@eshg/lib-procedures-api"; import { isDefined, isEmpty } from "remeda"; import { FileCardProps } from "@/lib/shared/components/FileCard"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/approvalRequestApi.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/approvalRequestApi.ts index 28d905326..1fd14230c 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/approvalRequestApi.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/approvalRequestApi.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiApprovalRequest } from "@eshg/employee-portal-api/businessProcedures"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiApprovalRequest } from "@eshg/lib-procedures-api"; import { useMutation } from "@tanstack/react-query"; import { ApprovalRequestClient } from "@/lib/shared/components/procedures/progress-entries/types"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/fileApi.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/fileApi.ts index de792b83a..a6fc5e71d 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/fileApi.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/fileApi.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiCreateApprovalRequestRequest } from "@eshg/employee-portal-api/businessProcedures"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +import { ApiCreateApprovalRequestRequest } from "@eshg/lib-procedures-api"; import { FileClient } from "@/lib/shared/components/procedures/progress-entries/types"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/progressEntryApi.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/progressEntryApi.ts index 7bd7fc6dd..fdc43e7b8 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/progressEntryApi.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/mutations/progressEntryApi.ts @@ -3,15 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; +import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; import { ApiCreateApprovalRequestRequest, ApiCreateManualProgressEntryRequest, ApiFileMetaData, ApiPatchManualProgressEntryRequest, ApiUpdateFileMetaDataRequest, -} from "@eshg/employee-portal-api/businessProcedures"; -import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; -import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; +} from "@eshg/lib-procedures-api"; import { isDefined } from "remeda"; import { useProgressEntriesConfig } from "@/lib/shared/components/procedures/progress-entries/ProgressEntriesContext"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/queries/progressEntryApi.ts b/employee-portal/src/lib/shared/components/procedures/progress-entries/queries/progressEntryApi.ts index 181e84b21..db44939ec 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/queries/progressEntryApi.ts +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/queries/progressEntryApi.ts @@ -4,8 +4,8 @@ */ import { ApiUserRole } from "@eshg/base-api"; -import { ApiGetProceduresSortOrder } from "@eshg/employee-portal-api/businessProcedures"; import { type QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; +import { ApiGetProceduresSortOrder } from "@eshg/lib-procedures-api"; import { useSuspenseQuery } from "@tanstack/react-query"; import { useSearchParams } from "next/navigation"; import { isDefined, reverse } from "remeda"; 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 8a8d91c3a..5cf5c245c 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 @@ -5,11 +5,11 @@ "use client"; -import { ApiManualProgressEntryType } from "@eshg/employee-portal-api/businessProcedures"; import { SelectField } from "@eshg/lib-portal/components/formFields/SelectField"; import { buildEnumOptions } from "@eshg/lib-portal/helpers/form"; import { validateFile } from "@eshg/lib-portal/helpers/validators"; import { OptionalFieldValue } from "@eshg/lib-portal/types/form"; +import { ApiManualProgressEntryType } from "@eshg/lib-procedures-api"; import { Stack } from "@mui/joy"; import { Formik, FormikHelpers } from "formik"; import { isEmpty } from "remeda"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ApprovalRequestCard.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ApprovalRequestCard.tsx index f83b3c4b3..47c9d1fa5 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ApprovalRequestCard.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ApprovalRequestCard.tsx @@ -3,10 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - ApiAbstractFile, - ApiApprovalRequest, -} from "@eshg/employee-portal-api/businessProcedures"; +import { ApiAbstractFile, ApiApprovalRequest } from "@eshg/lib-procedures-api"; import { Box, Button, diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/FileAsApprovalRequestEntity.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/FileAsApprovalRequestEntity.tsx index 757e73b2f..efbaa21af 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/FileAsApprovalRequestEntity.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/FileAsApprovalRequestEntity.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiAbstractFile } from "@eshg/employee-portal-api/businessProcedures"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiAbstractFile } from "@eshg/lib-procedures-api"; import { Stack, Typography } from "@mui/joy"; import { useContext } from "react"; import { isDefined } from "remeda"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ProgressEntryAsApprovalRequestEntity.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ProgressEntryAsApprovalRequestEntity.tsx index cfca8be37..dfdafc6f6 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ProgressEntryAsApprovalRequestEntity.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/approvalRequestOverviewSidebar/ProgressEntryAsApprovalRequestEntity.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiManualProgressEntry } from "@eshg/employee-portal-api/businessProcedures"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiManualProgressEntry } from "@eshg/lib-procedures-api"; import { Stack, Typography } from "@mui/joy"; import { useContext } from "react"; import { isDefined } from "remeda"; 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 eb88d27c6..233716637 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 @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; import { ApiGetFile200Response, ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner, ApiInboxProgressEntryFileReference, ApiMail, ApiProgressEntry, -} from "@eshg/employee-portal-api/businessProcedures"; -import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +} from "@eshg/lib-procedures-api"; import DateRangeOutlinedIcon from "@mui/icons-material/DateRangeOutlined"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined"; diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory.tsx index f37ef0258..91d965c72 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/DetailsHistory.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiManualProgressEntry } from "@eshg/employee-portal-api/businessProcedures"; import { formatDateTime } from "@eshg/lib-portal/formatters/dateTime"; +import { ApiManualProgressEntry } from "@eshg/lib-procedures-api"; import { Button, Chip, diff --git a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/InboxProgressEntryDetails.tsx b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/InboxProgressEntryDetails.tsx index 222394ee2..ea21426e4 100644 --- a/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/InboxProgressEntryDetails.tsx +++ b/employee-portal/src/lib/shared/components/procedures/progress-entries/sidebars/progressEntryDetailsSidebar/InboxProgressEntryDetails.tsx @@ -6,7 +6,7 @@ import { ApiProcessedInboxProgressEntry, ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { isDefined } from "remeda"; import { inboxProgressEntryTitles } from "@/lib/shared/components/procedures/progress-entries/constants"; 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 1cf09dc0b..55f089881 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,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; import { ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner, ApiManualProgressEntry, ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; -import { SubmitButton } from "@eshg/lib-portal/components/buttons/SubmitButton"; +} from "@eshg/lib-procedures-api"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import { Button, Chip } from "@mui/joy"; import { Formik } from "formik"; 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 596cce0b1..678e74414 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 @@ -7,7 +7,7 @@ import { ApiGetProgressEntryResponseRelatedKeyDocumentProgressEntriesInner, ApiSystemProgressEntry, ApiUser, -} from "@eshg/employee-portal-api/businessProcedures"; +} from "@eshg/lib-procedures-api"; import { Chip } from "@mui/joy"; import { isDefined } from "remeda"; 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 d4c2cbb9e..2802af95f 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 @@ -6,6 +6,8 @@ "use server"; import { ApiUserRole } from "@eshg/base-api"; +import { QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; +import { SearchParams } from "@eshg/lib-portal/helpers/searchParams"; import { ApiGetDetailedProcedureResponse, ApiGetProcedureApprovalRequestsResponse, @@ -17,9 +19,7 @@ import { GetProgressEntriesRequest, ProcedureApi, ProgressEntryApi, -} from "@eshg/employee-portal-api/businessProcedures"; -import { QueryKeyFactory } from "@eshg/lib-portal/api/queryKeyFactory"; -import { SearchParams } from "@eshg/lib-portal/helpers/searchParams"; +} from "@eshg/lib-procedures-api"; import { UseFilterSettings } from "@/lib/shared/components/filterSettings/useFilterSettings"; diff --git a/employee-portal/src/lib/shared/components/table/TableSheet.tsx b/employee-portal/src/lib/shared/components/table/TableSheet.tsx index b8114b282..3700197ac 100644 --- a/employee-portal/src/lib/shared/components/table/TableSheet.tsx +++ b/employee-portal/src/lib/shared/components/table/TableSheet.tsx @@ -22,11 +22,12 @@ export interface TableSheetProps extends RequiresChildren { hideTable?: boolean; title?: ReactNode; footer?: ReactNode; + "aria-label"?: string; } export function TableSheet(props: TableSheetProps): ReactElement { return ( - <StyledSheet> + <StyledSheet aria-label={props["aria-label"]}> <Stack flex={1} overflow="auto"> {props.title} {props.hideTable ? <Box flex={1} overflow="auto" /> : props.children} diff --git a/employee-portal/src/lib/shared/components/users/userFormatter.ts b/employee-portal/src/lib/shared/components/users/userFormatter.ts index bf06605d2..f50ed4a17 100644 --- a/employee-portal/src/lib/shared/components/users/userFormatter.ts +++ b/employee-portal/src/lib/shared/components/users/userFormatter.ts @@ -3,12 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiUser } from "@eshg/base-api"; import { formatPersonName } from "@eshg/lib-portal/formatters/person"; import { isNonNullish } from "remeda"; export const unknownUser = "Unbekannter Benutzer"; -export function fullName(user: ApiUser | undefined) { +interface NamedUser { + firstName: string; + lastName: string; +} + +export function fullName(user: NamedUser | undefined) { return isNonNullish(user) ? formatPersonName(user) : unknownUser; } diff --git a/employee-portal/src/lib/shared/helpers/facilityUtils.ts b/employee-portal/src/lib/shared/helpers/facilityUtils.ts index a7e19ba00..79847945a 100644 --- a/employee-portal/src/lib/shared/helpers/facilityUtils.ts +++ b/employee-portal/src/lib/shared/helpers/facilityUtils.ts @@ -8,7 +8,7 @@ import { ApiAddFacilityFileStateResponse, ApiFacilityContactPerson, } from "@eshg/base-api"; -import { ApiDataOrigin } from "@eshg/employee-portal-api/inspection"; +import { ApiDataOrigin } from "@eshg/inspection-api"; import { mapOptionalValue } from "@eshg/lib-portal/helpers/form"; import { isNullish } from "remeda"; diff --git a/employee-portal/src/lib/shared/helpers/guards.ts b/employee-portal/src/lib/shared/helpers/guards.ts index 008390a30..e18189c47 100644 --- a/employee-portal/src/lib/shared/helpers/guards.ts +++ b/employee-portal/src/lib/shared/helpers/guards.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiBusinessModule } from "@eshg/employee-portal-api/businessProcedures"; +import { ApiBusinessModule } from "@eshg/lib-procedures-api"; export function isInteger(value: unknown): value is number { return Number.isInteger(value); diff --git a/employee-portal/src/lib/shared/hooks/searchParams/useSearchParam.ts b/employee-portal/src/lib/shared/hooks/searchParams/useSearchParam.ts index 03151082e..e94ee3a74 100644 --- a/employee-portal/src/lib/shared/hooks/searchParams/useSearchParam.ts +++ b/employee-portal/src/lib/shared/hooks/searchParams/useSearchParam.ts @@ -33,12 +33,13 @@ export function useSearchParam<K extends BooleanNumberStringNames>( }); let value = arrayParam[0]; if (castTo === "boolean") { - value = !!value as NonNullable<BooleanNumberStringType<K>>; + value = (value != null) as NonNullable<BooleanNumberStringType<K>>; } - return [ - (value ?? null) as BooleanNumberStringType<K>, - (value) => setArrayParam([value]), - ]; + const setValue = useCallback( + (newValue: BooleanNumberStringType<K>) => setArrayParam([newValue]), + [setArrayParam], + ); + return [(value ?? null) as BooleanNumberStringType<K>, setValue]; } export function updateSearchParam( @@ -106,7 +107,7 @@ export function useSearchParamArray<K extends BooleanNumberStringNames>( return [ rawValues.map( (rawValue) => - (rawValue === "true") as NonNullable<BooleanNumberStringType<K>>, + (rawValue !== "false") as NonNullable<BooleanNumberStringType<K>>, ), setParam, ]; diff --git a/employee-portal/src/serviceWorker/common/common.ts b/employee-portal/src/serviceWorker/common/common.ts index e20a0bf8f..8320910bb 100644 --- a/employee-portal/src/serviceWorker/common/common.ts +++ b/employee-portal/src/serviceWorker/common/common.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiResponse } from "@eshg/employee-portal-api/inspection"; +import { ApiResponse } from "@eshg/inspection-api"; export const SERVICE_WORKER_SERVER_NAME = "eshg-employee-portal-service-worker"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/getPendingFacilities.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/getPendingFacilities.ts index 060363a39..c03864df6 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/getPendingFacilities.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/getPendingFacilities.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiInspPendingFacilitiesOverviewResponse } from "@eshg/employee-portal-api/inspection"; +import { ApiInspPendingFacilitiesOverviewResponse } from "@eshg/inspection-api"; import { precachedInspectionIds } from "@/serviceWorker/common/precachedInspectionIds"; import { getFacilities } from "@/serviceWorker/sw/inspection/service/getFacilities"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/updateChecklist.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/updateChecklist.ts index 0e026407c..888a113bf 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/updateChecklist.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/updateChecklist.ts @@ -6,7 +6,7 @@ import { ApiUpdateChecklistRequestFromJSON, ApiUpdateChecklistResponseToJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { API_INSPECTION_CHECKLISTS_CHECKLIST_PATH_PATTERN } from "@/serviceWorker/sw/config"; import { updateChecklist } from "@/serviceWorker/sw/inspection/service/updateChecklist"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/updateFile.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/updateFile.ts index d9683a82e..b8993abd9 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/updateFile.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/updateFile.ts @@ -7,7 +7,7 @@ import { ApiUpdateChecklistResponseToJSON, ApiUploadMediaFileRequest, ApiUploadMediaFileRequestFromJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { API_INSPECTION_CHECKLISTS_FILE_PATH_PATTERN } from "@/serviceWorker/sw/config"; import { diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/updateIncidents.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/updateIncidents.ts index d3d61fb8f..1293143c1 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/updateIncidents.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/updateIncidents.ts @@ -10,7 +10,7 @@ import { ApiInspectionIncident, ApiInspectionIncidentToJSON, ApiUpdateInspectionIncidentRequestFromJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { uuidV4Re } from "@/serviceWorker/common/common"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/updateInspection.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/updateInspection.ts index 264da5c5c..ae17a6bd0 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/updateInspection.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/updateInspection.ts @@ -6,7 +6,7 @@ import { ApiInspectionToJSON, ApiUpdateInspectionRequestFromJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { uuidV4Re } from "@/serviceWorker/common/common"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/controller/updatePacklists.ts b/employee-portal/src/serviceWorker/sw/inspection/controller/updatePacklists.ts index 9f43edb59..f2ae4b353 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/controller/updatePacklists.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/controller/updatePacklists.ts @@ -6,7 +6,7 @@ import { ApiPacklistToJSON, ApiUpdatePacklistElementRequestFromJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { API_INSPECTION_PACKLISTS_PACKLIST_PATH_PATTERN } from "@/serviceWorker/sw/config"; import { updatePacklistElement } from "@/serviceWorker/sw/inspection/service/updatePacklistElement"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/getFacilities.ts b/employee-portal/src/serviceWorker/sw/inspection/service/getFacilities.ts index 6a0586f6e..e09b9e751 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/getFacilities.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/getFacilities.ts @@ -3,10 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { - ApiInspPendingFacility, - ApiInspection, -} from "@eshg/employee-portal-api/inspection"; +import { ApiInspPendingFacility, ApiInspection } from "@eshg/inspection-api"; import { getInspection } from "@/serviceWorker/sw/inspection/service/updateInspection"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/updateChecklist.ts b/employee-portal/src/serviceWorker/sw/inspection/service/updateChecklist.ts index 282b86a85..a256a6372 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/updateChecklist.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/updateChecklist.ts @@ -10,7 +10,7 @@ import { ApiGetChecklistsResponseFromJSON, ApiGetChecklistsResponseToJSON, ApiUpdateChecklistRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; import { diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/updateFile.ts b/employee-portal/src/serviceWorker/sw/inspection/service/updateFile.ts index fb76efd03..110b37d8b 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/updateFile.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/updateFile.ts @@ -10,7 +10,7 @@ import { ApiGetChecklistsResponseFromJSON, ApiGetChecklistsResponseToJSON, ApiUpdateChecklistElementsInner, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { X_ESHG_INSPECTION_ID, uuidV4Re } from "@/serviceWorker/common/common"; import { diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/updateIncidents.ts b/employee-portal/src/serviceWorker/sw/inspection/service/updateIncidents.ts index a2ef56f40..86a59da77 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/updateIncidents.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/updateIncidents.ts @@ -8,7 +8,7 @@ import { ApiGetInspectionIncidentsResponseToJSON, ApiInspectionIncident, ApiUpdateInspectionIncidentRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { isDefined } from "remeda"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/updateInspection.ts b/employee-portal/src/serviceWorker/sw/inspection/service/updateInspection.ts index 9a31b92c0..f9f103554 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/updateInspection.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/updateInspection.ts @@ -8,7 +8,7 @@ import { ApiInspectionIncident, ApiInspectionPhase, ApiInspectionToJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; import { API_INSPECTION_INSPECTIONS_FINALIZE_PATH_PATTERN } from "@/serviceWorker/sw/config"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/service/updatePacklistElement.ts b/employee-portal/src/serviceWorker/sw/inspection/service/updatePacklistElement.ts index 6791deb57..6c92bbabe 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/service/updatePacklistElement.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/service/updatePacklistElement.ts @@ -8,7 +8,7 @@ import { ApiGetPacklistsResponseToJSON, ApiPacklist, ApiUpdatePacklistElementRequest, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { getFromApiCache, writeToApiCache } from "@/serviceWorker/sw/cache"; diff --git a/employee-portal/src/serviceWorker/sw/inspection/test/cacheMock.ts b/employee-portal/src/serviceWorker/sw/inspection/test/cacheMock.ts index 608ac26cb..dee73dc3a 100644 --- a/employee-portal/src/serviceWorker/sw/inspection/test/cacheMock.ts +++ b/employee-portal/src/serviceWorker/sw/inspection/test/cacheMock.ts @@ -10,7 +10,7 @@ import { ApiGetChecklistsResponseToJSON, ApiInspection, ApiInspectionToJSON, -} from "@eshg/employee-portal-api/inspection"; +} from "@eshg/inspection-api"; import { isString } from "remeda"; import { vi } from "vitest"; diff --git a/employee-portal/src/serviceWorker/sw/replaceRecursive.ts b/employee-portal/src/serviceWorker/sw/replaceRecursive.ts index f946e8332..ccb242ece 100644 --- a/employee-portal/src/serviceWorker/sw/replaceRecursive.ts +++ b/employee-portal/src/serviceWorker/sw/replaceRecursive.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ApiUpdateChecklistElementsInner } from "@eshg/employee-portal-api/inspection"; +import { ApiUpdateChecklistElementsInner } from "@eshg/inspection-api"; import { isArray, isPlainObject } from "remeda"; // Recursively update all objects with matching id and type. diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 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 delta 3889 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dedd5d1e6..d9fbee2e1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6..f3b75f3b0 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/lib-portal/gradleDependencies.json b/lib-portal/gradleDependencies.json index b786b4de5..890ca1a33 100644 --- a/lib-portal/gradleDependencies.json +++ b/lib-portal/gradleDependencies.json @@ -1,3 +1,3 @@ { - "dependencies": [":base-api", ":employee-portal-api"] + "dependencies": [":base-api", ":medical-registry-api"] } diff --git a/lib-portal/package.json b/lib-portal/package.json index 24a6d73ca..e2570187d 100644 --- a/lib-portal/package.json +++ b/lib-portal/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@eshg/base-api": "workspace:*", - "@eshg/employee-portal-api": "workspace:*", + "@eshg/medical-registry-api": "workspace:*", "@mui/icons-material": "catalog:joy", "@mui/joy": "catalog:joy", "@mui/material": "catalog:joy", @@ -26,18 +26,18 @@ "@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", + "@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-import": "catalog:eslint", "eslint-plugin-promise": "catalog:eslint", + "eslint-plugin-unused-imports": "catalog:eslint", "prettier": "catalog:prettier", "typescript": "catalog:common", "vite-tsconfig-paths": "catalog:vitest", diff --git a/lib-portal/src/api/errorInterceptionMiddleware.ts b/lib-portal/src/api/errorInterceptionMiddleware.ts index 2f445f617..b925e50ac 100644 --- a/lib-portal/src/api/errorInterceptionMiddleware.ts +++ b/lib-portal/src/api/errorInterceptionMiddleware.ts @@ -5,6 +5,8 @@ import { Middleware } from "@eshg/base-api"; +import { PortalError } from "../errorHandling/PortalError"; +import { PortalErrorCode } from "../errorHandling/PortalErrorCode"; import { resolveErrorResponse } from "../errorHandling/errorResolvers"; export const errorInterceptionMiddleware = { @@ -18,7 +20,10 @@ export const errorInterceptionMiddleware = { async onError(context) { if (context.response === undefined) { const cause = resolveCause(context.error); - throw new Error(`Failed to fetch ${context.url}${cause}`); + throw new PortalError({ + errorCode: PortalErrorCode.UnexpectedError, + message: `Failed to fetch ${context.url}${cause}`, + }); } return Promise.resolve(); }, diff --git a/lib-portal/src/api/useHandledMutation.ts b/lib-portal/src/api/useHandledMutation.ts index 101b75c4d..1b20dbd32 100644 --- a/lib-portal/src/api/useHandledMutation.ts +++ b/lib-portal/src/api/useHandledMutation.ts @@ -17,6 +17,29 @@ import { } from "../errorHandling/errorMappers"; import { resolveError } from "../errorHandling/errorResolvers"; +interface AlertOptions { + /** + * Enables retrying the mutation from the alert after a recoverable error + * + * Use this option for mutations where the original trigger (e.g. a button) + * is not available anymore when an error occurs. + */ + enableRetryAfterError?: boolean; + /** + * Enables closing any error alert + */ + closeable?: boolean; +} + +interface UseHandledMutationOptions< + TData = unknown, + TError = DefaultError, + TVariables = void, + TContext = unknown, +> extends UseMutationOptions<TData, TError, TVariables, TContext> { + alertOptions?: AlertOptions; +} + /** * Use mutation with default error handling * @@ -28,26 +51,33 @@ export function useHandledMutation< TError = DefaultError, TVariables = void, TContext = unknown, ->(options: UseMutationOptions<TData, TError, TVariables, TContext>) { +>(options: UseHandledMutationOptions<TData, TError, TVariables, TContext>) { + const { alertOptions, ...mutationOptions } = options; const alert = useAlert(); - return useMutation({ - ...options, - onError: runBefore(options.onError, (error) => { + const mutation = useMutation({ + ...mutationOptions, + onError: runBefore(options.onError, (error, variables) => { const { errorCode } = resolveError(error); const { title, message } = getErrorDescription(errorCode); + const onReset = + alertOptions?.enableRetryAfterError === true + ? () => mutation.mutate(variables) + : undefined; alert.error({ title, message, - action: getErrorAction(errorCode), - closeable: getCloseable(errorCode), + action: getErrorAction(errorCode, onReset), + closeable: alertOptions?.closeable ?? getCloseable(errorCode), }); }), onMutate: runBefore(options.onMutate, () => { alert.close(); }), }); + + return mutation; } function runBefore<const TParams extends unknown[]>( diff --git a/lib-portal/src/businessModules/medicalRegistry/api/mapper.ts b/lib-portal/src/businessModules/medicalRegistry/api/mapper.ts index 2c6671693..bd3e306f2 100644 --- a/lib-portal/src/businessModules/medicalRegistry/api/mapper.ts +++ b/lib-portal/src/businessModules/medicalRegistry/api/mapper.ts @@ -13,7 +13,7 @@ import { ApiTypeOfFullChange, ApiTypeOfPracticeChange, CreateProcedureRequest, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; import { isEmpty } from "remeda"; import { diff --git a/lib-portal/src/businessModules/medicalRegistry/constants.ts b/lib-portal/src/businessModules/medicalRegistry/constants.ts index cc7f763b0..58d43a0e6 100644 --- a/lib-portal/src/businessModules/medicalRegistry/constants.ts +++ b/lib-portal/src/businessModules/medicalRegistry/constants.ts @@ -8,7 +8,7 @@ import { ApiEmploymentType, ApiProfessionalTitle, ApiTypeOfChange, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; export const changeTypeNames = { [ApiTypeOfChange.NewRegistration]: "Neuanmeldung", diff --git a/lib-portal/src/businessModules/medicalRegistry/medicalRegistryCreateProcedureFormValues.ts b/lib-portal/src/businessModules/medicalRegistry/medicalRegistryCreateProcedureFormValues.ts index 59d5c768d..02a8a19a5 100644 --- a/lib-portal/src/businessModules/medicalRegistry/medicalRegistryCreateProcedureFormValues.ts +++ b/lib-portal/src/businessModules/medicalRegistry/medicalRegistryCreateProcedureFormValues.ts @@ -9,7 +9,7 @@ import { ApiEmploymentType, ApiProfessionalTitle, ApiTypeOfChange, -} from "@eshg/employee-portal-api/medicalRegistry"; +} from "@eshg/medical-registry-api"; import { NullableFieldValue, OptionalFieldValue } from "../../types/form"; diff --git a/lib-portal/src/businessModules/medicalRegistry/sections.ts b/lib-portal/src/businessModules/medicalRegistry/sections.ts index 58b51b74b..21541e1b5 100644 --- a/lib-portal/src/businessModules/medicalRegistry/sections.ts +++ b/lib-portal/src/businessModules/medicalRegistry/sections.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ApiTypeOfChange } from "@eshg/employee-portal-api/medicalRegistry"; +import { ApiTypeOfChange } from "@eshg/medical-registry-api"; type Section = | "profession" diff --git a/lib-portal/src/components/formFields/InputField.tsx b/lib-portal/src/components/formFields/InputField.tsx index ea8afc09b..2db70a8db 100644 --- a/lib-portal/src/components/formFields/InputField.tsx +++ b/lib-portal/src/components/formFields/InputField.tsx @@ -5,12 +5,14 @@ import { Input, InputProps } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; +import { FormikErrors } from "formik"; import { ChangeEvent, FocusEvent, FocusEventHandler, HTMLInputTypeAttribute, ReactNode, + memo, } from "react"; import { FieldProps } from "../../types/form"; @@ -46,35 +48,75 @@ export interface InputFieldProps } export function InputField(props: Readonly<InputFieldProps>) { + const field = useBaseField<string>(props); + + return ( + <MemoizedInputField + {...props} + fieldHelperText={field.helperText} + fieldRequired={field.required} + fieldError={field.error} + fieldInputValue={field.input.value} + fieldHelpersSetValue={field.helpers.setValue} + fieldInputOnBlur={field.input.onBlur} + fieldInputOnChange={field.input.onChange} + /> + ); +} + +interface InnerInputFieldProps extends InputFieldProps { + fieldHelperText?: string; + fieldRequired: boolean; + fieldError: boolean; + fieldInputValue: string; + fieldHelpersSetValue: ( + value: string, + shouldValidate?: boolean, + ) => Promise<void | FormikErrors<string>>; + fieldInputOnBlur: (event: FocusEvent<HTMLInputElement>) => void; + fieldInputOnChange: (event: ChangeEvent<HTMLInputElement>) => void; +} + +const MemoizedInputField = memo(function InnerInputField( + props: Readonly<InnerInputFieldProps>, +) { const FieldComponent = props.component ?? BaseField; const InputComponent = props.input ?? Input; - const field = useBaseField<string>(props); const disabled = useIsFormDisabled() || props.disabled; + const { + fieldHelperText, + fieldError, + fieldRequired, + fieldHelpersSetValue, + fieldInputValue, + fieldInputOnBlur, + fieldInputOnChange, + } = props; function handleChange(event: ChangeEvent<HTMLInputElement>): void { - field.input.onChange(event); + fieldInputOnChange(event); props.onChange?.(event.target.value); } async function handleBlur(event: FocusEvent<HTMLInputElement>) { if (!props.untrimmedInput) { - const value: string | undefined = field.input.value; + const value: string | undefined = fieldInputValue; const trimmedValue = value?.trim(); if (value !== trimmedValue) { - await field.helpers.setValue(trimmedValue); + await fieldHelpersSetValue(trimmedValue); event.target.value = trimmedValue; } } - field.input.onBlur?.(event); + fieldInputOnBlur?.(event); props.onBlur?.(event); } return ( <FieldComponent label={props.label} - helperText={field.helperText} - required={field.required} - error={field.error} + helperText={fieldHelperText} + required={fieldRequired} + error={fieldError} sx={props.sx} fieldDecorator={props.fieldDecorator} disabled={disabled} @@ -82,7 +124,7 @@ export function InputField(props: Readonly<InputFieldProps>) { <InputComponent type={props.type} name={props.name} - value={field.input.value} + value={fieldInputValue} placeholder={props.placeholder} onChange={handleChange} onFocus={props.onFocus} @@ -108,4 +150,4 @@ export function InputField(props: Readonly<InputFieldProps>) { /> </FieldComponent> ); -} +}); diff --git a/lib-portal/src/components/formFields/SelectField.tsx b/lib-portal/src/components/formFields/SelectField.tsx index 16e171312..1ff9b18dd 100644 --- a/lib-portal/src/components/formFields/SelectField.tsx +++ b/lib-portal/src/components/formFields/SelectField.tsx @@ -5,7 +5,15 @@ import { Select, SelectProps } from "@mui/joy"; import { SxProps } from "@mui/joy/styles/types"; -import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; +import { FieldHelperProps, FormikHandlers } from "formik"; +import { + ReactNode, + memo, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { isDefined } from "remeda"; import { FieldProps } from "../../types/form"; @@ -60,9 +68,47 @@ export function SelectField< TMultiple extends boolean = false, TOptionLabel extends string | ReactNode = string, >(props: SelectFieldProps<TMultiple, TOptionLabel>) { + const field = useBaseField<SelectFieldValue<TMultiple>>(props); + + return ( + <MemoizedSelectField + {...props} + fieldHelperText={field.helperText} + fieldRequired={field.required} + fieldError={field.error} + fieldInputValue={field.input.value} + fieldHelpersSetValue={field.helpers.setValue} + fieldHelpersSetTouched={field.helpers.setTouched} + fieldInputOnBlur={field.input.onBlur} + /> + ); +} + +interface InnerSelectFieldProps< + TMultiple extends boolean, + TOptionLabel extends string | ReactNode = string, +> extends SelectFieldProps<TMultiple, TOptionLabel> { + fieldHelperText?: string; + fieldRequired: boolean; + fieldError: boolean; + fieldInputValue: SelectFieldValue<TMultiple>; + fieldHelpersSetValue: FieldHelperProps< + SelectFieldValue<TMultiple> + >["setValue"]; + fieldHelpersSetTouched: FieldHelperProps< + SelectFieldValue<TMultiple> + >["setTouched"]; + fieldInputOnBlur: FormikHandlers["handleBlur"]; +} + +const MemoizedSelectField = memo(InnerSelectField) as typeof InnerSelectField; + +function InnerSelectField< + TMultiple extends boolean = false, + TOptionLabel extends string | ReactNode = string, +>(props: Readonly<InnerSelectFieldProps<TMultiple, TOptionLabel>>) { const FieldComponent = props.component ?? BaseField; const SelectComponent = props.select ?? Select; - const field = useBaseField<SelectFieldValue<TMultiple>>(props); const disabled = useIsFormDisabled() || props.disabled; const { enqueue } = usePromiseSequencer(); @@ -70,34 +116,34 @@ export function SelectField< return ( <FieldComponent label={props.label} - helperText={field.helperText} - required={field.required} - error={field.error} + helperText={props.fieldHelperText} + required={props.fieldRequired} + error={props.fieldError} className={props.className} sx={props.sx} disabled={disabled} > <SelectComponent name={props.name} - value={toJoyUiSelectValue<TMultiple>(field.input.value)} + value={toJoyUiSelectValue<TMultiple>(props.fieldInputValue)} onChange={(_, newValue) => { const emptyValue = props.multiple ? [] : ""; const newFieldValue = (newValue ?? emptyValue) as SelectFieldValue<TMultiple>; enqueue(async () => { - await field.helpers.setValue(newFieldValue); + await props.fieldHelpersSetValue(newFieldValue); if (isDefined(props.onChange)) { props.onChange(newFieldValue); } }); }} // Trigger the validation when the dropdown is closed, acting similar to a blur - onClose={() => enqueue(() => field.helpers.setTouched(true, true))} + onClose={() => enqueue(() => props.fieldHelpersSetTouched(true, true))} onBlur={(event) => { // The relatedTarget is a <li> when the dropdown is opened, // we don't want to trigger the blur in that case. if (event.relatedTarget instanceof HTMLInputElement) { - field.input.onBlur(event); + props.fieldInputOnBlur(event); } }} multiple={props.multiple} diff --git a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx index b5e8b2ac6..a3cb2b01a 100644 --- a/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx +++ b/lib-portal/src/components/formFields/appointmentPicker/AppointmentListForDate.tsx @@ -5,6 +5,7 @@ import { Chip, + ChipProps, Divider, List, ListItem, @@ -73,7 +74,14 @@ export function AppointmentListForDate<T extends Appointment>({ onAppointmentSelected, isAppointmentEqual = (apt1, apt2) => apt1 === apt2, label, -}: AppointmentListProps<T>) { + slotProps, + getLabel = (apt) => timeForm.format(apt.start), +}: AppointmentListProps<T> & { + slotProps?: { + chip?: Omit<ChipProps, "variant" | "color">; + }; + getLabel?: (appointment: T) => string; +}) { const theme = useTheme(); const labelId = useId(); const hasAppointments = appointments.length > 0; @@ -88,6 +96,8 @@ export function AppointmentListForDate<T extends Appointment>({ }; } + const { sx: chipSx, ...otherChipProps } = slotProps?.chip ?? {}; + return ( <RadioGroup> <Divider sx={{ my: 2 }} /> @@ -118,7 +128,9 @@ export function AppointmentListForDate<T extends Appointment>({ minWidth: "56px", textAlign: "center", paddingX: 2, + ...chipSx, }} + {...otherChipProps} > <Radio disableIcon @@ -146,7 +158,7 @@ export function AppointmentListForDate<T extends Appointment>({ fontWeight: theme.fontWeight.md, }} > - {timeForm.format(apt.start)} + {getLabel(apt)} </Typography> } /> diff --git a/lib-portal/src/components/formFields/autocomplete/SingleAutocompleteField.tsx b/lib-portal/src/components/formFields/autocomplete/SingleAutocompleteField.tsx index e0e779418..d4527fb9f 100644 --- a/lib-portal/src/components/formFields/autocomplete/SingleAutocompleteField.tsx +++ b/lib-portal/src/components/formFields/autocomplete/SingleAutocompleteField.tsx @@ -20,6 +20,7 @@ export interface SingleAutocompleteFieldProps freeSolo?: boolean; disableFiltering?: boolean; popupIcon?: ReactNode; + disabled?: boolean; } export function SingleAutocompleteField(props: SingleAutocompleteFieldProps) { @@ -47,7 +48,8 @@ export function SingleAutocompleteField(props: SingleAutocompleteFieldProps) { <Autocomplete {...autocompleteProps} multiple={false} - freeSolo={props.freeSolo} + freeSolo={true} + forcePopupIcon={!props.freeSolo} value={field.input.value} onChange={(_, newValue) => { handleChange(newValue); @@ -55,7 +57,7 @@ export function SingleAutocompleteField(props: SingleAutocompleteFieldProps) { onInputChange={handleInputChange} options={props.options.map((opt) => opt.value)} filterOptions={props.disableFiltering ? identity() : undefined} - disabled={disabled} + disabled={disabled || props.disabled} popupIcon={props.popupIcon} /> </BaseField> diff --git a/lib-portal/src/components/navigation/InternalLinkIconButton.tsx b/lib-portal/src/components/navigation/InternalLinkIconButton.tsx index 6680c7556..2018b87c8 100644 --- a/lib-portal/src/components/navigation/InternalLinkIconButton.tsx +++ b/lib-portal/src/components/navigation/InternalLinkIconButton.tsx @@ -4,11 +4,13 @@ */ import { IconButton, IconButtonProps } from "@mui/joy"; +import { forwardRef } from "react"; import { NavigationLink } from "./NavigationLink"; -export function InternalLinkIconButton( - props: Omit<IconButtonProps<typeof NavigationLink>, "component">, -) { - return <IconButton component={NavigationLink} {...props} />; -} +export const InternalLinkIconButton = forwardRef< + HTMLAnchorElement, + Omit<IconButtonProps<typeof NavigationLink>, "component"> +>(function InternalLinkIconButton(props, ref) { + return <IconButton component={NavigationLink} ref={ref} {...props} />; +}); diff --git a/lib-portal/src/components/snackbar/SnackbarProvider.tsx b/lib-portal/src/components/snackbar/SnackbarProvider.tsx index 1989d5c4e..2de6bd553 100644 --- a/lib-portal/src/components/snackbar/SnackbarProvider.tsx +++ b/lib-portal/src/components/snackbar/SnackbarProvider.tsx @@ -149,10 +149,9 @@ function BaseSnackbar({ ); } -const SnackbarContext = createContext<{ - queue: SnackbarValues[]; - setQueue: Dispatch<SetStateAction<SnackbarValues[]>>; -}>(null!); +const SnackbarContext = createContext< + Dispatch<SetStateAction<SnackbarValues[]>> +>(null!); export function SnackbarProvider({ children, @@ -164,8 +163,9 @@ export function SnackbarProvider({ closeLabel?: string; }>) { const [queue, setQueue] = useState<SnackbarValues[]>([]); + return ( - <SnackbarContext.Provider value={{ queue, setQueue }}> + <SnackbarContext.Provider value={setQueue}> <> {queue.map((snackbarValues, index) => ( <BaseSnackbar @@ -204,11 +204,10 @@ export interface Snackbar { } export function useSnackbar(): Snackbar { - const context = useContext(SnackbarContext); - if (context === null) { + const setQueue = useContext(SnackbarContext); + if (setQueue === null) { throw new Error("useSnackbar was called outside SnackbarProvider"); } - const { setQueue } = context; return useMemo(() => { function enqueueSnackbar(snackbar: SnackbarPropsKeyOptional) { @@ -249,7 +248,10 @@ export function useSnackbar(): Snackbar { */ function close(key: string) { setQueue((prevQueue) => - removeSnackbarFromQueue({ queue: prevQueue, key }), + removeSnackbarFromQueue({ + queue: prevQueue, + key, + }), ); } diff --git a/lib-portal/src/helpers/validators.ts b/lib-portal/src/helpers/validators.ts index 68d9862e2..db941e08a 100644 --- a/lib-portal/src/helpers/validators.ts +++ b/lib-portal/src/helpers/validators.ts @@ -3,7 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { endOfDay, isFuture, isThisMonth, isToday } from "date-fns"; +import { + addYears, + endOfDay, + isAfter, + isBefore, + isFuture, + isThisMonth, + isToday, + parseISO, + startOfDay, + subYears, +} from "date-fns"; import { isDefined, isEmpty, isNullish } from "remeda"; import { OptionalFieldValue, Validator } from "../types/form"; @@ -116,6 +127,23 @@ export function validatePastMonthAndYear(year: number, month: number) { return undefined; } +export function validateDateOfBirth(value: string) { + const inputDate = parseISO(value); + + const today = new Date(); + const minDate = subYears(startOfDay(today), 150); + const maxDate = addYears(endOfDay(today), 1); + + if (isBefore(inputDate, minDate)) { + return "Das Geburtsdatum darf maximal 150 Jahre in der Vergangenheit liegen."; + } + if (isAfter(inputDate, maxDate)) { + return "Das Geburtsdatum darf maximal ein Jahr in der Zukunft liegen."; + } + + return undefined; +} + export function validateInteger(value: OptionalFieldValue<number>) { if (isEmptyString(value) || isInteger(value)) { return undefined; diff --git a/package.json b/package.json index 553c5709e..e2e819452 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,13 @@ "type": "module", "private": true, "devDependencies": { - "@cyclonedx/cdxgen": "11.0.10", + "@cyclonedx/cdxgen": "11.1.7", "@eslint/compat": "catalog:eslint", "@eslint/eslintrc": "catalog:eslint", "@eslint/js": "catalog:eslint", - "@pnpm/lockfile.fs": "1001.1.1", + "@pnpm/lockfile.fs": "1001.1.2", "@pnpm/meta-updater": "2.0.3", - "@pnpm/types": "1000.1.0", + "@pnpm/types": "1000.1.1", "@tanstack/eslint-plugin-query": "catalog:common", "@trivago/prettier-plugin-sort-imports": "catalog:prettier", "@types/node": "catalog:common", diff --git a/packages/auditlog-api/.gitignore b/packages/auditlog-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/auditlog-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/auditlog-api/build.gradle b/packages/auditlog-api/build.gradle new file mode 100644 index 000000000..e9288fc83 --- /dev/null +++ b/packages/auditlog-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('auditlog/openApi.json') +} diff --git a/citizen-portal-api/buildscript-gradle.lockfile b/packages/auditlog-api/buildscript-gradle.lockfile similarity index 100% rename from citizen-portal-api/buildscript-gradle.lockfile rename to packages/auditlog-api/buildscript-gradle.lockfile diff --git a/packages/auditlog-api/package.json b/packages/auditlog-api/package.json new file mode 100644 index 000000000..13bd42aee --- /dev/null +++ b/packages/auditlog-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/auditlog-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/auditlog-api/tsconfig.json b/packages/auditlog-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/auditlog-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/auditlog-api/tsup.config.ts b/packages/auditlog-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/auditlog-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/central-repository-api/.gitignore b/packages/central-repository-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/central-repository-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/central-repository-api/build.gradle b/packages/central-repository-api/build.gradle new file mode 100644 index 000000000..60720b3da --- /dev/null +++ b/packages/central-repository-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('central-repository/openApi.json') +} diff --git a/employee-portal-api/buildscript-gradle.lockfile b/packages/central-repository-api/buildscript-gradle.lockfile similarity index 100% rename from employee-portal-api/buildscript-gradle.lockfile rename to packages/central-repository-api/buildscript-gradle.lockfile diff --git a/packages/central-repository-api/package.json b/packages/central-repository-api/package.json new file mode 100644 index 000000000..fbf41b0fa --- /dev/null +++ b/packages/central-repository-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/central-repository-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/central-repository-api/tsconfig.json b/packages/central-repository-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/central-repository-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/central-repository-api/tsup.config.ts b/packages/central-repository-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/central-repository-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/chat-management-api/.gitignore b/packages/chat-management-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/chat-management-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/chat-management-api/build.gradle b/packages/chat-management-api/build.gradle new file mode 100644 index 000000000..d7bc319ff --- /dev/null +++ b/packages/chat-management-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('chat-management/openApi.json') +} diff --git a/packages/chat-management-api/buildscript-gradle.lockfile b/packages/chat-management-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/chat-management-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/chat-management-api/package.json b/packages/chat-management-api/package.json new file mode 100644 index 000000000..f15ab1371 --- /dev/null +++ b/packages/chat-management-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/chat-management-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/chat-management-api/tsconfig.json b/packages/chat-management-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/chat-management-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/chat-management-api/tsup.config.ts b/packages/chat-management-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/chat-management-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/dental/src/api/models/ChildExamination.ts b/packages/dental/src/api/models/ChildExamination.ts index ac886ed99..f7b4883eb 100644 --- a/packages/dental/src/api/models/ChildExamination.ts +++ b/packages/dental/src/api/models/ChildExamination.ts @@ -14,6 +14,8 @@ import { ExaminationStatus, mapToExaminationStatus } from "./ExaminationStatus"; export interface ChildExamination { readonly childId: string; + readonly examinationId: string; + readonly examinationVersion: number; readonly firstName: string; readonly lastName: string; readonly dateOfBirth: Date; @@ -30,6 +32,8 @@ export function mapChildExamination( ): ChildExamination { return { childId: response.childId, + examinationId: response.examinationId, + examinationVersion: response.examinationVersion, firstName: response.firstName, lastName: response.lastName, dateOfBirth: response.dateOfBirth, diff --git a/packages/dental/src/api/models/Examination.ts b/packages/dental/src/api/models/Examination.ts index 851155235..11345c2b4 100644 --- a/packages/dental/src/api/models/Examination.ts +++ b/packages/dental/src/api/models/Examination.ts @@ -4,27 +4,34 @@ */ import { ApiExamination, ApiProphylaxisType } from "@eshg/dental-api"; -import { BaseEntity } from "@eshg/lib-employee-portal/api/models/BaseEntity"; +import { + BaseEntity, + mapBaseEntity, +} from "@eshg/lib-employee-portal/api/models/BaseEntity"; +import { + Versioned, + mapVersioned, +} from "@eshg/lib-employee-portal/api/models/Versioned"; import { mapOptional } from "@eshg/lib-employee-portal/api/models/utils"; import { ExaminationResult, mapExaminationResult } from "./ExaminationResult"; import { ExaminationStatus, mapToExaminationStatus } from "./ExaminationStatus"; -export interface Examination extends BaseEntity { +export interface Examination extends BaseEntity, Versioned { readonly dateAndTime: Date; readonly prophylaxisType: ApiProphylaxisType; readonly screening: boolean; readonly fluoridation: boolean; - readonly fluoridationConsentGiven: boolean; + readonly fluoridationConsentGiven?: boolean; readonly note?: string; readonly result?: ExaminationResult; - readonly version: number; readonly status: ExaminationStatus; } export function mapExamination(response: ApiExamination): Examination { return { - ...response, + ...mapBaseEntity(response), + ...mapVersioned(response), dateAndTime: response.dateAndTime, prophylaxisType: response.prophylaxisType, screening: response.isScreening, diff --git a/packages/dental/src/api/models/ExaminationResult.ts b/packages/dental/src/api/models/ExaminationResult.ts index a93a629a4..97c7680e8 100644 --- a/packages/dental/src/api/models/ExaminationResult.ts +++ b/packages/dental/src/api/models/ExaminationResult.ts @@ -10,7 +10,11 @@ import { ApiOralHygieneStatus, ApiReasonForAbsence, ApiScreeningExaminationResult, + ApiTooth, } from "@eshg/dental-api"; +import { mapToObj } from "remeda"; + +import { ToothDiagnosis, mapToothDiagnosis } from "./ToothDiagnosis"; export type ExaminationResult = | FluoridationExaminationResult @@ -19,18 +23,21 @@ export type ExaminationResult = export interface FluoridationExaminationResult { readonly type: "fluoridation"; - readonly fluorideVarnishApplied: boolean; + readonly fluorideVarnishApplied?: boolean; } export interface ScreeningExaminationResult { readonly type: "screening"; readonly oralHygieneStatus?: ApiOralHygieneStatus; - readonly fluorideVarnishApplied: boolean; + readonly fluorideVarnishApplied?: boolean; + readonly toothDiagnoses: ToothDiagnoses; } +export type ToothDiagnoses = Partial<Record<ApiTooth, ToothDiagnosis>>; + export interface AbsenceExaminationResult { readonly type: "absence"; - readonly reasonForAbsence?: ApiReasonForAbsence; + readonly reasonForAbsence: ApiReasonForAbsence; } export function mapExaminationResult( @@ -62,6 +69,13 @@ function mapScreeningExaminationResult( type: "screening", oralHygieneStatus: response.oralHygieneStatus, fluorideVarnishApplied: response.fluorideVarnishApplied, + toothDiagnoses: mapToObj( + response.toothDiagnoses, + (toothDiagnosisResponse) => [ + toothDiagnosisResponse.tooth, + mapToothDiagnosis(toothDiagnosisResponse), + ], + ), }; } @@ -73,3 +87,67 @@ function mapAbsenceExaminationResult( reasonForAbsence: response.reasonForAbsence, }; } + +type FieldFunctionMap<T> = { + [K in keyof T]-?: (value: T[K]) => boolean; +}; + +const screeningResultEmptinessChecks: FieldFunctionMap<ScreeningExaminationResult> = + { + type: (value) => { + return value === "screening"; + }, + oralHygieneStatus: (value) => { + return value === undefined; + }, + fluorideVarnishApplied: (value) => { + return value === undefined; + }, + toothDiagnoses: (value) => { + return Object.keys(value).length === 0; + }, + }; + +const fluoridationResultEmptinessChecks: FieldFunctionMap<FluoridationExaminationResult> = + { + type: (value) => { + return value === "fluoridation"; + }, + fluorideVarnishApplied: (value) => { + return value === undefined; + }, + }; + +function isEmptyResult<T extends ExaminationResult>( + examinationResult: T, + emptinessChecks: { [K in keyof T]: (val: T[K]) => boolean }, +): boolean { + const keys = Object.keys(examinationResult) as (keyof T)[]; + return keys.every((key) => { + const isEmpty = emptinessChecks[key]; + const value = examinationResult[key]; + return isEmpty(value); + }); +} + +function isEmptyScreeningExaminationResult( + result: ScreeningExaminationResult, +): boolean { + return isEmptyResult(result, screeningResultEmptinessChecks); +} + +function isEmptyFluoridationExaminationResult( + result: FluoridationExaminationResult, +): boolean { + return isEmptyResult(result, fluoridationResultEmptinessChecks); +} + +export function isEmptyExaminationResult( + result: ScreeningExaminationResult | FluoridationExaminationResult, +): boolean { + if (result.type === "screening") { + return isEmptyScreeningExaminationResult(result); + } else { + return isEmptyFluoridationExaminationResult(result); + } +} diff --git a/packages/dental/src/api/models/ExaminationStatus.ts b/packages/dental/src/api/models/ExaminationStatus.ts index 4dadf8ecc..862b3c0e2 100644 --- a/packages/dental/src/api/models/ExaminationStatus.ts +++ b/packages/dental/src/api/models/ExaminationStatus.ts @@ -6,10 +6,13 @@ import { ApiExaminationResult } from "@eshg/dental-api"; import { isDefined } from "remeda"; -export type ExaminationStatus = "OPEN" | "CLOSED"; +export type ExaminationStatus = "OPEN" | "CLOSED" | "NOT_PRESENT"; export function mapToExaminationStatus( examinationResult: ApiExaminationResult | undefined, ): ExaminationStatus { + if (examinationResult?.type === "AbsenceExaminationResult") { + return "NOT_PRESENT"; + } return isDefined(examinationResult) ? "CLOSED" : "OPEN"; } diff --git a/packages/dental/src/api/models/ToothDiagnosis.ts b/packages/dental/src/api/models/ToothDiagnosis.ts new file mode 100644 index 000000000..420ce0b2a --- /dev/null +++ b/packages/dental/src/api/models/ToothDiagnosis.ts @@ -0,0 +1,27 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + ApiMainResult, + ApiSecondaryResult, + ApiTooth, + ApiToothDiagnosis, +} from "@eshg/dental-api"; + +export interface ToothDiagnosis { + readonly tooth: ApiTooth; + readonly mainResult: ApiMainResult; + readonly secondaryResult1?: ApiSecondaryResult; + readonly secondaryResult2?: ApiSecondaryResult; +} + +export function mapToothDiagnosis(response: ApiToothDiagnosis): ToothDiagnosis { + return { + tooth: response.tooth, + mainResult: response.mainResult, + secondaryResult1: response.secondaryResult1, + secondaryResult2: response.secondaryResult2, + }; +} diff --git a/packages/dental/src/api/mutations/prophylaxisSessionApi.ts b/packages/dental/src/api/mutations/prophylaxisSessionApi.ts index 67261f4f6..e78ba126f 100644 --- a/packages/dental/src/api/mutations/prophylaxisSessionApi.ts +++ b/packages/dental/src/api/mutations/prophylaxisSessionApi.ts @@ -6,7 +6,9 @@ import { ApiCreateProphylaxisSessionRequest, ApiProphylaxisSessionDetails, + ApiUpdateExaminationsInBulkRequest, ApiUpdateProphylaxisSessionParticipantsRequest, + ApiUpdateProphylaxisSessionRequest, } from "@eshg/dental-api"; import { useHandledMutation } from "@eshg/lib-portal/api/useHandledMutation"; import { useSnackbar } from "@eshg/lib-portal/components/snackbar/SnackbarProvider"; @@ -28,6 +30,21 @@ export function useCreateProphylaxisSession() { }); } +export function useUpdateProphylaxisSession(prophylaxisSessionId: string) { + const { prophylaxisSessionApi } = useDentalApi(); + const snackbar = useSnackbar(); + return useHandledMutation({ + mutationFn: (request: ApiUpdateProphylaxisSessionRequest) => + prophylaxisSessionApi.updateProphylaxisSession( + prophylaxisSessionId, + request, + ), + onSuccess: () => { + snackbar.confirmation("Prophylaxe erfolgreich gespeichert."); + }, + }); +} + export function useDeleteProphylaxisSessionParticipantOptions( prophylaxisSessionId: string, prophylaxisSessionVersion: number, @@ -79,3 +96,37 @@ export function useUpdateProphylaxisSessionParticipants( }, }); } + +interface UseUpdateProphylaxisSessionExaminationsOptions { + onSuccess: () => void; +} + +export function useUpdateProphylaxisSessionExaminations( + prophylaxisSessionId: string, + options?: UseUpdateProphylaxisSessionExaminationsOptions, +) { + const { prophylaxisSessionApi } = useDentalApi(); + const queryClient = useQueryClient(); + const { queryKey } = getProphylaxisSessionQuery(prophylaxisSessionApi, { + prophylaxisSessionId, + }); + const snackbar = useSnackbar(); + + return useHandledMutation({ + meta: { updatesQuery: queryKey }, + mutationFn: (examinationUpdates: ApiUpdateExaminationsInBulkRequest[]) => + prophylaxisSessionApi.updateProphylaxisSessionExaminations( + prophylaxisSessionId, + { examinationUpdates }, + ), + onSuccess: (response) => { + queryClient.setQueryData(queryKey, response); + snackbar.confirmation("Untersuchungen erfolgreich gespeichert."); + options?.onSuccess?.(); + }, + alertOptions: { + enableRetryAfterError: true, + closeable: true, + }, + }); +} diff --git a/packages/inspection-api/.gitignore b/packages/inspection-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/inspection-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/inspection-api/build.gradle b/packages/inspection-api/build.gradle new file mode 100644 index 000000000..42319de81 --- /dev/null +++ b/packages/inspection-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('inspection/openApi.json') +} diff --git a/packages/inspection-api/buildscript-gradle.lockfile b/packages/inspection-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/inspection-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/inspection-api/package.json b/packages/inspection-api/package.json new file mode 100644 index 000000000..a60cf433f --- /dev/null +++ b/packages/inspection-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/inspection-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/inspection-api/tsconfig.json b/packages/inspection-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/inspection-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/inspection-api/tsup.config.ts b/packages/inspection-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/inspection-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/lib-editor-api/.gitignore b/packages/lib-editor-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/lib-editor-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/lib-editor-api/build.gradle b/packages/lib-editor-api/build.gradle new file mode 100644 index 000000000..0eccd5262 --- /dev/null +++ b/packages/lib-editor-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('lib-editor/openApi.json') +} diff --git a/packages/lib-editor-api/buildscript-gradle.lockfile b/packages/lib-editor-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/lib-editor-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/lib-editor-api/package.json b/packages/lib-editor-api/package.json new file mode 100644 index 000000000..dae8aebf9 --- /dev/null +++ b/packages/lib-editor-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/lib-editor-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/lib-editor-api/tsconfig.json b/packages/lib-editor-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/lib-editor-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/lib-editor-api/tsup.config.ts b/packages/lib-editor-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/lib-editor-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/lib-employee-portal/gradleDependencies.json b/packages/lib-employee-portal/gradleDependencies.json index 5b6411646..b88ecd157 100644 --- a/packages/lib-employee-portal/gradleDependencies.json +++ b/packages/lib-employee-portal/gradleDependencies.json @@ -1,3 +1,3 @@ { - "dependencies": [":base-api", ":employee-portal-api", ":lib-portal"] + "dependencies": [":base-api", ":lib-portal"] } diff --git a/packages/lib-employee-portal/package.json b/packages/lib-employee-portal/package.json index cb2b05c6f..b09f3074a 100644 --- a/packages/lib-employee-portal/package.json +++ b/packages/lib-employee-portal/package.json @@ -13,7 +13,6 @@ }, "dependencies": { "@eshg/base-api": "workspace:*", - "@eshg/employee-portal-api": "workspace:*", "@eshg/lib-portal": "workspace:*", "@mui/icons-material": "catalog:joy", "@mui/joy": "catalog:joy", @@ -41,7 +40,7 @@ "eslint-plugin-promise": "catalog:eslint", "prettier": "catalog:prettier", "resolve-tspaths": "catalog:common", - "tsup": "8.3.5", + "tsup": "catalog:common", "typescript": "catalog:common", "typescript-eslint": "catalog:eslint", "vite-tsconfig-paths": "catalog:vitest", diff --git a/packages/lib-procedures-api/.gitignore b/packages/lib-procedures-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/lib-procedures-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/lib-procedures-api/build.gradle b/packages/lib-procedures-api/build.gradle new file mode 100644 index 000000000..d381d3e1f --- /dev/null +++ b/packages/lib-procedures-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('lib-procedures/openApi.json') +} diff --git a/packages/lib-procedures-api/buildscript-gradle.lockfile b/packages/lib-procedures-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/lib-procedures-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/lib-procedures-api/package.json b/packages/lib-procedures-api/package.json new file mode 100644 index 000000000..fbb2e9b99 --- /dev/null +++ b/packages/lib-procedures-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/lib-procedures-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/lib-procedures-api/tsconfig.json b/packages/lib-procedures-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/lib-procedures-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/lib-procedures-api/tsup.config.ts b/packages/lib-procedures-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/lib-procedures-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/lib-statistics-api/.gitignore b/packages/lib-statistics-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/lib-statistics-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/lib-statistics-api/build.gradle b/packages/lib-statistics-api/build.gradle new file mode 100644 index 000000000..00905a651 --- /dev/null +++ b/packages/lib-statistics-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('lib-statistics/openApi.json') +} diff --git a/packages/lib-statistics-api/buildscript-gradle.lockfile b/packages/lib-statistics-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/lib-statistics-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/lib-statistics-api/package.json b/packages/lib-statistics-api/package.json new file mode 100644 index 000000000..ab2185f29 --- /dev/null +++ b/packages/lib-statistics-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/lib-statistics-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/lib-statistics-api/tsconfig.json b/packages/lib-statistics-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/lib-statistics-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/lib-statistics-api/tsup.config.ts b/packages/lib-statistics-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/lib-statistics-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/medical-registry-api/.gitignore b/packages/medical-registry-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/medical-registry-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/medical-registry-api/build.gradle b/packages/medical-registry-api/build.gradle new file mode 100644 index 000000000..727bbb1b0 --- /dev/null +++ b/packages/medical-registry-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('medical-registry/openApi.json') +} diff --git a/packages/medical-registry-api/buildscript-gradle.lockfile b/packages/medical-registry-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/medical-registry-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/medical-registry-api/package.json b/packages/medical-registry-api/package.json new file mode 100644 index 000000000..0e31159b7 --- /dev/null +++ b/packages/medical-registry-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/medical-registry-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/medical-registry-api/tsconfig.json b/packages/medical-registry-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/medical-registry-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/medical-registry-api/tsup.config.ts b/packages/medical-registry-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/medical-registry-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/official-medical-service-api/.gitignore b/packages/official-medical-service-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/official-medical-service-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/official-medical-service-api/build.gradle b/packages/official-medical-service-api/build.gradle new file mode 100644 index 000000000..3b7a3f7ad --- /dev/null +++ b/packages/official-medical-service-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('official-medical-service/openApi.json') +} diff --git a/packages/official-medical-service-api/buildscript-gradle.lockfile b/packages/official-medical-service-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/official-medical-service-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/official-medical-service-api/package.json b/packages/official-medical-service-api/package.json new file mode 100644 index 000000000..068073663 --- /dev/null +++ b/packages/official-medical-service-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/official-medical-service-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/official-medical-service-api/tsconfig.json b/packages/official-medical-service-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/official-medical-service-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/official-medical-service-api/tsup.config.ts b/packages/official-medical-service-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/official-medical-service-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/opendata-api/.gitignore b/packages/opendata-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/opendata-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/opendata-api/build.gradle b/packages/opendata-api/build.gradle new file mode 100644 index 000000000..42fbbd3e7 --- /dev/null +++ b/packages/opendata-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('opendata/openApi.json') +} diff --git a/packages/opendata-api/buildscript-gradle.lockfile b/packages/opendata-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/opendata-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/opendata-api/package.json b/packages/opendata-api/package.json new file mode 100644 index 000000000..1bb376ef2 --- /dev/null +++ b/packages/opendata-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/opendata-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/opendata-api/tsconfig.json b/packages/opendata-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/opendata-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/opendata-api/tsup.config.ts b/packages/opendata-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/opendata-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/packages/sti-protection-api/.gitignore b/packages/sti-protection-api/.gitignore new file mode 100644 index 000000000..0edb02e46 --- /dev/null +++ b/packages/sti-protection-api/.gitignore @@ -0,0 +1,2 @@ +# Generated files +src diff --git a/packages/sti-protection-api/build.gradle b/packages/sti-protection-api/build.gradle new file mode 100644 index 000000000..53291e82b --- /dev/null +++ b/packages/sti-protection-api/build.gradle @@ -0,0 +1,9 @@ +plugins { + id 'api-package' +} + +def backendDir = rootProject.layout.projectDirectory.dir('backend') + +apiPackage { + inputSpec = backendDir.file('sti-protection/openApi.json') +} diff --git a/packages/sti-protection-api/buildscript-gradle.lockfile b/packages/sti-protection-api/buildscript-gradle.lockfile new file mode 100644 index 000000000..0d156738b --- /dev/null +++ b/packages/sti-protection-api/buildscript-gradle.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty=classpath diff --git a/packages/sti-protection-api/package.json b/packages/sti-protection-api/package.json new file mode 100644 index 000000000..b6c46b2bb --- /dev/null +++ b/packages/sti-protection-api/package.json @@ -0,0 +1,10 @@ +{ + "name": "@eshg/sti-protection-api", + "version": "0.0.1", + "type": "module", + "private": true, + "exports": { + "types": "./build/types/src/index.d.ts", + "import": "./build/lib/index.js" + } +} diff --git a/packages/sti-protection-api/tsconfig.json b/packages/sti-protection-api/tsconfig.json new file mode 100644 index 000000000..7fcfda752 --- /dev/null +++ b/packages/sti-protection-api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../config/tsconfig.lib.json" +} diff --git a/packages/sti-protection-api/tsup.config.ts b/packages/sti-protection-api/tsup.config.ts new file mode 100644 index 000000000..5b8cbfe04 --- /dev/null +++ b/packages/sti-protection-api/tsup.config.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 cronn GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineApiConfig } from "../../config/tsup.base"; + +export default defineApiConfig; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc22e853f..3439126d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,18 +9,21 @@ settings: catalogs: common: + '@keycloak/keycloak-admin-client': + specifier: 26.1.0 + version: 26.1.0 '@tanstack/eslint-plugin-query': - specifier: 5.62.16 - version: 5.62.16 + specifier: 5.66.0 + version: 5.66.0 '@tanstack/react-query': - specifier: 5.64.1 - version: 5.64.1 + specifier: 5.66.0 + version: 5.66.0 '@tanstack/react-table': specifier: 8.20.6 version: 8.20.6 '@types/node': - specifier: 22.10.5 - version: 22.10.5 + specifier: 22.13.0 + version: 22.13.0 date-fns: specifier: 4.1.0 version: 4.1.0 @@ -34,8 +37,8 @@ catalogs: specifier: 5.0.0 version: 5.0.0 remeda: - specifier: 2.19.0 - version: 2.19.0 + specifier: 2.20.1 + version: 2.20.1 resolve-tspaths: specifier: 0.8.23 version: 0.8.23 @@ -43,8 +46,8 @@ catalogs: specifier: 0.0.1 version: 0.0.1 tsup: - specifier: 8.3.5 - version: 8.3.5 + specifier: 8.3.6 + version: 8.3.6 typescript: specifier: 5.7.3 version: 5.7.3 @@ -62,20 +65,20 @@ catalogs: version: 5.0.3 eslint: '@eslint/compat': - specifier: 1.2.5 - version: 1.2.5 + specifier: 1.2.6 + version: 1.2.6 '@eslint/eslintrc': specifier: 3.2.0 version: 3.2.0 '@eslint/js': - specifier: 9.18.0 - version: 9.18.0 + specifier: 9.19.0 + version: 9.19.0 eslint: - specifier: 9.18.0 - version: 9.18.0 + specifier: 9.19.0 + version: 9.19.0 eslint-config-prettier: - specifier: 9.1.0 - version: 9.1.0 + specifier: 10.0.1 + version: 10.0.1 eslint-plugin-import: specifier: 2.31.0 version: 2.31.0 @@ -86,8 +89,8 @@ catalogs: specifier: 4.1.4 version: 4.1.4 typescript-eslint: - specifier: 8.19.1 - version: 8.19.1 + specifier: 8.22.0 + version: 8.22.0 fullcalendar: '@fullcalendar/core': specifier: 6.1.15 @@ -109,8 +112,8 @@ catalogs: version: 6.1.15 i18next: i18next: - specifier: 24.2.1 - version: 24.2.1 + specifier: 24.2.2 + version: 24.2.2 i18next-resources-to-backend: specifier: 1.2.1 version: 1.2.1 @@ -151,8 +154,8 @@ catalogs: version: 14.2.14 prettier: '@trivago/prettier-plugin-sort-imports': - specifier: 5.2.1 - version: 5.2.1 + specifier: 5.2.2 + version: 5.2.2 prettier: specifier: 3.4.2 version: 3.4.2 @@ -174,91 +177,91 @@ catalogs: specifier: 4.3.4 version: 4.3.4 '@vitest/coverage-istanbul': - specifier: 2.1.8 - version: 2.1.8 + specifier: 3.0.4 + version: 3.0.4 vite-tsconfig-paths: specifier: 5.1.4 version: 5.1.4 vitest: - specifier: 2.1.8 - version: 2.1.8 + specifier: 3.0.4 + version: 3.0.4 importers: .: devDependencies: '@cyclonedx/cdxgen': - specifier: 11.0.10 - version: 11.0.10 + specifier: 11.1.7 + version: 11.1.7 '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 '@eslint/js': specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 '@pnpm/lockfile.fs': - specifier: 1001.1.1 - version: 1001.1.1(@pnpm/logger@5.2.0) + specifier: 1001.1.2 + version: 1001.1.2(@pnpm/logger@5.2.0) '@pnpm/meta-updater': specifier: 2.0.3 version: 2.0.3 '@pnpm/types': - specifier: 1000.1.0 - version: 1000.1.0 + specifier: 1000.1.1 + version: 1000.1.1 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) prettier: specifier: catalog:prettier version: 3.4.2 tsup: specifier: catalog:common - version: 8.3.5(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) + version: 8.3.6(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) typescript: specifier: catalog:common version: 5.7.3 typescript-eslint: specifier: catalog:eslint - version: 8.19.1(eslint@9.18.0)(typescript@5.7.3) + version: 8.22.0(eslint@9.19.0)(typescript@5.7.3) vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) admin-portal: dependencies: @@ -288,7 +291,7 @@ importers: version: '@mui/joy@5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) '@tanstack/react-table': specifier: catalog:common version: 8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -300,13 +303,13 @@ importers: version: 2.4.6(react@18.3.1) i18next: specifier: catalog:i18next - version: 24.2.1(typescript@5.7.3) + version: 24.2.2(typescript@5.7.3) i18next-resources-to-backend: specifier: catalog:i18next version: 1.2.1 next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) pkijs: specifier: 3.2.4 version: 3.2.4 @@ -318,10 +321,10 @@ importers: version: 18.3.1(react@18.3.1) react-i18next: specifier: catalog:i18next - version: 15.4.0(i18next@24.2.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 use-debounce: specifier: catalog:common version: 10.0.4(react@18.3.1) @@ -331,7 +334,7 @@ importers: devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 @@ -340,13 +343,13 @@ importers: version: 14.2.14 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -355,28 +358,28 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -385,10 +388,10 @@ importers: version: 5.7.3 vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) citizen-portal: dependencies: @@ -401,15 +404,24 @@ importers: '@eshg/base-api': specifier: workspace:* version: link:../packages/base-api - '@eshg/citizen-portal-api': - specifier: workspace:* - version: link:../citizen-portal-api '@eshg/lib-portal': specifier: workspace:* version: link:../lib-portal + '@eshg/lib-procedures-api': + specifier: workspace:* + version: link:../packages/lib-procedures-api '@eshg/measles-protection-api': specifier: workspace:* version: link:../packages/measles-protection-api + '@eshg/medical-registry-api': + specifier: workspace:* + version: link:../packages/medical-registry-api + '@eshg/official-medical-service-api': + specifier: workspace:* + version: link:../packages/official-medical-service-api + '@eshg/opendata-api': + specifier: workspace:* + version: link:../packages/opendata-api '@eshg/school-entry-api': specifier: workspace:* version: link:../packages/school-entry-api @@ -445,7 +457,7 @@ importers: version: '@mui/joy@5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) date-fns: specifier: catalog:common version: 4.1.0 @@ -457,7 +469,7 @@ importers: version: 2.4.6(react@18.3.1) i18next: specifier: catalog:i18next - version: 24.2.1(typescript@5.7.3) + version: 24.2.2(typescript@5.7.3) i18next-resources-to-backend: specifier: catalog:i18next version: 1.2.1 @@ -466,7 +478,7 @@ importers: version: 1.0.0 next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: catalog:react version: 18.3.1 @@ -475,10 +487,10 @@ importers: version: 18.3.1(react@18.3.1) react-i18next: specifier: catalog:i18next - version: 15.4.0(i18next@24.2.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 server-only: specifier: catalog:common version: 0.0.1 @@ -491,7 +503,7 @@ importers: devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 @@ -500,10 +512,10 @@ importers: version: 14.2.14 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/mdx': specifier: 2.0.13 version: 2.0.13 @@ -512,7 +524,7 @@ importers: version: 0.6.3 '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -521,28 +533,28 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -551,36 +563,49 @@ importers: version: 5.7.3 vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) - - citizen-portal-api: {} + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) e2e: devDependencies: '@axe-core/playwright': specifier: 4.10.1 - version: 4.10.1(playwright-core@1.49.1) + version: 4.10.1(playwright-core@1.50.1) + '@eshg/auditlog-api': + specifier: workspace:* + version: link:../packages/auditlog-api '@eshg/base-api': specifier: workspace:* version: link:../packages/base-api - '@eshg/citizen-portal-api': + '@eshg/central-repository-api': + specifier: workspace:* + version: link:../packages/central-repository-api + '@eshg/chat-management-api': specifier: workspace:* - version: link:../citizen-portal-api + version: link:../packages/chat-management-api '@eshg/dental-api': specifier: workspace:* version: link:../packages/dental-api - '@eshg/e2e-api': + '@eshg/inspection-api': specifier: workspace:* - version: link:../e2e-api - '@eshg/employee-portal-api': + version: link:../packages/inspection-api + '@eshg/lib-procedures-api': specifier: workspace:* - version: link:../employee-portal-api + version: link:../packages/lib-procedures-api '@eshg/measles-protection-api': specifier: workspace:* version: link:../packages/measles-protection-api + '@eshg/medical-registry-api': + specifier: workspace:* + version: link:../packages/medical-registry-api + '@eshg/official-medical-service-api': + specifier: workspace:* + version: link:../packages/official-medical-service-api + '@eshg/opendata-api': + specifier: workspace:* + version: link:../packages/opendata-api '@eshg/school-entry-api': specifier: workspace:* version: link:../packages/school-entry-api @@ -590,27 +615,30 @@ importers: '@eshg/statistics-api': specifier: workspace:* version: link:../packages/statistics-api + '@eshg/sti-protection-api': + specifier: workspace:* + version: link:../packages/sti-protection-api '@eshg/travel-medicine-api': specifier: workspace:* version: link:../packages/travel-medicine-api '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 '@keycloak/keycloak-admin-client': - specifier: 26.0.7 - version: 26.0.7 + specifier: catalog:common + version: 26.1.0 '@playwright/test': - specifier: 1.49.1 - version: 1.49.1 + specifier: 1.50.1 + version: 1.50.1 '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 axe-core: specifier: 4.10.2 version: 4.10.2 @@ -622,25 +650,25 @@ importers: version: 4.1.0 eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 tsx: specifier: 4.19.2 version: 4.19.2 @@ -652,51 +680,76 @@ importers: version: 11.0.5 vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) - - e2e-api: {} + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) employee-portal: 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.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1) + version: 10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1) '@emotion/react': specifier: catalog:joy version: 11.14.0(@types/react@18.3.12)(react@18.3.1) '@emotion/styled': specifier: catalog:joy version: 11.14.0(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1) + '@eshg/auditlog-api': + specifier: workspace:* + version: link:../packages/auditlog-api '@eshg/base-api': specifier: workspace:* version: link:../packages/base-api + '@eshg/chat-management-api': + specifier: workspace:* + version: link:../packages/chat-management-api '@eshg/dental': specifier: workspace:* version: link:../packages/dental '@eshg/dental-api': specifier: workspace:* version: link:../packages/dental-api - '@eshg/employee-portal-api': + '@eshg/inspection-api': specifier: workspace:* - version: link:../employee-portal-api + version: link:../packages/inspection-api + '@eshg/lib-editor-api': + specifier: workspace:* + version: link:../packages/lib-editor-api '@eshg/lib-employee-portal': specifier: workspace:* version: link:../packages/lib-employee-portal '@eshg/lib-portal': specifier: workspace:* version: link:../lib-portal + '@eshg/lib-procedures-api': + specifier: workspace:* + version: link:../packages/lib-procedures-api + '@eshg/lib-statistics-api': + specifier: workspace:* + version: link:../packages/lib-statistics-api '@eshg/measles-protection-api': specifier: workspace:* version: link:../packages/measles-protection-api + '@eshg/medical-registry-api': + specifier: workspace:* + version: link:../packages/medical-registry-api + '@eshg/official-medical-service-api': + specifier: workspace:* + version: link:../packages/official-medical-service-api + '@eshg/opendata-api': + specifier: workspace:* + version: link:../packages/opendata-api '@eshg/school-entry-api': specifier: workspace:* version: link:../packages/school-entry-api '@eshg/statistics-api': specifier: workspace:* version: link:../packages/statistics-api + '@eshg/sti-protection-api': + specifier: workspace:* + version: link:../packages/sti-protection-api '@eshg/travel-medicine-api': specifier: workspace:* version: link:../packages/travel-medicine-api @@ -741,7 +794,7 @@ importers: version: '@mui/joy@5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) '@tanstack/react-table': specifier: catalog:common version: 8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -774,7 +827,7 @@ importers: version: 34.13.0 next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: catalog:react version: 18.3.1 @@ -789,7 +842,7 @@ importers: version: 5.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 server-only: specifier: catalog:common version: 0.0.1 @@ -817,7 +870,7 @@ importers: devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 @@ -826,16 +879,16 @@ importers: version: 14.2.14 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/mdx': specifier: 2.0.13 version: 2.0.13 '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -847,28 +900,28 @@ importers: version: 4.4.12(@types/react@18.3.12) '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -877,21 +930,19 @@ importers: version: 5.7.3 vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) - - employee-portal-api: {} + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) lib-portal: dependencies: '@eshg/base-api': specifier: workspace:* version: link:../packages/base-api - '@eshg/employee-portal-api': + '@eshg/medical-registry-api': specifier: workspace:* - version: link:../employee-portal-api + version: link:../packages/medical-registry-api '@mui/icons-material': specifier: catalog:joy version: 5.16.14(@mui/joy@5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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) @@ -903,7 +954,7 @@ importers: version: '@mui/joy@5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) date-fns: specifier: catalog:common version: 4.1.0 @@ -912,7 +963,7 @@ importers: version: 2.4.6(react@18.3.1) next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: catalog:react version: 18.3.1 @@ -921,7 +972,7 @@ importers: version: 18.3.1(react@18.3.1) remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 uuid: specifier: catalog:common version: 11.0.5 @@ -931,19 +982,19 @@ importers: devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -952,28 +1003,28 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -982,13 +1033,19 @@ importers: version: 5.7.3 vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) + + packages/auditlog-api: {} packages/base-api: {} + packages/central-repository-api: {} + + packages/chat-management-api: {} + packages/dental: dependencies: '@eshg/base-api': @@ -1011,35 +1068,35 @@ importers: version: 5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) formik: specifier: catalog:common version: 2.4.6(react@18.3.1) next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: catalog:react version: 18.3.1 remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -1048,28 +1105,28 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -1078,30 +1135,31 @@ importers: version: 0.8.23(typescript@5.7.3) tsup: specifier: catalog:common - version: 8.3.5(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) + version: 8.3.6(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) typescript: specifier: catalog:common version: 5.7.3 typescript-eslint: specifier: catalog:eslint - version: 8.19.1(eslint@9.18.0)(typescript@5.7.3) + version: 8.22.0(eslint@9.19.0)(typescript@5.7.3) vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) packages/dental-api: {} + packages/inspection-api: {} + + packages/lib-editor-api: {} + packages/lib-employee-portal: dependencies: '@eshg/base-api': specifier: workspace:* version: link:../base-api - '@eshg/employee-portal-api': - specifier: workspace:* - version: link:../../employee-portal-api '@eshg/lib-portal': specifier: workspace:* version: link:../../lib-portal @@ -1113,35 +1171,35 @@ importers: version: 5.0.0-beta.51(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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: catalog:common - version: 5.64.1(react@18.3.1) + version: 5.66.0(react@18.3.1) formik: specifier: catalog:common version: 2.4.6(react@18.3.1) next: specifier: catalog:next - version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: catalog:react version: 18.3.1 remeda: specifier: catalog:common - version: 2.19.0 + version: 2.20.1 devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 '@tanstack/eslint-plugin-query': specifier: catalog:common - version: 5.62.16(eslint@9.18.0)(typescript@5.7.3) + version: 5.66.0(eslint@9.19.0)(typescript@5.7.3) '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 '@types/react': specifier: catalog:react version: 18.3.12 @@ -1150,28 +1208,28 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: catalog:vitest - version: 4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) '@vitest/coverage-istanbul': specifier: catalog:vitest - version: 2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0)) + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0)) eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-next: specifier: catalog:next - version: 14.2.14(eslint@9.18.0)(typescript@5.7.3) + version: 14.2.14(eslint@9.19.0)(typescript@5.7.3) eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -1179,29 +1237,41 @@ importers: specifier: catalog:common version: 0.8.23(typescript@5.7.3) tsup: - specifier: 8.3.5 - version: 8.3.5(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) + specifier: catalog:common + version: 8.3.6(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3) typescript: specifier: catalog:common version: 5.7.3 typescript-eslint: specifier: catalog:eslint - version: 8.19.1(eslint@9.18.0)(typescript@5.7.3) + version: 8.22.0(eslint@9.19.0)(typescript@5.7.3) vite-tsconfig-paths: specifier: catalog:vitest - version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) + version: 5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) vitest: specifier: catalog:vitest - version: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) + + packages/lib-procedures-api: {} + + packages/lib-statistics-api: {} packages/measles-protection-api: {} + packages/medical-registry-api: {} + + packages/official-medical-service-api: {} + + packages/opendata-api: {} + packages/school-entry-api: {} packages/service-directory-api: {} packages/statistics-api: {} + packages/sti-protection-api: {} + packages/travel-medicine-api: {} performance-test: @@ -1209,33 +1279,39 @@ importers: '@eshg/base-api': specifier: workspace:* version: link:../packages/base-api - '@eshg/citizen-portal-api': + '@eshg/dental-api': specifier: workspace:* - version: link:../citizen-portal-api - '@eshg/employee-portal-api': + version: link:../packages/dental-api + '@eshg/inspection-api': specifier: workspace:* - version: link:../employee-portal-api + version: link:../packages/inspection-api '@eshg/measles-protection-api': specifier: workspace:* version: link:../packages/measles-protection-api + '@eshg/medical-registry-api': + specifier: workspace:* + version: link:../packages/medical-registry-api '@eshg/school-entry-api': specifier: workspace:* version: link:../packages/school-entry-api '@eshg/statistics-api': specifier: workspace:* version: link:../packages/statistics-api + '@eshg/sti-protection-api': + specifier: workspace:* + version: link:../packages/sti-protection-api '@eshg/travel-medicine-api': specifier: workspace:* version: link:../packages/travel-medicine-api '@faker-js/faker': - specifier: 9.3.0 - version: 9.3.0 + specifier: 9.4.0 + version: 9.4.0 '@grafana/schema': - specifier: 11.4.0 - version: 11.4.0 + specifier: 11.5.0 + version: 11.5.0 '@keycloak/keycloak-admin-client': - specifier: 26.0.7 - version: 26.0.7 + specifier: catalog:common + version: 26.1.0 '@types/k6': specifier: 0.54.2 version: 0.54.2 @@ -1251,7 +1327,7 @@ importers: devDependencies: '@eslint/compat': specifier: catalog:eslint - version: 1.2.5(eslint@9.18.0) + version: 1.2.6(eslint@9.19.0) '@eslint/eslintrc': specifier: catalog:eslint version: 3.2.0 @@ -1260,25 +1336,25 @@ importers: version: 2.7.0 '@trivago/prettier-plugin-sort-imports': specifier: catalog:prettier - version: 5.2.1(prettier@3.4.2) + version: 5.2.2(prettier@3.4.2) '@types/node': specifier: catalog:common - version: 22.10.5 + version: 22.13.0 eslint: specifier: catalog:eslint - version: 9.18.0 + version: 9.19.0 eslint-config-prettier: specifier: catalog:eslint - version: 9.1.0(eslint@9.18.0) + version: 10.0.1(eslint@9.19.0) eslint-plugin-import: specifier: catalog:eslint - version: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) eslint-plugin-promise: specifier: catalog:eslint - version: 7.2.1(eslint@9.18.0) + version: 7.2.1(eslint@9.19.0) eslint-plugin-unused-imports: specifier: catalog:eslint - version: 4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0) + version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0) prettier: specifier: catalog:prettier version: 3.4.2 @@ -1298,8 +1374,8 @@ packages: peerDependencies: ajv: '>=8' - '@appthreat/atom@2.0.25': - resolution: {integrity: sha512-JrqAtI7iDOixEDr4hqMQRjWujD6y8HQqI5LgsCX3QNyDuS2aSscYJUoHRRQKWmhjjaS77mFhH+aV8KfvAXVU5Q==} + '@appthreat/atom@2.1.11': + resolution: {integrity: sha512-ZqDQNw4Fi5eq+Yh+m9SA/KnXJnXMD8JS3VRU9KQIqQKSn7AnP95+hLfmgqb249SwCzpM5CBxHm1ToQ2H9wHMeA==} engines: {node: '>=16.0.0'} hasBin: true @@ -1316,30 +1392,14 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.7': - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.25.8': - resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.2': resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} engines: {node: '>=6.9.0'} - '@babel/core@7.25.8': - resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==} - engines: {node: '>=6.9.0'} - '@babel/core@7.26.0': resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.2': - resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.26.5': resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} engines: {node: '>=6.9.0'} @@ -1352,10 +1412,6 @@ packages: resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.7': - resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.9': resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} engines: {node: '>=6.9.0'} @@ -1397,12 +1453,6 @@ packages: resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.25.7': - resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.26.0': resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} @@ -1413,10 +1463,6 @@ packages: resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.24.7': - resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.25.9': resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} engines: {node: '>=6.9.0'} @@ -1433,10 +1479,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.25.7': resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} engines: {node: '>=6.9.0'} @@ -1449,30 +1491,14 @@ packages: resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} 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-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.7': - resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.7': - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.25.7': - resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} @@ -1481,26 +1507,12 @@ packages: resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.25.7': - resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} - engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.25.8': - resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.26.2': - resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.26.5': - resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + '@babel/parser@7.26.7': + resolution: {integrity: sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==} engines: {node: '>=6.0.0'} hasBin: true @@ -1952,24 +1964,12 @@ packages: resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.25.9': - resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.26.5': - resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.25.8': - resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==} + '@babel/traverse@7.26.7': + resolution: {integrity: sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.0': - resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.26.5': - resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} + '@babel/types@7.26.7': + resolution: {integrity: sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==} engines: {node: '>=6.9.0'} '@bufbuild/protobuf@1.7.2': @@ -2014,8 +2014,8 @@ packages: resolution: {integrity: sha512-WGXpmuokvkCblmvvPxH8tvk5bC/Jq2Zgy/RVXrYYt51uW9vtA++GmPeWpMewMfgg/jto9pJY+twnDS2l4t/bCQ==} cpu: [x64] - '@cyclonedx/cdxgen@11.0.10': - resolution: {integrity: sha512-ny+dTpwhTBdberISDBqdaZbo00BQ7izFR9XWMdEFrttz5RJruXvqaGM0xVETawCmKHXWKl+hlD+oKIV2jT4RVQ==} + '@cyclonedx/cdxgen@11.1.7': + resolution: {integrity: sha512-Eyb0v3z+7uhmcgItaXpLs+lGFGJJeY6V8wFRMj6ZYqKaDyQBXvpQieVhzQ5OAqK6mhQDa9RgA6jC20W2WEkwmQ==} engines: {node: '>=20'} hasBin: true @@ -2035,9 +2035,6 @@ packages: '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} - '@emotion/cache@11.13.5': - resolution: {integrity: sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==} - '@emotion/cache@11.14.0': resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} @@ -2531,8 +2528,8 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.2.5': - resolution: {integrity: sha512-5iuG/StT+7OfvhoBHPlmxkPA9om6aDUFgmD4+mWKAGsYt4vCe8rypneG03AuseyRHBmcCLXQtIH5S26tIoggLg==} + '@eslint/compat@1.2.6': + resolution: {integrity: sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.10.0 @@ -2552,8 +2549,8 @@ packages: resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.18.0': - resolution: {integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==} + '@eslint/js@9.19.0': + resolution: {integrity: sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.4': @@ -2564,8 +2561,8 @@ packages: resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@faker-js/faker@9.3.0': - resolution: {integrity: sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw==} + '@faker-js/faker@9.4.0': + resolution: {integrity: sha512-85+k0AxaZSTowL0gXp8zYWDIrWclTbRPg/pm/V0dSFZ6W6D4lhcG3uuZl4zLsEKfEvs69xDbLN2cHQudwp95JA==} engines: {node: '>=18.0.0', npm: '>=9.0.0'} '@floating-ui/core@1.6.2': @@ -2622,8 +2619,8 @@ packages: '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - '@grafana/schema@11.4.0': - resolution: {integrity: sha512-ZcUZtVdNSuBmTjwPAgWwQ/9/ilozAXggfXye797Odp3mXU9erQX7CqV9JV3y5pyj1+KMd+khzfjxEZotsjHWIA==} + '@grafana/schema@11.5.0': + resolution: {integrity: sha512-LLyrZeTEDvggzgG+4aKELRE3fY/GVOaU+L9qnBU1jlqZTKo3KTJ5fpUswRz2D7Gxt9b6viPIXJspMtLT5yr3bA==} '@gwhitney/detect-indent@7.0.1': resolution: {integrity: sha512-7bQW+gkKa2kKZPeJf6+c6gFK9ARxQfn+FKy9ScTBppyKRWH2KzsmweXUoklqeEiHiNVWaeP5csIdsNq6w7QhzA==} @@ -2714,8 +2711,8 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@keycloak/keycloak-admin-client@26.0.7': - resolution: {integrity: sha512-2vdx04sxnTQavnE7zuztyfmf1l5opG1Bi76hh3Wj02m6vR+aSiHFxlTXJFHVZtE9Emko+qgU7a5kEFC+Il+EwQ==} + '@keycloak/keycloak-admin-client@26.1.0': + resolution: {integrity: sha512-dOC64QbdYkAp8tv8rwdyerQMovV1cE58C/t8LeBGzvFYrJf+aCOA30qKXu8hNu7fRVvP8AWJ3u45X3lAZFhSYA==} engines: {node: '>=18'} '@matrix-org/matrix-sdk-crypto-wasm@9.1.0': @@ -2915,10 +2912,6 @@ packages: resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} - '@noble/hashes@1.6.0': resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==} engines: {node: ^14.21.3 || >=16} @@ -3017,8 +3010,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.49.1': - resolution: {integrity: sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==} + '@playwright/test@1.50.1': + resolution: {integrity: sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==} engines: {node: '>=18'} hasBin: true @@ -3064,8 +3057,8 @@ packages: resolution: {integrity: sha512-dxIXcW1F1dxIGfye2JXE7Q8WVwYB0axVzdBOkvE1WKIVR4xjB8e6k/Dkjo7DpbyfW5Vu2k21p6dyM32YLSAWoQ==} engines: {node: '>=18.12'} - '@pnpm/constants@1001.0.0': - resolution: {integrity: sha512-yg+S/e8goJoUC4CxyKAplT2fuI94G/nynshPCNkLsi69bUMim5iDMuiiiMo/LeMoOjuyfByealD3u1mqNXrl+A==} + '@pnpm/constants@1001.1.0': + resolution: {integrity: sha512-xb9dfSGi1qfUKY3r4Zy9JdC9+ZeaDxwfE7HrrGIEsBVY1hvIn6ntbR7A97z3nk44yX7vwbINNf9sizTp0WEtEw==} engines: {node: '>=18.12'} '@pnpm/constants@9.0.0': @@ -3108,16 +3101,16 @@ packages: peerDependencies: '@pnpm/logger': ^5.1.0 - '@pnpm/dependency-path@1000.0.1': - resolution: {integrity: sha512-gnOe/pRm3oyHxhuxgXNZX/vw71lcL73VeZHWKHHtNr5BmM92Q1QOLZHtxaRCNOM+heg0T073iScPuRAgVjqceg==} + '@pnpm/dependency-path@1000.0.2': + resolution: {integrity: sha512-bBwgKCKYxVtKdfrQi6gYe3IU6spJIRLKG/l2S7r8CGRTmqpGwjk7mpeQuk+IBlHMT4DxI7KomKSdIf8EE64j+w==} engines: {node: '>=18.12'} '@pnpm/env.system-node-version@1.0.1': resolution: {integrity: sha512-oRNq2uMkKye/8ujRAVbcW4qGL99E6bXP6BTjpN7o3IN1nBHuQ4uj/8g3PFJJMzw+80vH3QDMQUF41ABXJN0sRQ==} engines: {node: '>=18.12'} - '@pnpm/error@1000.0.1': - resolution: {integrity: sha512-FdDMjp9ORXK/JlxwjlNF7mRohVKx5pvM01bxsVUSLFLZ9/oMBtDceWlntUTLhvr2MwYcr69mGilTojnXEbsjQA==} + '@pnpm/error@1000.0.2': + resolution: {integrity: sha512-2SfE4FFL73rE1WVIoESbqlj4sLy5nWW4M/RVdHvCRJPjlQHa9MH7m7CVJM204lz6I+eHoB+E7rL3zmpJR5wYnQ==} engines: {node: '>=18.12'} '@pnpm/error@6.0.2': @@ -3156,26 +3149,26 @@ packages: resolution: {integrity: sha512-tiDFwNcqeFwXi+PIImZOaPxWc4fpS6msXTua+4vHQTit7xrRoUAJSq0cHzoAzRc1XaoHrlhl4D4nvCBU7rx4Aw==} engines: {node: '>=18.12'} - '@pnpm/lockfile.fs@1001.1.1': - resolution: {integrity: sha512-fw9/Sz7VplR1cw4EPXQvLA3z56kD2u0wtb87j2TzxNOr4OviRw2vK1Wq2RD8hPAdKhHZX6AtLMXTF23AOYh6mg==} + '@pnpm/lockfile.fs@1001.1.2': + resolution: {integrity: sha512-cInUJPvKpUYQ380mGJv9rBzKaQFM41e49uZhykMMR7a/9UPRm1OwcNoqn56zMiAUG1kQqZ4Um9QCwawNM4fBbA==} engines: {node: '>=18.12'} peerDependencies: '@pnpm/logger': '>=5.1.0 <1001.0.0' - '@pnpm/lockfile.merger@1001.0.1': - resolution: {integrity: sha512-wlTc1OgccPmpw8ZwazEyGz0x9KMX01pqavXYrclLoBH4qdR56JfZH1UwFIGs+UjdZBU97Jzidimrx3PmIiCkiw==} + '@pnpm/lockfile.merger@1001.0.2': + resolution: {integrity: sha512-qq11nCoKnOBhYNIaL2n6k6Jdl+Dd0XUtTrEzO0zF6T1hI/N/XkWKRl2zdwp/RaOOfSy+H8Cd9DUs8qrVW8JYjQ==} engines: {node: '>=18.12'} '@pnpm/lockfile.types@1.0.3': resolution: {integrity: sha512-A7vUWktnhDkrIs+WmXm7AdffJVyVYJpQUEouya/DYhB+Y+tQ3BXjZ6CV0KybqLgI/8AZErgCJqFxA0GJH6QDjA==} engines: {node: '>=18.12'} - '@pnpm/lockfile.types@1001.0.1': - resolution: {integrity: sha512-S2JSDaxN7NM+GEkuC3/5czI8JvuAZgIz4voWZxVXcCubYwRusbpaaGkuQw2dCMFaI9P9vGLPTk1QXTj7vR3POg==} + '@pnpm/lockfile.types@1001.0.2': + resolution: {integrity: sha512-drx2TRYPWriUQDvFHygJ/MuacQ7BRvGvptENNX2QXAsnHx8G5ZYnF21XDc6fCkPG6f83Rw1PcHOaUHObLZbEeA==} engines: {node: '>=18.12'} - '@pnpm/lockfile.utils@1001.0.1': - resolution: {integrity: sha512-fSBeni8HAwlaYNRUfQ1jbKA9dcYxFZOq+v0fp1JEDC3QaIe9W5R14LcBDQXy9UHLxaxl/+FvuvNnUl8/cTVMeg==} + '@pnpm/lockfile.utils@1001.0.2': + resolution: {integrity: sha512-yhtvSsS8kwEbY+g+IyBHiz61pcmdyqXIH8H2DV+/iSwZJf3llQztNYk8+xVgYdzsEFyvCuYicG/dlas2IHXegg==} engines: {node: '>=18.12'} '@pnpm/logger@5.2.0': @@ -3246,8 +3239,8 @@ packages: resolution: {integrity: sha512-+w/ZSaCEOToZtQXniZoQnxwjgs6bL8R/8S1UOl6jnXrPr70ZtgAORfZle/vXVdbqef3T63ZiX9F9uGOMCNTghA==} engines: {node: '>=18.12'} - '@pnpm/resolver-base@1000.1.1': - resolution: {integrity: sha512-bODmHt5wMgYVU9U8Js+EM1QAKxv2lD/EIa156Ilayf6YZZ4bOAQGedKJDOIltgzAM4fKKrGkiUKWdgOmQrQ+6g==} + '@pnpm/resolver-base@1000.1.2': + resolution: {integrity: sha512-kU8vG7Tt18nIwzIpu0oWejdzg5uFPKSh6mPN/wvVNQZO1T3zcQtgILs28fY8u7ONZPHKV4lcbbEFI/KDiGewFg==} engines: {node: '>=18.12'} '@pnpm/resolver-base@13.0.4': @@ -3262,8 +3255,8 @@ packages: resolution: {integrity: sha512-BSGvYd59kPKVTUk1InekEp+TiPnJ8650/bQyiOUFSvqHi61YipcR+E4H2i3xTnk2e+GHdGbXvEtAZbQmyxb0/g==} engines: {node: '>=18.12'} - '@pnpm/types@1000.1.0': - resolution: {integrity: sha512-+F58+ZHk9vJS4NKeVIzvPGCdU9tiPs7p1egqZRBiMN6Sc55LdDUEd/ArvyuooesY1hqmapUcDsgpU2krmNFMRQ==} + '@pnpm/types@1000.1.1': + resolution: {integrity: sha512-5zrv7B+4mW/6iiYS/BrFZWbMqLHjKmaBrMcw+F8LcuMQ7p4SQX4ANoycpr1VIQpn4+xQ0ckyuadfxBttpzicbg==} engines: {node: '>=18.12'} '@pnpm/types@11.1.0': @@ -3361,41 +3354,21 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.30.1': resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.30.1': resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.30.1': resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.30.1': resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} cpu: [x64] @@ -3411,41 +3384,21 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.30.1': resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.30.1': resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-musl@4.30.1': resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] @@ -3456,81 +3409,41 @@ packages: cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} - cpu: [riscv64] - os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.30.1': resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} - cpu: [s390x] - os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.30.1': resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.30.1': resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-musl@4.30.1': resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.30.1': resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.30.1': resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.30.1': resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} cpu: [x64] @@ -3586,16 +3499,16 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} - '@tanstack/eslint-plugin-query@5.62.16': - resolution: {integrity: sha512-VhnHSQ/hc62olLzGhlLJ4BJGWynwjs3cDMsByasKJ3zjW1YZ+6raxOv0gHHISm+VEnAY42pkMowmSWrXfL4NTw==} + '@tanstack/eslint-plugin-query@5.66.0': + resolution: {integrity: sha512-CzZhBxicLDuuSJbkZ4nPcuBqWnhLu72Zt9p/7qLQ93BepVnZJV6ZDlBLBuN5eg7YRACwECPLsntnwo1zuhgseQ==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - '@tanstack/query-core@5.64.1': - resolution: {integrity: sha512-978Wx4Wl4UJZbmvU/rkaM9cQtXXrbhK0lsz/UZhYIbyKYA8E4LdomTwyh2GHZ4oU0BKKoDH4YlKk2VscCUgNmg==} + '@tanstack/query-core@5.66.0': + resolution: {integrity: sha512-J+JeBtthiKxrpzUu7rfIPDzhscXF2p5zE/hVdrqkACBP8Yu0M96mwJ5m/8cPPYQE9aRNvXztXHlNwIh4FEeMZw==} - '@tanstack/react-query@5.64.1': - resolution: {integrity: sha512-vW5ggHpIO2Yjj44b4sB+Fd3cdnlMJppXRBJkEHvld6FXh3j5dwWJoQo7mGtKI2RbSFyiyu/PhGAy0+Vv5ev9Eg==} + '@tanstack/react-query@5.66.0': + resolution: {integrity: sha512-z3sYixFQJe8hndFnXgWu7C79ctL+pI0KAelYyW+khaNJ1m22lWrhJU2QrsTcRKMuVPtoZvfBYrTStIdKo+x0Xw==} peerDependencies: react: ^18 || ^19 @@ -3617,8 +3530,8 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@trivago/prettier-plugin-sort-imports@5.2.1': - resolution: {integrity: sha512-NDZndt0fmVThIx/8cExuJHLZagUVzfGCoVrwH9x6aZvwfBdkrDFTYujecek6X2WpG4uUFsVaPg5+aNQPSyjcmw==} + '@trivago/prettier-plugin-sort-imports@5.2.2': + resolution: {integrity: sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==} engines: {node: '>18.12'} peerDependencies: '@vue/compiler-sfc': 3.x @@ -3671,9 +3584,6 @@ packages: '@types/estree@0.0.39': resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -3713,8 +3623,8 @@ packages: '@types/negotiator@0.6.3': resolution: {integrity: sha512-JkXTOdKs5MF086b/pt8C3+yVp3iDUwG635L7oCH6HvJvvr6lSUU5oe/gLXnPEfYRROHjJIPgCV6cuAg8gGkntQ==} - '@types/node@22.10.5': - resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/node@22.13.0': + resolution: {integrity: sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3763,108 +3673,51 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.10.0': - resolution: {integrity: sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/eslint-plugin@8.19.1': - resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} + '@typescript-eslint/eslint-plugin@8.22.0': + resolution: {integrity: sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.10.0': - resolution: {integrity: sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@8.19.1': - resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} + '@typescript-eslint/parser@8.22.0': + resolution: {integrity: sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.10.0': - resolution: {integrity: sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.19.1': - resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.10.0': - resolution: {integrity: sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==} + '@typescript-eslint/scope-manager@8.22.0': + resolution: {integrity: sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/type-utils@8.19.1': - resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} + '@typescript-eslint/type-utils@8.22.0': + resolution: {integrity: sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.10.0': - resolution: {integrity: sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/types@8.19.1': - resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.10.0': - resolution: {integrity: sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==} + '@typescript-eslint/types@8.22.0': + resolution: {integrity: sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/typescript-estree@8.19.1': - resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} + '@typescript-eslint/typescript-estree@8.22.0': + resolution: {integrity: sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.10.0': - resolution: {integrity: sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - - '@typescript-eslint/utils@8.19.1': - resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} + '@typescript-eslint/utils@8.22.0': + resolution: {integrity: sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.10.0': - resolution: {integrity: sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.19.1': - resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} + '@typescript-eslint/visitor-keys@8.22.0': + resolution: {integrity: sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.2.0': @@ -3876,39 +3729,39 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 - '@vitest/coverage-istanbul@2.1.8': - resolution: {integrity: sha512-cSaCd8KcWWvgDwEJSXm0NEWZ1YTiJzjicKHy+zOEbUm0gjbbkz+qJf1p8q71uBzSlS7vdnZA8wRLeiwVE3fFTA==} + '@vitest/coverage-istanbul@3.0.4': + resolution: {integrity: sha512-a+SgPMom0PlRTuDasoucL2V7FDpS8j7p6jpHLNgt3d7oOSWYwtAFVCfZ3iQ+a+cOnh76g4mOftVR5Y9HokB/GQ==} peerDependencies: - vitest: 2.1.8 + vitest: 3.0.4 - '@vitest/expect@2.1.8': - resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + '@vitest/expect@3.0.4': + resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==} - '@vitest/mocker@2.1.8': - resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} + '@vitest/mocker@3.0.4': + resolution: {integrity: sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 + vite: ^5.0.0 || ^6.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@2.1.8': - resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + '@vitest/pretty-format@3.0.4': + resolution: {integrity: sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==} - '@vitest/runner@2.1.8': - resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + '@vitest/runner@3.0.4': + resolution: {integrity: sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==} - '@vitest/snapshot@2.1.8': - resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + '@vitest/snapshot@3.0.4': + resolution: {integrity: sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==} - '@vitest/spy@2.1.8': - resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + '@vitest/spy@3.0.4': + resolution: {integrity: sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==} - '@vitest/utils@2.1.8': - resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + '@vitest/utils@3.0.4': + resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==} '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -3995,11 +3848,6 @@ packages: resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} - acorn@8.13.0: - resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} @@ -4009,10 +3857,6 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} - engines: {node: '>= 14'} - agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} @@ -4655,15 +4499,6 @@ packages: supports-color: optional: true - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -4875,8 +4710,8 @@ packages: resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} engines: {node: '>= 0.4'} - es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} @@ -4942,8 +4777,8 @@ packages: typescript: optional: true - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@10.0.1: + resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -5044,8 +4879,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.18.0: - resolution: {integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==} + eslint@9.19.0: + resolution: {integrity: sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -5486,10 +5321,6 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} - https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -5504,8 +5335,8 @@ packages: i18next-resources-to-backend@1.2.1: resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} - i18next@24.2.1: - resolution: {integrity: sha512-Q2wC1TjWcSikn1VAJg13UGIjc+okpFxQTxjVAymOnSA3RpttBQNMPf2ovcgoFVsV4QNxTfNZMAxorXZXsk4fBA==} + i18next@24.2.2: + resolution: {integrity: sha512-NE6i86lBCKRYZa5TaUDkU5S4HFgLIEJRLr3Whf2psgaxBleQ2LC1YW1Vc+SCgkAW7VEzndT6al6+CzegSUHcTQ==} peerDependencies: typescript: ^5 peerDependenciesMeta: @@ -6037,8 +5868,8 @@ packages: magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.12: - resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} @@ -6722,8 +6553,8 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.2: + resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} @@ -6739,9 +6570,6 @@ packages: pg-connection-string@2.6.4: resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -6761,13 +6589,13 @@ packages: resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==} engines: {node: '>=12.0.0'} - playwright-core@1.49.1: - resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} + playwright-core@1.50.1: + resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==} engines: {node: '>=18'} hasBin: true - playwright@1.49.1: - resolution: {integrity: sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==} + playwright@1.50.1: + resolution: {integrity: sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==} engines: {node: '>=18'} hasBin: true @@ -7117,8 +6945,8 @@ packages: remark-rehype@11.1.1: resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==} - remeda@2.19.0: - resolution: {integrity: sha512-B/2+zHNPXu0BAopJU8ZrqMjA0u56M/l6BUxDq8AcU+3LWlOYVuf98I6qpYrB5BeeEICunpmXPcQl2ReGXkiQyw==} + remeda@2.20.1: + resolution: {integrity: sha512-gsEsSmjE0CHkNp6xEsWsU/6JVNWq7rqw+ZfzNMbVV4YFIPtTj/i0FfxurTRI6Z9sAnQufU9de2Cb3xHsUTFTMA==} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -7196,11 +7024,6 @@ packages: engines: {node: '>=10.0.0'} hasBin: true - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.30.1: resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -7256,6 +7079,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.0: + resolution: {integrity: sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==} + engines: {node: '>=10'} + hasBin: true + sequelize-pool@7.1.0: resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==} engines: {node: '>= 10.0.0'} @@ -7375,10 +7203,6 @@ packages: resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} engines: {node: '>= 10'} - socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} - engines: {node: '>= 14'} - socks-proxy-agent@8.0.5: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} @@ -7657,11 +7481,6 @@ packages: uglify-js: optional: true - terser@5.31.1: - resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} - engines: {node: '>=10'} - hasBin: true - terser@5.36.0: resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==} engines: {node: '>=10'} @@ -7696,29 +7515,25 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.1: - resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} tinyglobby@0.2.10: resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} engines: {node: '>=12.0.0'} - tinypool@1.0.1: - resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -7755,12 +7570,6 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - ts-api-utils@2.0.0: resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==} engines: {node: '>=18.12'} @@ -7786,14 +7595,11 @@ packages: tslib@2.3.0: resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - - tsup@8.3.5: - resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==} + tsup@8.3.6: + resolution: {integrity: sha512-XkVtlDV/58S9Ye0JxUUTcrQk4S+EqlOHKzg6Roa62rdjL1nGWNUstG0xgI4vanHdfIpjP448J8vlN0oK6XOJ5g==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -7851,14 +7657,14 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-fest@4.29.0: - resolution: {integrity: sha512-RPYt6dKyemXJe7I6oNstcH24myUGSReicxcHTvCLgzm4e0n8y05dGvcGB15/SoPRBmhlMthWQ9pvKyL81ko8nQ==} - engines: {node: '>=16'} - type-fest@4.32.0: resolution: {integrity: sha512-rfgpoi08xagF3JSdtJlCwMq9DGNDE0IMh3Mkpc1wUypg9vPi786AiqeBBKcqvIkq42azsBM85N490fyZjeUftw==} engines: {node: '>=16'} + type-fest@4.33.0: + resolution: {integrity: sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -7882,8 +7688,8 @@ packages: typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - typescript-eslint@8.19.1: - resolution: {integrity: sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==} + typescript-eslint@8.22.0: + resolution: {integrity: sha512-Y2rj210FW1Wb6TWXzQc5+P+EWI9/zdS57hLEc0gnyuvdzWo8+Y8brKlbj0muejonhMI/xAZCnZZwjbIfv1CkOw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -8029,10 +7835,6 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@11.0.3: - resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==} - hasBin: true - uuid@11.0.5: resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} hasBin: true @@ -8077,9 +7879,9 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@2.1.8: - resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} - engines: {node: ^18.0.0 || >=20.0.0} + vite-node@3.0.4: + resolution: {integrity: sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true vite-tsconfig-paths@5.1.4: @@ -8118,20 +7920,23 @@ packages: terser: optional: true - vitest@2.1.8: - resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@3.0.4: + resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.8 - '@vitest/ui': 2.1.8 + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.4 + '@vitest/ui': 3.0.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@types/debug': + optional: true '@types/node': optional: true '@vitest/browser': @@ -8432,9 +8237,9 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - '@appthreat/atom@2.0.25': + '@appthreat/atom@2.1.11': dependencies: - '@babel/parser': 7.26.5 + '@babel/parser': 7.26.7 typescript: 5.7.3 yargs: 17.7.2 optional: true @@ -8444,98 +8249,58 @@ snapshots: '@bufbuild/protobuf': 1.7.2 optional: true - '@axe-core/playwright@4.10.1(playwright-core@1.49.1)': + '@axe-core/playwright@4.10.1(playwright-core@1.50.1)': dependencies: axe-core: 4.10.2 - playwright-core: 1.49.1 + playwright-core: 1.50.1 '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 - picocolors: 1.1.0 - - '@babel/compat-data@7.24.7': {} - - '@babel/compat-data@7.25.8': {} + picocolors: 1.1.1 '@babel/compat-data@7.26.2': {} - '@babel/core@7.25.8': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8) - '@babel/helpers': 7.25.7 - '@babel/parser': 7.26.2 - '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 - 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.26.0': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 + '@babel/generator': 7.26.5 '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 convert-source-map: 2.0.0 - debug: 4.3.7 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.26.2': - dependencies: - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.0.2 - '@babel/generator@7.26.5': dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 '@babel/helper-annotate-as-pure@7.24.7': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.26.7 '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color - '@babel/helper-compilation-targets@7.25.7': - dependencies: - '@babel/compat-data': 7.25.8 - '@babel/helper-validator-option': 7.25.7 - browserslist: 4.24.0 - lru-cache: 5.1.1 - semver: 6.3.1 - '@babel/helper-compilation-targets@7.25.9': dependencies: '@babel/compat-data': 7.26.2 @@ -8544,21 +8309,6 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.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-class-features-plugin@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -8574,13 +8324,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-annotate-as-pure': 7.24.7 - regexpu-core: 5.3.2 - semver: 6.3.1 - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -8588,22 +8331,11 @@ snapshots: regexpu-core: 5.3.2 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-plugin-utils': 7.24.7 - debug: 4.4.0 - lodash.debounce: 4.0.8 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 debug: 4.4.0 lodash.debounce: 4.0.8 resolve: 1.22.8 @@ -8612,48 +8344,28 @@ snapshots: '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.26.7 '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.0 + '@babel/types': 7.26.7 '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.26.7 '@babel/helper-member-expression-to-functions@7.24.7': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-simple-access': 7.25.7 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.25.7(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-simple-access': 7.25.7 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color @@ -8662,27 +8374,16 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.25.9 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.24.7': dependencies: - '@babel/types': 7.26.0 - - '@babel/helper-plugin-utils@7.24.7': {} + '@babel/types': 7.26.7 '@babel/helper-plugin-utils@7.25.9': {} - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-remap-async-to-generator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -8692,15 +8393,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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 - transitivePeerDependencies: - - supports-color - '@babel/helper-replace-supers@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -8710,408 +8402,208 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-simple-access@7.24.7': - dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 - transitivePeerDependencies: - - supports-color - '@babel/helper-simple-access@7.25.7': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.24.7': dependencies: - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.26.0 - - '@babel/helper-string-parser@7.25.7': {} + '@babel/types': 7.26.7 '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-validator-identifier@7.25.7': {} - '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.24.7': {} - - '@babel/helper-validator-option@7.25.7': {} - '@babel/helper-validator-option@7.25.9': {} '@babel/helper-wrap-function@7.24.7': dependencies: '@babel/helper-function-name': 7.24.7 '@babel/template': 7.25.9 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color - '@babel/helpers@7.25.7': - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.0 - '@babel/helpers@7.26.0': dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.0 - - '@babel/parser@7.25.8': - dependencies: - '@babel/types': 7.26.0 - - '@babel/parser@7.26.2': - dependencies: - '@babel/types': 7.26.0 - - '@babel/parser@7.26.5': - dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.25.8)': + '@babel/parser@7.26.7': dependencies: - '@babel/core': 7.25.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/types': 7.26.7 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@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.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) - transitivePeerDependencies: - - supports-color + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@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.25.8)': - dependencies: - '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.26.0) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) + '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color @@ -9119,238 +8611,118 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-annotate-as-pure': 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.25.8) - '@babel/helper-split-export-declaration': 7.24.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-compilation-targets': 7.25.9 '@babel/helper-environment-visitor': 7.24.7 '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-replace-supers': 7.24.7(@babel/core@7.26.0) '@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.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/template': 7.25.9 - '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/template': 7.25.9 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.8) + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-exponentiation-operator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.8) - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-for-of@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-function-name@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-compilation-targets': 7.25.9 '@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.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8) + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8) + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.26.0) - '@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.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-hoist-variables': 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.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-simple-access': 7.25.7 transitivePeerDependencies: - supports-color @@ -9358,169 +8730,84 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-umd@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.25.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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-named-capturing-groups-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8) + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8) - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.8) - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.26.0) - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-replace-supers': 7.24.7(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8) - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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.25.8) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.8) + '@babel/helper-plugin-utils': 7.25.9 transitivePeerDependencies: - supports-color @@ -9529,20 +8816,15 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': dependencies: @@ -9554,224 +8836,75 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - regenerator-transform: 0.15.2 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 regenerator-transform: 0.15.2 - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-spread@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.25.8)': - dependencies: - '@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/helper-plugin-utils': 7.25.9 '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/preset-env@7.24.7(@babel/core@7.25.8)': - dependencies: - '@babel/compat-data': 7.24.7 - '@babel/core': 7.25.8 - '@babel/helper-compilation-targets': 7.25.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.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/helper-plugin-utils': 7.25.9 '@babel/preset-env@7.24.7(@babel/core@7.26.0)': dependencies: - '@babel/compat-data': 7.24.7 + '@babel/compat-data': 7.26.2 '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.26.0) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.26.0) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.26.0) @@ -9852,18 +8985,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.25.8)': - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/types': 7.26.0 - esutils: 2.0.3 - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/types': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/types': 7.26.7 esutils: 2.0.3 '@babel/regjsgen@0.8.0': {} @@ -9875,45 +9001,22 @@ snapshots: '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 - - '@babel/traverse@7.25.9': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.2 - '@babel/parser': 7.26.2 - '@babel/template': 7.25.9 - '@babel/types': 7.26.0 - debug: 4.3.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 - '@babel/traverse@7.26.5': + '@babel/traverse@7.26.7': dependencies: '@babel/code-frame': 7.26.2 '@babel/generator': 7.26.5 - '@babel/parser': 7.26.5 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/types': 7.26.5 - debug: 4.3.7 + '@babel/types': 7.26.7 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - - supports-color - - '@babel/types@7.25.8': - dependencies: - '@babel/helper-string-parser': 7.25.7 - '@babel/helper-validator-identifier': 7.25.7 - to-fast-properties: 2.0.0 - - '@babel/types@7.26.0': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + - supports-color - '@babel/types@7.26.5': + '@babel/types@7.26.7': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 @@ -9945,10 +9048,10 @@ snapshots: '@cyclonedx/cdxgen-plugins-bin@1.6.9': optional: true - '@cyclonedx/cdxgen@11.0.10': + '@cyclonedx/cdxgen@11.1.7': dependencies: - '@babel/parser': 7.26.5 - '@babel/traverse': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/traverse': 7.26.7 '@iarna/toml': 2.2.5 '@npmcli/arborist': 9.0.0 ajv: 8.17.1 @@ -9966,16 +9069,16 @@ snapshots: packageurl-js: 1.0.2 prettify-xml: 1.2.0 properties-reader: 2.3.0 - semver: 7.6.3 + semver: 7.7.0 ssri: 12.0.0 table: 6.9.0 tar: 7.4.3 - uuid: 11.0.3 + uuid: 11.0.5 validate-iri: 1.0.1 xml-js: 1.6.11 yargs: 17.7.2 optionalDependencies: - '@appthreat/atom': 2.0.25 + '@appthreat/atom': 2.1.11 '@appthreat/cdx-proto': 1.0.1 '@cyclonedx/cdxgen-plugins-bin': 1.6.9 '@cyclonedx/cdxgen-plugins-bin-arm': 1.6.9 @@ -10007,10 +9110,10 @@ snapshots: '@drauu/core@0.4.2': {} - '@ducanh2912/next-pwa@10.2.9(@types/babel__core@7.20.5)(next@14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.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.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(webpack@5.92.1)': dependencies: fast-glob: 3.3.2 - next: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) semver: 7.6.3 webpack: 5.92.1 workbox-build: 7.1.1(@types/babel__core@7.20.5) @@ -10037,14 +9140,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@emotion/cache@11.13.5': - dependencies: - '@emotion/memoize': 0.9.0 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - stylis: 4.2.0 - '@emotion/cache@11.14.0': dependencies: '@emotion/memoize': 0.9.0 @@ -10328,21 +9423,21 @@ snapshots: '@esbuild/win32-x64@0.24.2': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.18.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.19.0)': dependencies: - eslint: 9.18.0 + eslint: 9.19.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.5(eslint@9.18.0)': + '@eslint/compat@1.2.6(eslint@9.19.0)': optionalDependencies: - eslint: 9.18.0 + eslint: 9.19.0 '@eslint/config-array@0.19.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.7 + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -10354,7 +9449,7 @@ snapshots: '@eslint/eslintrc@3.2.0': dependencies: ajv: 6.12.6 - debug: 4.3.7 + debug: 4.4.0 espree: 10.3.0 globals: 14.0.0 ignore: 5.3.1 @@ -10365,7 +9460,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.18.0': {} + '@eslint/js@9.19.0': {} '@eslint/object-schema@2.1.4': {} @@ -10374,7 +9469,7 @@ snapshots: '@eslint/core': 0.10.0 levn: 0.4.1 - '@faker-js/faker@9.3.0': {} + '@faker-js/faker@9.4.0': {} '@floating-ui/core@1.6.2': dependencies: @@ -10427,9 +9522,9 @@ snapshots: '@gar/promisify@1.1.3': optional: true - '@grafana/schema@11.4.0': + '@grafana/schema@11.5.0': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@gwhitney/detect-indent@7.0.1': {} @@ -10524,7 +9619,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@keycloak/keycloak-admin-client@26.0.7': + '@keycloak/keycloak-admin-client@26.1.0': dependencies: camelize-ts: 3.0.0 url-join: 5.0.0 @@ -10646,7 +9741,7 @@ snapshots: '@mui/styled-engine@5.16.14(@emotion/react@11.14.0(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@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.5 + '@emotion/cache': 11.14.0 csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 @@ -10732,8 +9827,6 @@ snapshots: dependencies: '@noble/hashes': 1.6.0 - '@noble/hashes@1.4.0': {} - '@noble/hashes@1.6.0': {} '@nodelib/fs.scandir@2.1.5': @@ -10752,21 +9845,21 @@ snapshots: '@npmcli/agent@2.2.2': dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 lru-cache: 10.2.2 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color '@npmcli/agent@3.0.0': dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 lru-cache: 10.2.2 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -10802,7 +9895,7 @@ snapshots: promise-all-reject-late: 1.0.1 promise-call-limit: 3.0.1 read-package-json-fast: 4.0.0 - semver: 7.6.3 + semver: 7.7.0 ssri: 12.0.0 treeverse: 3.0.0 walk-up-path: 4.0.0 @@ -10813,16 +9906,16 @@ snapshots: '@npmcli/fs@1.1.1': dependencies: '@gar/promisify': 1.1.3 - semver: 7.6.3 + semver: 7.7.0 optional: true '@npmcli/fs@3.1.1': dependencies: - semver: 7.6.3 + semver: 7.7.0 '@npmcli/fs@4.0.0': dependencies: - semver: 7.6.3 + semver: 7.7.0 '@npmcli/git@6.0.1': dependencies: @@ -10833,7 +9926,7 @@ snapshots: proc-log: 5.0.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.6.3 + semver: 7.7.0 which: 5.0.0 transitivePeerDependencies: - bluebird @@ -10858,7 +9951,7 @@ snapshots: json-parse-even-better-errors: 4.0.0 pacote: 21.0.0 proc-log: 5.0.0 - semver: 7.6.3 + semver: 7.7.0 transitivePeerDependencies: - bluebird - supports-color @@ -10881,7 +9974,7 @@ snapshots: json-parse-even-better-errors: 4.0.0 normalize-package-data: 7.0.0 proc-log: 5.0.0 - semver: 7.6.3 + semver: 7.7.0 transitivePeerDependencies: - bluebird @@ -10910,9 +10003,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.49.1': + '@playwright/test@1.50.1': dependencies: - playwright: 1.49.1 + playwright: 1.50.1 '@pnpm/catalogs.config@0.1.1': dependencies: @@ -10982,7 +10075,7 @@ snapshots: '@pnpm/constants@10.0.0': {} - '@pnpm/constants@1001.0.0': {} + '@pnpm/constants@1001.1.0': {} '@pnpm/constants@9.0.0': {} @@ -11032,14 +10125,14 @@ snapshots: pretty-ms: 7.0.1 ramda: '@pnpm/ramda@0.28.1' rxjs: 7.8.1 - semver: 7.6.3 + semver: 7.7.0 stacktracey: 2.1.8 string-length: 4.0.2 - '@pnpm/dependency-path@1000.0.1': + '@pnpm/dependency-path@1000.0.2': dependencies: '@pnpm/crypto.hash': 1000.0.0 - '@pnpm/types': 1000.1.0 + '@pnpm/types': 1000.1.1 semver: 7.6.3 '@pnpm/env.system-node-version@1.0.1': @@ -11048,9 +10141,9 @@ snapshots: execa: safe-execa@0.1.2 mem: 8.1.1 - '@pnpm/error@1000.0.1': + '@pnpm/error@1000.0.2': dependencies: - '@pnpm/constants': 1001.0.0 + '@pnpm/constants': 1001.1.0 '@pnpm/error@6.0.2': dependencies: @@ -11096,17 +10189,17 @@ snapshots: '@pnpm/lockfile.types': 1.0.3 '@pnpm/types': 12.2.0 - '@pnpm/lockfile.fs@1001.1.1(@pnpm/logger@5.2.0)': + '@pnpm/lockfile.fs@1001.1.2(@pnpm/logger@5.2.0)': dependencies: - '@pnpm/constants': 1001.0.0 - '@pnpm/dependency-path': 1000.0.1 - '@pnpm/error': 1000.0.1 + '@pnpm/constants': 1001.1.0 + '@pnpm/dependency-path': 1000.0.2 + '@pnpm/error': 1000.0.2 '@pnpm/git-utils': 1000.0.0 - '@pnpm/lockfile.merger': 1001.0.1 - '@pnpm/lockfile.types': 1001.0.1 - '@pnpm/lockfile.utils': 1001.0.1 + '@pnpm/lockfile.merger': 1001.0.2 + '@pnpm/lockfile.types': 1001.0.2 + '@pnpm/lockfile.utils': 1001.0.2 '@pnpm/logger': 5.2.0 - '@pnpm/types': 1000.1.0 + '@pnpm/types': 1000.1.1 '@pnpm/util.lex-comparator': 3.0.0 '@zkochan/rimraf': 3.0.2 comver-to-semver: 1.0.0 @@ -11118,10 +10211,10 @@ snapshots: strip-bom: 4.0.0 write-file-atomic: 5.0.1 - '@pnpm/lockfile.merger@1001.0.1': + '@pnpm/lockfile.merger@1001.0.2': dependencies: - '@pnpm/lockfile.types': 1001.0.1 - '@pnpm/types': 1000.1.0 + '@pnpm/lockfile.types': 1001.0.2 + '@pnpm/types': 1000.1.1 comver-to-semver: 1.0.0 ramda: '@pnpm/ramda@0.28.1' semver: 7.6.3 @@ -11131,18 +10224,18 @@ snapshots: '@pnpm/patching.types': 1.0.0 '@pnpm/types': 12.2.0 - '@pnpm/lockfile.types@1001.0.1': + '@pnpm/lockfile.types@1001.0.2': dependencies: '@pnpm/patching.types': 1000.0.0 - '@pnpm/types': 1000.1.0 + '@pnpm/types': 1000.1.1 - '@pnpm/lockfile.utils@1001.0.1': + '@pnpm/lockfile.utils@1001.0.2': dependencies: - '@pnpm/dependency-path': 1000.0.1 - '@pnpm/lockfile.types': 1001.0.1 + '@pnpm/dependency-path': 1000.0.2 + '@pnpm/lockfile.types': 1001.0.2 '@pnpm/pick-fetcher': 1000.0.0 - '@pnpm/resolver-base': 1000.1.1 - '@pnpm/types': 1000.1.0 + '@pnpm/resolver-base': 1000.1.2 + '@pnpm/types': 1000.1.1 get-npm-tarball-url: 2.1.0 ramda: '@pnpm/ramda@0.28.1' @@ -11197,7 +10290,7 @@ snapshots: detect-libc: 2.0.3 execa: safe-execa@0.1.2 mem: 8.1.1 - semver: 7.6.3 + semver: 7.7.0 '@pnpm/parse-overrides@5.1.1': dependencies: @@ -11257,11 +10350,11 @@ snapshots: archy: 1.0.0 chalk: 4.1.2 cli-columns: 4.0.0 - semver: 7.6.3 + semver: 7.7.0 - '@pnpm/resolver-base@1000.1.1': + '@pnpm/resolver-base@1000.1.2': dependencies: - '@pnpm/types': 1000.1.0 + '@pnpm/types': 1000.1.1 '@pnpm/resolver-base@13.0.4': dependencies: @@ -11277,7 +10370,7 @@ snapshots: dependencies: strip-comments-strings: 1.2.0 - '@pnpm/types@1000.1.0': {} + '@pnpm/types@1000.1.1': {} '@pnpm/types@11.1.0': {} @@ -11334,17 +10427,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@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.25.8 - '@babel/helper-module-imports': 7.25.9 - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) - rollup: 2.79.1 - optionalDependencies: - '@types/babel__core': 7.20.5 - transitivePeerDependencies: - - supports-color - '@rollup/plugin-babel@5.3.1(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@2.79.1)': dependencies: '@babel/core': 7.26.0 @@ -11377,7 +10459,7 @@ snapshots: dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.1 + terser: 5.36.0 optionalDependencies: rollup: 2.79.1 @@ -11396,27 +10478,15 @@ snapshots: optionalDependencies: rollup: 2.79.1 - '@rollup/rollup-android-arm-eabi@4.18.0': - optional: true - '@rollup/rollup-android-arm-eabi@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.18.0': - optional: true - '@rollup/rollup-android-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.18.0': - optional: true - '@rollup/rollup-darwin-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.18.0': - optional: true - '@rollup/rollup-darwin-x64@4.30.1': optional: true @@ -11426,78 +10496,42 @@ snapshots: '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': - optional: true - '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': - optional: true - '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': - optional: true - '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': - optional: true - '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true @@ -11553,25 +10587,25 @@ snapshots: '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 - tslib: 2.6.3 + tslib: 2.8.1 '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 - '@tanstack/eslint-plugin-query@5.62.16(eslint@9.18.0)(typescript@5.7.3)': + '@tanstack/eslint-plugin-query@5.66.0(eslint@9.19.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - eslint: 9.18.0 + '@typescript-eslint/utils': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + eslint: 9.19.0 transitivePeerDependencies: - supports-color - typescript - '@tanstack/query-core@5.64.1': {} + '@tanstack/query-core@5.66.0': {} - '@tanstack/react-query@5.64.1(react@18.3.1)': + '@tanstack/react-query@5.66.0(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.64.1 + '@tanstack/query-core': 5.66.0 react: 18.3.1 '@tanstack/react-table@8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -11587,12 +10621,12 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@trivago/prettier-plugin-sort-imports@5.2.1(prettier@3.4.2)': + '@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.4.2)': dependencies: - '@babel/generator': 7.26.2 - '@babel/parser': 7.26.2 - '@babel/traverse': 7.25.9 - '@babel/types': 7.26.0 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 javascript-natural-sort: 0.7.1 lodash: 4.17.21 prettier: 3.4.2 @@ -11612,24 +10646,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.25.8 - '@babel/types': 7.25.8 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@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.26.0 + '@babel/types': 7.26.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.26.0 + '@babel/types': 7.26.7 '@types/debug@4.1.12': dependencies: @@ -11651,8 +10685,6 @@ snapshots: '@types/estree@0.0.39': {} - '@types/estree@1.0.5': {} - '@types/estree@1.0.6': {} '@types/events@3.0.3': {} @@ -11686,7 +10718,7 @@ snapshots: '@types/negotiator@0.6.3': {} - '@types/node@22.10.5': + '@types/node@22.13.0': dependencies: undici-types: 6.20.0 @@ -11715,7 +10747,7 @@ snapshots: '@types/ssri@7.1.5': dependencies: - '@types/node': 22.10.5 + '@types/node': 22.13.0 '@types/trusted-types@2.0.7': {} @@ -11730,54 +10762,18 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.10.5 - optional: true - - '@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.10.0 - '@typescript-eslint/type-utils': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.10.0 - eslint: 9.18.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.7.3) - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 - eslint: 9.18.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 2.0.0(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color + '@types/node': 22.13.0 optional: true - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 - eslint: 9.18.0 + '@typescript-eslint/parser': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.22.0 + '@typescript-eslint/type-utils': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.22.0 + eslint: 9.19.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -11786,88 +10782,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.10.0 - '@typescript-eslint/types': 8.10.0 - '@typescript-eslint/typescript-estree': 8.10.0(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.10.0 - debug: 4.3.7 - eslint: 9.18.0 - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/scope-manager': 8.22.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.22.0 debug: 4.4.0 - eslint: 9.18.0 + eslint: 9.19.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.10.0': - dependencies: - '@typescript-eslint/types': 8.10.0 - '@typescript-eslint/visitor-keys': 8.10.0 - - '@typescript-eslint/scope-manager@8.19.1': - dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 - - '@typescript-eslint/type-utils@8.10.0(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/scope-manager@8.22.0': dependencies: - '@typescript-eslint/typescript-estree': 8.10.0(typescript@5.7.3) - '@typescript-eslint/utils': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.7.3) - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - eslint - - supports-color + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 - '@typescript-eslint/type-utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.22.0(eslint@9.19.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.22.0(eslint@9.19.0)(typescript@5.7.3) debug: 4.4.0 - eslint: 9.18.0 + eslint: 9.19.0 ts-api-utils: 2.0.0(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.10.0': {} - - '@typescript-eslint/types@8.19.1': {} - - '@typescript-eslint/typescript-estree@8.10.0(typescript@5.7.3)': - dependencies: - '@typescript-eslint/types': 8.10.0 - '@typescript-eslint/visitor-keys': 8.10.0 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.7.3) - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types@8.22.0': {} - '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.22.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 - debug: 4.3.7 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 + debug: 4.4.0 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 @@ -11877,55 +10826,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.10.0(eslint@9.18.0)(typescript@5.7.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0) - '@typescript-eslint/scope-manager': 8.10.0 - '@typescript-eslint/types': 8.10.0 - '@typescript-eslint/typescript-estree': 8.10.0(typescript@5.7.3) - eslint: 9.18.0 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/utils@8.22.0(eslint@9.19.0)(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - eslint: 9.18.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0) + '@typescript-eslint/scope-manager': 8.22.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.7.3) + eslint: 9.19.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.10.0': - dependencies: - '@typescript-eslint/types': 8.10.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.19.1': + '@typescript-eslint/visitor-keys@8.22.0': dependencies: - '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/types': 8.22.0 eslint-visitor-keys: 4.2.0 '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.4(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0))': + '@vitejs/plugin-react@4.3.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.1(@types/node@22.10.5)(terser@5.36.0) + vite: 5.3.1(@types/node@22.13.0)(terser@5.36.0) transitivePeerDependencies: - supports-color - '@vitest/coverage-istanbul@2.1.8(vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0))': + '@vitest/coverage-istanbul@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0))': dependencies: '@istanbuljs/schema': 0.1.3 - debug: 4.3.7 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 @@ -11933,50 +10866,50 @@ snapshots: istanbul-reports: 3.1.7 magicast: 0.3.5 test-exclude: 7.0.1 - tinyrainbow: 1.2.0 - vitest: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + tinyrainbow: 2.0.0 + vitest: 3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0) transitivePeerDependencies: - supports-color - '@vitest/expect@2.1.8': + '@vitest/expect@3.0.4': dependencies: - '@vitest/spy': 2.1.8 - '@vitest/utils': 2.1.8 + '@vitest/spy': 3.0.4 + '@vitest/utils': 3.0.4 chai: 5.1.2 - tinyrainbow: 1.2.0 + tinyrainbow: 2.0.0 - '@vitest/mocker@2.1.8(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0))': + '@vitest/mocker@3.0.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0))': dependencies: - '@vitest/spy': 2.1.8 + '@vitest/spy': 3.0.4 estree-walker: 3.0.3 - magic-string: 0.30.12 + magic-string: 0.30.17 optionalDependencies: - vite: 5.3.1(@types/node@22.10.5)(terser@5.36.0) + vite: 5.3.1(@types/node@22.13.0)(terser@5.36.0) - '@vitest/pretty-format@2.1.8': + '@vitest/pretty-format@3.0.4': dependencies: - tinyrainbow: 1.2.0 + tinyrainbow: 2.0.0 - '@vitest/runner@2.1.8': + '@vitest/runner@3.0.4': dependencies: - '@vitest/utils': 2.1.8 - pathe: 1.1.2 + '@vitest/utils': 3.0.4 + pathe: 2.0.2 - '@vitest/snapshot@2.1.8': + '@vitest/snapshot@3.0.4': dependencies: - '@vitest/pretty-format': 2.1.8 - magic-string: 0.30.12 - pathe: 1.1.2 + '@vitest/pretty-format': 3.0.4 + magic-string: 0.30.17 + pathe: 2.0.2 - '@vitest/spy@2.1.8': + '@vitest/spy@3.0.4': dependencies: tinyspy: 3.0.2 - '@vitest/utils@2.1.8': + '@vitest/utils@3.0.4': dependencies: - '@vitest/pretty-format': 2.1.8 + '@vitest/pretty-format': 3.0.4 loupe: 3.1.2 - tinyrainbow: 1.2.0 + tinyrainbow: 2.0.0 '@webassemblyjs/ast@1.12.1': dependencies: @@ -12083,9 +11016,7 @@ snapshots: acorn-walk@8.3.3: dependencies: - acorn: 8.13.0 - - acorn@8.13.0: {} + acorn: 8.14.0 acorn@8.14.0: {} @@ -12096,12 +11027,6 @@ snapshots: - supports-color optional: true - agent-base@7.1.1: - dependencies: - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - agent-base@7.1.3: {} agentkeepalive@4.5.0: @@ -12264,7 +11189,7 @@ snapshots: dependencies: pvtsutils: 1.3.5 pvutils: 1.1.3 - tslib: 2.6.3 + tslib: 2.8.1 assertion-error@2.0.1: {} @@ -12272,7 +11197,7 @@ snapshots: ast-types@0.13.4: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 astral-regex@2.0.0: {} @@ -12303,32 +11228,15 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.8 - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.8): - dependencies: - '@babel/compat-data': 7.25.8 - '@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-corejs2@0.4.11(@babel/core@7.26.0): dependencies: - '@babel/compat-data': 7.25.8 + '@babel/compat-data': 7.26.2 '@babel/core': 7.26.0 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.25.8): - dependencies: - '@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-corejs3@0.10.4(@babel/core@7.26.0): dependencies: '@babel/core': 7.26.0 @@ -12337,13 +11245,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.25.8): - dependencies: - '@babel/core': 7.25.8 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.8) - transitivePeerDependencies: - - supports-color - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.26.0): dependencies: '@babel/core': 7.26.0 @@ -12485,7 +11386,7 @@ snapshots: builtins@5.1.0: dependencies: - semver: 7.6.3 + semver: 7.7.0 bundle-require@5.1.0(esbuild@0.24.2): dependencies: @@ -12855,10 +11756,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.7: - dependencies: - ms: 2.1.3 - debug@4.4.0: dependencies: ms: 2.1.3 @@ -13111,7 +12008,7 @@ snapshots: iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - es-module-lexer@1.5.4: {} + es-module-lexer@1.6.0: {} es-object-atoms@1.0.0: dependencies: @@ -13245,19 +12142,19 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@14.2.14(eslint@9.18.0)(typescript@5.7.3): + eslint-config-next@14.2.14(eslint@9.19.0)(typescript@5.7.3): dependencies: '@next/eslint-plugin-next': 14.2.14 '@rushstack/eslint-patch': 1.10.3 - '@typescript-eslint/eslint-plugin': 8.10.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - eslint: 9.18.0 + '@typescript-eslint/eslint-plugin': 8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + eslint: 9.19.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.18.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.18.0) - eslint-plugin-react: 7.34.2(eslint@9.18.0) - eslint-plugin-react-hooks: 4.6.2(eslint@9.18.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.19.0) + eslint-plugin-react: 7.34.2(eslint@9.19.0) + eslint-plugin-react-hooks: 4.6.2(eslint@9.19.0) optionalDependencies: typescript: 5.7.3 transitivePeerDependencies: @@ -13265,9 +12162,9 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@9.1.0(eslint@9.18.0): + eslint-config-prettier@10.0.1(eslint@9.19.0): dependencies: - eslint: 9.18.0 + eslint: 9.19.0 eslint-import-resolver-node@0.3.9: dependencies: @@ -13277,76 +12174,37 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.18.0): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.3.7 + debug: 4.4.0 enhanced-resolve: 5.17.1 - eslint: 9.18.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0) + eslint: 9.19.0 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.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.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.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.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.10.0(eslint@9.18.0)(typescript@5.7.3) - eslint: 9.18.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.18.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.18.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - eslint: 9.18.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.18.0 + '@typescript-eslint/parser': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + eslint: 9.19.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.18.0) - hasown: 2.0.2 - is-core-module: 2.15.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - string.prototype.trimend: 1.0.8 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.10.0(eslint@9.18.0)(typescript@5.7.3) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0) transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -13355,9 +12213,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.18.0 + eslint: 9.19.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.18.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -13369,13 +12227,13 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.22.0(eslint@9.19.0)(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.18.0): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.19.0): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -13385,7 +12243,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.18.0 + eslint: 9.19.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -13394,16 +12252,16 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 - eslint-plugin-promise@7.2.1(eslint@9.18.0): + eslint-plugin-promise@7.2.1(eslint@9.19.0): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0) - eslint: 9.18.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0) + eslint: 9.19.0 - eslint-plugin-react-hooks@4.6.2(eslint@9.18.0): + eslint-plugin-react-hooks@4.6.2(eslint@9.19.0): dependencies: - eslint: 9.18.0 + eslint: 9.19.0 - eslint-plugin-react@7.34.2(eslint@9.18.0): + eslint-plugin-react@7.34.2(eslint@9.19.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -13412,7 +12270,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 - eslint: 9.18.0 + eslint: 9.19.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -13425,17 +12283,11 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0): - dependencies: - eslint: 9.18.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.10.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) - - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0): + eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0): dependencies: - eslint: 9.18.0 + eslint: 9.19.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3) eslint-scope@5.1.1: dependencies: @@ -13451,14 +12303,14 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.18.0: + eslint@9.19.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.18.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.19.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.0 '@eslint/core': 0.10.0 '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.18.0 + '@eslint/js': 9.19.0 '@eslint/plugin-kit': 0.2.5 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 @@ -13468,7 +12320,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.3.7 + debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.2.0 eslint-visitor-keys: 4.2.0 @@ -13688,7 +12540,7 @@ snapshots: react: 18.3.1 react-fast-compare: 2.0.4 tiny-warning: 1.0.3 - tslib: 2.7.0 + tslib: 2.8.1 fs-constants@1.0.0: optional: true @@ -13849,7 +12701,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.6.3 + semver: 7.7.0 serialize-error: 7.0.1 globals@11.12.0: {} @@ -13879,7 +12731,7 @@ snapshots: lowercase-keys: 3.0.0 p-cancelable: 4.0.1 responselike: 3.0.0 - type-fest: 4.29.0 + type-fest: 4.32.0 graceful-fs@4.2.10: {} @@ -14039,13 +12891,6 @@ snapshots: - supports-color optional: true - https-proxy-agent@7.0.5: - dependencies: - agent-base: 7.1.1 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 @@ -14064,7 +12909,7 @@ snapshots: dependencies: '@babel/runtime': 7.25.6 - i18next@24.2.1(typescript@5.7.3): + i18next@24.2.2(typescript@5.7.3): dependencies: '@babel/runtime': 7.25.6 optionalDependencies: @@ -14292,10 +13137,10 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.3 + semver: 7.7.0 transitivePeerDependencies: - supports-color @@ -14308,7 +13153,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.7 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -14355,7 +13200,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.10.5 + '@types/node': 22.13.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -14522,19 +13367,19 @@ snapshots: dependencies: sourcemap-codec: 1.4.8 - magic-string@0.30.12: + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 magicast@0.3.5: dependencies: - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 source-map-js: 1.2.0 make-dir@4.0.0: dependencies: - semver: 7.6.3 + semver: 7.7.0 make-fetch-happen@13.0.1: dependencies: @@ -15128,7 +13973,7 @@ snapshots: netmask@2.0.2: {} - next@14.2.14(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.14(@babel/core@7.26.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.14 '@swc/helpers': 0.5.5 @@ -15149,14 +13994,14 @@ 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.49.1 + '@playwright/test': 1.50.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros node-abi@3.65.0: dependencies: - semver: 7.6.3 + semver: 7.7.0 optional: true node-addon-api@7.1.0: @@ -15173,7 +14018,7 @@ snapshots: make-fetch-happen: 13.0.1 nopt: 7.2.1 proc-log: 3.0.0 - semver: 7.6.3 + semver: 7.7.0 tar: 6.2.1 which: 4.0.0 transitivePeerDependencies: @@ -15188,7 +14033,7 @@ snapshots: nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.6.3 + semver: 7.7.0 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -15217,20 +14062,20 @@ snapshots: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.15.1 - semver: 7.6.3 + semver: 7.7.0 validate-npm-package-license: 3.0.4 normalize-package-data@4.0.1: dependencies: hosted-git-info: 5.2.1 is-core-module: 2.15.1 - semver: 7.6.3 + semver: 7.7.0 validate-npm-package-license: 3.0.4 normalize-package-data@7.0.0: dependencies: hosted-git-info: 8.0.0 - semver: 7.6.3 + semver: 7.7.0 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -15245,7 +14090,7 @@ snapshots: npm-install-checks@7.1.0: dependencies: - semver: 7.6.3 + semver: 7.7.0 npm-normalize-package-bin@4.0.0: {} @@ -15253,7 +14098,7 @@ snapshots: dependencies: hosted-git-info: 8.0.0 proc-log: 5.0.0 - semver: 7.6.3 + semver: 7.7.0 validate-npm-package-name: 6.0.0 npm-packlist@10.0.0: @@ -15265,7 +14110,7 @@ snapshots: npm-install-checks: 7.1.0 npm-normalize-package-bin: 4.0.0 npm-package-arg: 12.0.0 - semver: 7.6.3 + semver: 7.7.0 npm-registry-fetch@18.0.2: dependencies: @@ -15534,13 +14379,13 @@ snapshots: path-type@4.0.0: {} - pathe@1.1.2: {} + pathe@2.0.2: {} pathval@2.0.0: {} pdf-parse-new@1.3.9: dependencies: - debug: 4.3.7 + debug: 4.4.0 node-ensure: 0.0.0 transitivePeerDependencies: - supports-color @@ -15550,8 +14395,6 @@ snapshots: pg-connection-string@2.6.4: optional: true - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -15562,18 +14405,18 @@ snapshots: pkijs@3.2.4: dependencies: - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.6.0 asn1js: 3.0.5 bytestreamjs: 2.0.1 pvtsutils: 1.3.5 pvutils: 1.1.3 - tslib: 2.6.3 + tslib: 2.8.1 - playwright-core@1.49.1: {} + playwright-core@1.50.1: {} - playwright@1.49.1: + playwright@1.50.1: dependencies: - playwright-core: 1.49.1 + playwright-core: 1.50.1 optionalDependencies: fsevents: 2.3.2 @@ -15594,13 +14437,13 @@ snapshots: postcss@8.4.31: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.0 postcss@8.4.38: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.0 preact@10.12.1: {} @@ -15698,7 +14541,7 @@ snapshots: pvtsutils@1.3.5: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 pvutils@1.1.3: {} @@ -15754,11 +14597,11 @@ snapshots: react-fast-compare@2.0.4: {} - react-i18next@15.4.0(i18next@24.2.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-i18next@15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 html-parse-stringify: 3.0.1 - i18next: 24.2.1(typescript@5.7.3) + i18next: 24.2.2(typescript@5.7.3) react: 18.3.1 optionalDependencies: react-dom: 18.3.1(react@18.3.1) @@ -15953,9 +14796,9 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - remeda@2.19.0: + remeda@2.20.1: dependencies: - type-fest: 4.32.0 + type-fest: 4.33.0 require-directory@2.1.1: {} @@ -16025,28 +14868,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.18.0: - dependencies: - '@types/estree': 1.0.5 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 - fsevents: 2.3.3 - rollup@4.30.1: dependencies: '@types/estree': 1.0.6 @@ -16078,7 +14899,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 safe-array-concat@1.1.2: dependencies: @@ -16123,6 +14944,8 @@ snapshots: semver@7.6.3: {} + semver@7.7.0: {} + sequelize-pool@7.1.0: optional: true @@ -16130,7 +14953,7 @@ snapshots: dependencies: '@types/debug': 4.1.12 '@types/validator': 13.12.0 - debug: 4.3.7 + debug: 4.4.0 dottie: 2.0.6 inflection: 1.13.4 lodash: 4.17.21 @@ -16138,7 +14961,7 @@ snapshots: moment-timezone: 0.5.45 pg-connection-string: 2.6.4 retry-as-promised: 7.0.4 - semver: 7.6.3 + semver: 7.7.0 sequelize-pool: 7.1.0 toposort-class: 1.0.1 uuid: 8.3.2 @@ -16255,14 +15078,6 @@ snapshots: - supports-color optional: true - socks-proxy-agent@8.0.4: - dependencies: - agent-base: 7.1.1 - debug: 4.4.0 - socks: 2.8.3 - transitivePeerDependencies: - - supports-color - socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 @@ -16591,13 +15406,6 @@ snapshots: terser: 5.36.0 webpack: 5.92.1 - terser@5.31.1: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.0 - commander: 2.20.3 - source-map-support: 0.5.21 - terser@5.36.0: dependencies: '@jridgewell/source-map': 0.3.6 @@ -16633,21 +15441,19 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.1: {} + tinyexec@0.3.2: {} tinyglobby@0.2.10: dependencies: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 - tinypool@1.0.1: {} + tinypool@1.0.2: {} - tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} tinyspy@3.0.2: {} - to-fast-properties@2.0.0: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -16674,10 +15480,6 @@ snapshots: trough@2.2.0: {} - ts-api-utils@1.3.0(typescript@5.7.3): - dependencies: - typescript: 5.7.3 - ts-api-utils@2.0.0(typescript@5.7.3): dependencies: typescript: 5.7.3 @@ -16697,11 +15499,9 @@ snapshots: tslib@2.3.0: {} - tslib@2.6.3: {} - - tslib@2.7.0: {} + tslib@2.8.1: {} - tsup@8.3.5(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3): + tsup@8.3.6(postcss@8.4.38)(tsx@4.19.2)(typescript@5.7.3): dependencies: bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 @@ -16716,7 +15516,7 @@ snapshots: rollup: 4.30.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyexec: 0.3.1 + tinyexec: 0.3.2 tinyglobby: 0.2.10 tree-kill: 1.2.2 optionalDependencies: @@ -16764,10 +15564,10 @@ snapshots: type-fest@3.13.1: {} - type-fest@4.29.0: {} - type-fest@4.32.0: {} + type-fest@4.33.0: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -16810,12 +15610,12 @@ snapshots: dependencies: is-typedarray: 1.0.0 - typescript-eslint@8.19.1(eslint@9.18.0)(typescript@5.7.3): + typescript-eslint@8.22.0(eslint@9.19.0)(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - eslint: 9.18.0 + '@typescript-eslint/eslint-plugin': 8.22.0(@typescript-eslint/parser@8.22.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.22.0(eslint@9.19.0)(typescript@5.7.3) + eslint: 9.19.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -16931,7 +15731,7 @@ snapshots: dependencies: browserslist: 4.24.0 escalade: 3.2.0 - picocolors: 1.1.0 + picocolors: 1.1.1 uri-js@4.4.1: dependencies: @@ -16958,8 +15758,6 @@ snapshots: utils-merge@1.0.1: optional: true - uuid@11.0.3: {} - uuid@11.0.5: {} uuid@8.3.2: @@ -16998,13 +15796,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@2.1.8(@types/node@22.10.5)(terser@5.36.0): + vite-node@3.0.4(@types/node@22.13.0)(terser@5.36.0): dependencies: cac: 6.7.14 - debug: 4.3.7 - es-module-lexer: 1.5.4 - pathe: 1.1.2 - vite: 5.3.1(@types/node@22.10.5)(terser@5.36.0) + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.2 + vite: 5.3.1(@types/node@22.13.0)(terser@5.36.0) transitivePeerDependencies: - '@types/node' - less @@ -17015,51 +15813,52 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)): + vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)): dependencies: - debug: 4.3.7 + debug: 4.4.0 globrex: 0.1.2 tsconfck: 3.1.0(typescript@5.7.3) optionalDependencies: - vite: 5.3.1(@types/node@22.10.5)(terser@5.36.0) + vite: 5.3.1(@types/node@22.13.0)(terser@5.36.0) transitivePeerDependencies: - supports-color - typescript - vite@5.3.1(@types/node@22.10.5)(terser@5.36.0): + vite@5.3.1(@types/node@22.13.0)(terser@5.36.0): dependencies: esbuild: 0.21.5 postcss: 8.4.38 - rollup: 4.18.0 + rollup: 4.30.1 optionalDependencies: - '@types/node': 22.10.5 + '@types/node': 22.13.0 fsevents: 2.3.3 terser: 5.36.0 - vitest@2.1.8(@types/node@22.10.5)(terser@5.36.0): + vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.13.0)(terser@5.36.0): dependencies: - '@vitest/expect': 2.1.8 - '@vitest/mocker': 2.1.8(vite@5.3.1(@types/node@22.10.5)(terser@5.36.0)) - '@vitest/pretty-format': 2.1.8 - '@vitest/runner': 2.1.8 - '@vitest/snapshot': 2.1.8 - '@vitest/spy': 2.1.8 - '@vitest/utils': 2.1.8 + '@vitest/expect': 3.0.4 + '@vitest/mocker': 3.0.4(vite@5.3.1(@types/node@22.13.0)(terser@5.36.0)) + '@vitest/pretty-format': 3.0.4 + '@vitest/runner': 3.0.4 + '@vitest/snapshot': 3.0.4 + '@vitest/spy': 3.0.4 + '@vitest/utils': 3.0.4 chai: 5.1.2 - debug: 4.3.7 + debug: 4.4.0 expect-type: 1.1.0 - magic-string: 0.30.12 - pathe: 1.1.2 + magic-string: 0.30.17 + pathe: 2.0.2 std-env: 3.8.0 tinybench: 2.9.0 - tinyexec: 0.3.1 - tinypool: 1.0.1 - tinyrainbow: 1.2.0 - vite: 5.3.1(@types/node@22.10.5)(terser@5.36.0) - vite-node: 2.1.8(@types/node@22.10.5)(terser@5.36.0) + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 5.3.1(@types/node@22.13.0)(terser@5.36.0) + vite-node: 3.0.4(@types/node@22.13.0)(terser@5.36.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.10.5 + '@types/debug': 4.1.12 + '@types/node': 22.13.0 transitivePeerDependencies: - less - lightningcss @@ -17088,7 +15887,7 @@ snapshots: webpack-bundle-analyzer@4.10.1: dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.13.0 + acorn: 8.14.0 acorn-walk: 8.3.3 commander: 7.2.0 debounce: 1.2.1 @@ -17097,7 +15896,7 @@ snapshots: html-escaper: 2.0.2 is-plain-object: 5.0.0 opener: 1.5.2 - picocolors: 1.1.0 + picocolors: 1.1.1 sirv: 2.0.4 ws: 7.5.10 transitivePeerDependencies: @@ -17123,7 +15922,7 @@ snapshots: browserslist: 4.24.0 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1 - es-module-lexer: 1.5.4 + es-module-lexer: 1.6.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -17220,7 +16019,7 @@ snapshots: wkx@0.5.0: dependencies: - '@types/node': 22.10.5 + '@types/node': 22.13.0 optional: true word-wrap@1.2.5: {} @@ -17285,10 +16084,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.25.8 - '@babel/preset-env': 7.24.7(@babel/core@7.25.8) + '@babel/core': 7.26.0 + '@babel/preset-env': 7.24.7(@babel/core@7.26.0) '@babel/runtime': 7.25.6 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.8)(@types/babel__core@7.20.5)(rollup@2.79.1) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.26.0)(@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) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b7f933c5c..ded4df4f7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,12 +3,9 @@ packages: - "employee-portal" - - "employee-portal-api" - "citizen-portal" - - "citizen-portal-api" - "admin-portal" - "e2e" - - "e2e-api" - "lib-portal" - "performance-test" - "packages/*" @@ -36,7 +33,7 @@ catalogs: "@fontsource/source-code-pro": "5.1.1" i18next: - "i18next": "24.2.1" + "i18next": "24.2.2" "i18next-resources-to-backend": "1.2.1" "react-i18next": "15.4.0" @@ -49,41 +46,42 @@ catalogs: "@fullcalendar/timegrid": "6.1.15" eslint: - "eslint": "9.18.0" - "typescript-eslint": "8.19.1" - "@eslint/compat": "1.2.5" + "eslint": "9.19.0" + "typescript-eslint": "8.22.0" + "@eslint/compat": "1.2.6" "@eslint/eslintrc": "3.2.0" - "@eslint/js": "9.18.0" - "eslint-config-prettier": "9.1.0" + "@eslint/js": "9.19.0" + "eslint-config-prettier": "10.0.1" "eslint-plugin-import": "2.31.0" "eslint-plugin-unused-imports": "4.1.4" "eslint-plugin-promise": "7.2.1" prettier: "prettier": "3.4.2" - "@trivago/prettier-plugin-sort-imports": "5.2.1" + "@trivago/prettier-plugin-sort-imports": "5.2.2" vitest: - "vitest": "2.1.8" - "@vitest/coverage-istanbul": "2.1.8" + "vitest": "3.0.4" + "@vitest/coverage-istanbul": "3.0.4" "@vitejs/plugin-react": "4.3.4" "vite-tsconfig-paths": "5.1.4" common: "typescript": "5.7.3" - "@types/node": "22.10.5" - "@tanstack/react-query": "5.64.1" - "@tanstack/eslint-plugin-query": "5.62.16" + "@types/node": "22.13.0" + "@tanstack/react-query": "5.66.0" + "@tanstack/eslint-plugin-query": "5.66.0" "@tanstack/react-table": "8.20.6" "date-fns": "4.1.0" "formik": "2.4.6" "react-error-boundary": "5.0.0" - "remeda": "2.19.0" + "remeda": "2.20.1" "server-only": "0.0.1" "valibot": "0.42.1" "use-debounce": "10.0.4" "uuid": "11.0.5" "drauu": "0.4.2" - "tsup": "8.3.5" + "tsup": "8.3.6" "zustand": "5.0.3" "resolve-tspaths": "0.8.23" + "@keycloak/keycloak-admin-client": "26.1.0" diff --git a/reverse-proxy/citizen-portal.conf b/reverse-proxy/citizen-portal.conf index 84cf08732..45feb0aef 100644 --- a/reverse-proxy/citizen-portal.conf +++ b/reverse-proxy/citizen-portal.conf @@ -1,3 +1,4 @@ + server { listen 4001; server_name reverse-proxy; @@ -183,7 +184,7 @@ server { proxy_pass http://host.docker.internal:3001; } - location ~ ^/((en|de)/)?(einschulungsuntersuchung/.+|(?:unternehmen/)?mein-bereich/.+|impfberatung/meine-termine(?:/.+)?)$ { + location ~ ^/((en|de)/)?(einschulungsuntersuchung/.+|(?:unternehmen/)?mein-bereich/.+|impfberatung/meine-termine(?:/.+)?|sexuellegesundheit/.+/termin)$ { include auth_request.conf; auth_request_set $resolved_location $upstream_http_location; diff --git a/settings.gradle b/settings.gradle index 750d6046d..3be9a4500 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,12 +3,9 @@ rootProject.name = 'ga-lotse-code' includeBuild 'backend' include 'employee-portal' -include 'employee-portal-api' include 'citizen-portal' -include 'citizen-portal-api' include 'admin-portal' include 'e2e' -include 'e2e-api' include 'lib-portal' include 'performance-test' include 'k8s' diff --git a/vitest.config.ts b/vitest.config.ts index 9d7c94863..45bde8e4a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -11,8 +11,15 @@ import { VITEST_COVERAGE_EXCLUDES, VITEST_OUT_DIR } from "./config/vitest.base"; // https://vitejs.dev/config/ export default defineConfig({ test: { + workspace: [ + "employee-portal", + "citizen-portal", + "admin-portal", + "lib-portal", + "packages/*", + ], environment: "node", - reporters: ["basic", "junit"], + reporters: ["default", "junit"], outputFile: { junit: `${VITEST_OUT_DIR}/junit.xml`, }, @@ -23,9 +30,11 @@ export default defineConfig({ reporter: ["text", "html", "cobertura"], include: ["**/src/**/*"], exclude: [ - "*-api", + "packages/*-api", "e2e", "performance-test", + "**/build", + "**/.next", ...VITEST_COVERAGE_EXCLUDES, ], }, diff --git a/vitest.workspace.ts b/vitest.workspace.ts deleted file mode 100644 index 0654eca6e..000000000 --- a/vitest.workspace.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright 2025 cronn GmbH - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { defineWorkspace } from "vitest/config"; - -export default defineWorkspace([ - "employee-portal", - "citizen-portal", - "admin-portal", - "lib-portal", -]); -- GitLab