From cdc437466ab73fe37df842d8b8c458284f087d95 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Wed, 17 Dec 2025 13:15:25 +0100 Subject: [PATCH 01/12] feat: rework vigilancearea periods. Todo frontend --- .../cacem/monitorenv/config/SecurityConfig.kt | 1 - .../EditableBriefVigilanceAreaPeriodEntity.kt | 9 + .../vigilanceArea/VigilanceAreaEntity.kt | 9 +- .../VigilanceAreaPeriodEntity.kt | 16 ++ .../ArchiveOutdatedVigilanceAreas.kt | 3 +- .../CreateOrUpdateVigilanceArea.kt | 8 +- .../vigilance_area/VigilanceAreaValidator.kt | 82 +++---- .../vigilanceArea/VigilanceAreaDataInput.kt | 20 +- .../VigilanceAreaDataPeriodInput.kt | 32 +++ .../vigilanceArea/VigilanceAreaDataOutput.kt | 20 +- .../VigilanceAreaPeriodDataOutput.kt | 34 +++ .../vigilanceArea/VigilanceAreasDataOutput.kt | 20 +- .../database/model/VigilanceAreaModel.kt | 38 +--- .../model/VigilanceAreaPeriodModel.kt | 94 ++++++++ .../JpaVigilanceAreaRepository.kt | 22 +- .../IDBVigilanceAreaPeriodRepository.kt | 7 + .../V0.200__create_vigilance_area_period.sql | 38 ++++ .../V666.16__insert_dummy_vigilance_area.sql | 205 ++++++++++-------- .../CreateOrUpdateVigilanceAreaUTests.kt | 64 +----- .../fixtures/VigilanceAreaFixture.kt | 41 ++-- .../fixtures/VigilanceAreaPeriodFixture.kt | 31 +++ .../endpoints/bff/v1/VigilanceAreasITests.kt | 155 +++++++------ .../JpaVigilanceAreaRepositoryITests.kt | 119 ++++++---- 23 files changed, 633 insertions(+), 435 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaPeriodEntity.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataPeriodInput.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaPeriodDataOutput.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaPeriodModel.kt create mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBVigilanceAreaPeriodRepository.kt create mode 100644 backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql create mode 100644 backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaPeriodFixture.kt diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/SecurityConfig.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/SecurityConfig.kt index b4302eddc1..5665679513 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/SecurityConfig.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/SecurityConfig.kt @@ -133,7 +133,6 @@ class SecurityConfig( } private fun validateAndProcessUser(oidcUser: OidcUser): OidcUser { - if (oidcProperties.bypassEmailDomainsFilter == "true") { logger.info("✅ OIDC is bypassing email domain checks.") return oidcUser diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt new file mode 100644 index 0000000000..18399a43f2 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt @@ -0,0 +1,9 @@ +package fr.gouv.cacem.monitorenv.domain.entities.dashboard + +import java.time.ZonedDateTime + +data class EditableBriefVigilanceAreaPeriodEntity( + val endDatePeriod: ZonedDateTime?, + val endingOccurenceDate: String, + val frequency: String, +) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt index 1bdf951de6..ad2c0a8d3f 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt @@ -8,16 +8,9 @@ import java.time.ZonedDateTime data class VigilanceAreaEntity( val id: Int? = null, val comments: String? = null, - val computedEndDate: ZonedDateTime? = null, val createdBy: String? = null, - val endDatePeriod: ZonedDateTime? = null, - val endingCondition: EndingConditionEnum? = null, - val endingOccurrenceDate: ZonedDateTime? = null, - val endingOccurrencesNumber: Int? = null, - val frequency: FrequencyEnum? = null, val geom: MultiPolygon? = null, val images: List? = listOf(), - val isAtAllTimes: Boolean, val isArchived: Boolean, val isDeleted: Boolean, val isDraft: Boolean, @@ -27,9 +20,9 @@ data class VigilanceAreaEntity( val name: String, val seaFront: String? = null, val sources: List, - val startDatePeriod: ZonedDateTime? = null, val themes: List, val tags: List, + val periods: List, val visibility: VisibilityEnum? = null, val createdAt: ZonedDateTime?, val updatedAt: ZonedDateTime?, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaPeriodEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaPeriodEntity.kt new file mode 100644 index 0000000000..7b8c40a151 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaPeriodEntity.kt @@ -0,0 +1,16 @@ +package fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea + +import java.time.ZonedDateTime +import java.util.UUID + +data class VigilanceAreaPeriodEntity( + val id: UUID?, + val computedEndDate: ZonedDateTime?, + val endDatePeriod: ZonedDateTime?, + val endingCondition: EndingConditionEnum?, + val endingOccurrenceDate: ZonedDateTime?, + val endingOccurrencesNumber: Int?, + val frequency: FrequencyEnum?, + val isAtAllTimes: Boolean, + val startDatePeriod: ZonedDateTime?, +) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt index 67b70ff335..198d6c684f 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt @@ -4,7 +4,6 @@ import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.repositories.IVigilanceAreaRepository import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.springframework.scheduling.annotation.Scheduled @UseCase class ArchiveOutdatedVigilanceAreas( @@ -13,7 +12,7 @@ class ArchiveOutdatedVigilanceAreas( private val logger: Logger = LoggerFactory.getLogger(ArchiveOutdatedVigilanceAreas::class.java) // At every 6 hours, after 1 minute of initial delay - @Scheduled(fixedDelay = 21600000, initialDelay = 6000) +// @Scheduled(fixedDelay = 21600000, initialDelay = 6000) fun execute() { logger.info("Attempt to ARCHIVE vigilance areas") val numberOfArchivedVigilanceAreas = vigilanceAreaRepository.archiveOutdatedVigilanceAreas() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceArea.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceArea.kt index f29e455962..e6f654e7b4 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceArea.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceArea.kt @@ -10,7 +10,6 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.dashboard.SaveDashboard import fr.gouv.cacem.monitorenv.domain.validators.UseCaseValidation import fr.gouv.cacem.monitorenv.domain.validators.vigilance_area.VigilanceAreaValidator import org.slf4j.LoggerFactory -import java.time.ZonedDateTime @UseCase class CreateOrUpdateVigilanceArea( @@ -29,12 +28,7 @@ class CreateOrUpdateVigilanceArea( vigilanceArea.geom?.let { nonNullGeom -> facadeAreasRepository.findFacadeFromGeometry(nonNullGeom) } - var vigilanceAreaToSave = vigilanceArea.copy(seaFront = seaFront) - if (vigilanceArea.isArchived && - (vigilanceArea.computedEndDate?.isAfter(ZonedDateTime.now()) == true || vigilanceArea.isAtAllTimes) - ) { - vigilanceAreaToSave = vigilanceAreaToSave.copy(isArchived = false) - } + val vigilanceAreaToSave = vigilanceArea.copy(seaFront = seaFront) val savedVigilanceArea = vigilanceAreaRepository.save(vigilanceAreaToSave) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/validators/vigilance_area/VigilanceAreaValidator.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/validators/vigilance_area/VigilanceAreaValidator.kt index b5c0ab5b7c..60cfd8f12b 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/validators/vigilance_area/VigilanceAreaValidator.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/validators/vigilance_area/VigilanceAreaValidator.kt @@ -48,46 +48,48 @@ class VigilanceAreaValidator : Validator { data = "Un tag ou un thème est obligatoire", ) } - if (!vigilanceArea.isAtAllTimes) { - if (vigilanceArea.startDatePeriod === null) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "La date de début est obligatoire", - ) - } - if (vigilanceArea.endDatePeriod === null) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "La date de fin est obligatoire", - ) - } - if (vigilanceArea.frequency === null) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "La fréquence est obligatoire", - ) - } - if (vigilanceArea.frequency !== FrequencyEnum.NONE && vigilanceArea.endingCondition === null) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "La condition de fin est obligatoire", - ) - } - if (vigilanceArea.endingCondition === EndingConditionEnum.END_DATE && - vigilanceArea.endingOccurrenceDate === null - ) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "La date de fin de l'occurence est obligatoire", - ) - } - if (vigilanceArea.endingCondition === EndingConditionEnum.OCCURENCES_NUMBER && - (vigilanceArea.endingOccurrencesNumber === null || vigilanceArea.endingOccurrencesNumber == 0) - ) { - throw BackendUsageException( - code = BackendUsageErrorCode.UNVALID_PROPERTY, - data = "Le nombre d'occurence est obligatoire", - ) + vigilanceArea.periods.forEach { period -> + if (!period.isAtAllTimes) { + if (period.startDatePeriod === null) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "La date de début est obligatoire", + ) + } + if (period.endDatePeriod === null) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "La date de fin est obligatoire", + ) + } + if (period.frequency === null) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "La fréquence est obligatoire", + ) + } + if (period.frequency !== FrequencyEnum.NONE && period.endingCondition === null) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "La condition de fin est obligatoire", + ) + } + if (period.endingCondition === EndingConditionEnum.END_DATE && + period.endingOccurrenceDate === null + ) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "La date de fin de l'occurence est obligatoire", + ) + } + if (period.endingCondition === EndingConditionEnum.OCCURENCES_NUMBER && + (period.endingOccurrencesNumber === null || period.endingOccurrencesNumber == 0) + ) { + throw BackendUsageException( + code = BackendUsageErrorCode.UNVALID_PROPERTY, + data = "Le nombre d'occurence est obligatoire", + ) + } } } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt index e2fd562ca1..3e1e1eb2cb 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt @@ -1,7 +1,5 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.vigilanceArea -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum @@ -13,16 +11,9 @@ import java.time.ZonedDateTime data class VigilanceAreaDataInput( val id: Int? = null, val comments: String? = null, - val computedEndDate: ZonedDateTime? = null, val createdBy: String? = null, - val endDatePeriod: ZonedDateTime? = null, - val endingCondition: EndingConditionEnum? = null, - val endingOccurrenceDate: ZonedDateTime? = null, - val endingOccurrencesNumber: Int? = null, - val frequency: FrequencyEnum? = null, val geom: MultiPolygon? = null, val isArchived: Boolean, - val isAtAllTimes: Boolean, val isDraft: Boolean, val images: List? = listOf(), val links: List? = null, @@ -31,27 +22,20 @@ data class VigilanceAreaDataInput( val name: String, val seaFront: String?, val sources: List, - val startDatePeriod: ZonedDateTime? = null, val themes: List = listOf(), val visibility: VisibilityEnum? = null, val createdAt: ZonedDateTime? = null, val updatedAt: ZonedDateTime? = null, val tags: List = listOf(), val validatedAt: ZonedDateTime? = null, + val periods: List = listOf(), ) { fun toVigilanceAreaEntity(): VigilanceAreaEntity = VigilanceAreaEntity( id = this.id, comments = this.comments, - computedEndDate = this.computedEndDate, createdBy = this.createdBy, - endDatePeriod = this.endDatePeriod, - endingCondition = this.endingCondition, - endingOccurrenceDate = this.endingOccurrenceDate, - endingOccurrencesNumber = this.endingOccurrencesNumber, - frequency = this.frequency, geom = this.geom, - isAtAllTimes = this.isAtAllTimes, isArchived = this.isArchived, isDeleted = false, isDraft = this.isDraft, @@ -62,12 +46,12 @@ data class VigilanceAreaDataInput( name = this.name, seaFront = this.seaFront, sources = this.sources.map { it.toVigilanceAreaSourceEntity() }, - startDatePeriod = this.startDatePeriod, themes = this.themes.map { it.toThemeEntity() }, visibility = this.visibility, createdAt = this.createdAt, updatedAt = this.updatedAt, tags = tags.map { it.toTagEntity() }, validatedAt = this.validatedAt, + periods = periods.map { it.toVigilanceAreaPeriodEntity() }, ) } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataPeriodInput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataPeriodInput.kt new file mode 100644 index 0000000000..674b5677be --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataPeriodInput.kt @@ -0,0 +1,32 @@ +package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.vigilanceArea + +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity +import java.time.ZonedDateTime +import java.util.UUID + +data class VigilanceAreaDataPeriodInput( + val id: UUID?, + val computedEndDate: ZonedDateTime? = null, + val endDatePeriod: ZonedDateTime? = null, + val endingCondition: EndingConditionEnum? = null, + val endingOccurrenceDate: ZonedDateTime? = null, + val endingOccurrencesNumber: Int? = null, + val frequency: FrequencyEnum? = null, + val isAtAllTimes: Boolean, + val startDatePeriod: ZonedDateTime? = null, +) { + fun toVigilanceAreaPeriodEntity(): VigilanceAreaPeriodEntity = + VigilanceAreaPeriodEntity( + id = this.id, + computedEndDate = this.computedEndDate, + endDatePeriod = this.endDatePeriod, + endingCondition = this.endingCondition, + endingOccurrenceDate = this.endingOccurrenceDate, + endingOccurrencesNumber = this.endingOccurrencesNumber, + frequency = this.frequency, + isAtAllTimes = this.isAtAllTimes, + startDatePeriod = this.startDatePeriod, + ) +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt index 3d563e5bd9..6d81d3f244 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt @@ -1,7 +1,5 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.outputs.vigilanceArea -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum @@ -15,15 +13,8 @@ import java.time.ZonedDateTime data class VigilanceAreaDataOutput( val id: Int? = null, val comments: String? = null, - val computedEndDate: ZonedDateTime? = null, val createdBy: String? = null, - val endDatePeriod: ZonedDateTime? = null, - val endingCondition: EndingConditionEnum? = null, - val endingOccurrenceDate: ZonedDateTime? = null, - val endingOccurrencesNumber: Int? = null, - val frequency: FrequencyEnum? = null, val geom: MultiPolygon? = null, - val isAtAllTimes: Boolean, val isArchived: Boolean, val isDraft: Boolean, val images: List = mutableListOf(), @@ -33,26 +24,20 @@ data class VigilanceAreaDataOutput( val name: String? = null, val seaFront: String? = null, val sources: List, - val startDatePeriod: ZonedDateTime? = null, val themes: List? = null, val visibility: VisibilityEnum? = null, val createdAt: ZonedDateTime?, val updatedAt: ZonedDateTime?, val tags: List, val validatedAt: ZonedDateTime?, + val periods: List, ) { companion object { fun fromVigilanceArea(vigilanceArea: VigilanceAreaEntity): VigilanceAreaDataOutput = VigilanceAreaDataOutput( id = vigilanceArea.id, comments = vigilanceArea.comments, - computedEndDate = vigilanceArea.computedEndDate, createdBy = vigilanceArea.createdBy, - endDatePeriod = vigilanceArea.endDatePeriod, - endingCondition = vigilanceArea.endingCondition, - endingOccurrenceDate = vigilanceArea.endingOccurrenceDate, - endingOccurrencesNumber = vigilanceArea.endingOccurrencesNumber, - frequency = vigilanceArea.frequency, geom = vigilanceArea.geom, isArchived = vigilanceArea.isArchived, isDraft = vigilanceArea.isDraft, @@ -65,14 +50,13 @@ data class VigilanceAreaDataOutput( name = vigilanceArea.name, seaFront = vigilanceArea.seaFront, sources = vigilanceArea.sources.map { VigilanceAreaSourceOutput.fromVigilanceAreaSourceEntity(it) }, - startDatePeriod = vigilanceArea.startDatePeriod, themes = vigilanceArea.themes.map { fromThemeEntity(it) }, visibility = vigilanceArea.visibility, createdAt = vigilanceArea.createdAt, updatedAt = vigilanceArea.updatedAt, - isAtAllTimes = vigilanceArea.isAtAllTimes, tags = vigilanceArea.tags.map { fromTagEntity(it) }, validatedAt = vigilanceArea.validatedAt, + periods = vigilanceArea.periods.map { VigilanceAreaPeriodDataOutput.fromVigilanceAreaPeriod(it) }, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaPeriodDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaPeriodDataOutput.kt new file mode 100644 index 0000000000..1bd8cc1619 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaPeriodDataOutput.kt @@ -0,0 +1,34 @@ +package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.outputs.vigilanceArea + +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity +import java.time.ZonedDateTime +import java.util.UUID + +data class VigilanceAreaPeriodDataOutput( + val id: UUID?, + val computedEndDate: ZonedDateTime?, + val endDatePeriod: ZonedDateTime?, + val endingCondition: EndingConditionEnum?, + val endingOccurrenceDate: ZonedDateTime?, + val endingOccurrencesNumber: Int?, + val frequency: FrequencyEnum?, + val isAtAllTimes: Boolean, + val startDatePeriod: ZonedDateTime?, +) { + companion object { + fun fromVigilanceAreaPeriod(vigilanceArea: VigilanceAreaPeriodEntity): VigilanceAreaPeriodDataOutput = + VigilanceAreaPeriodDataOutput( + id = vigilanceArea.id, + computedEndDate = vigilanceArea.computedEndDate, + endDatePeriod = vigilanceArea.endDatePeriod, + endingCondition = vigilanceArea.endingCondition, + endingOccurrenceDate = vigilanceArea.endingOccurrenceDate, + endingOccurrencesNumber = vigilanceArea.endingOccurrencesNumber, + frequency = vigilanceArea.frequency, + isAtAllTimes = vigilanceArea.isAtAllTimes, + startDatePeriod = vigilanceArea.startDatePeriod, + ) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt index 8b62f7e60b..5a3d005a52 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt @@ -1,7 +1,5 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.outputs.vigilanceArea -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum @@ -15,15 +13,8 @@ import java.time.ZonedDateTime data class VigilanceAreasDataOutput( val id: Int? = null, val comments: String? = null, - val computedEndDate: ZonedDateTime? = null, val createdBy: String? = null, - val endDatePeriod: ZonedDateTime? = null, - val endingCondition: EndingConditionEnum? = null, - val endingOccurrenceDate: ZonedDateTime? = null, - val endingOccurrencesNumber: Int? = null, - val frequency: FrequencyEnum? = null, val geom: MultiPolygon? = null, - val isAtAllTimes: Boolean, val isArchived: Boolean, val isDraft: Boolean, val links: List? = null, @@ -32,26 +23,19 @@ data class VigilanceAreasDataOutput( val name: String? = null, val seaFront: String?, val sources: List, - val startDatePeriod: ZonedDateTime? = null, val themes: List, val visibility: VisibilityEnum? = null, val tags: List, val validatedAt: ZonedDateTime?, + val periods: List, ) { companion object { fun fromVigilanceArea(vigilanceArea: VigilanceAreaEntity): VigilanceAreasDataOutput = VigilanceAreasDataOutput( id = vigilanceArea.id, comments = vigilanceArea.comments, - computedEndDate = vigilanceArea.computedEndDate, createdBy = vigilanceArea.createdBy, - endDatePeriod = vigilanceArea.endDatePeriod, - endingCondition = vigilanceArea.endingCondition, - endingOccurrenceDate = vigilanceArea.endingOccurrenceDate, - endingOccurrencesNumber = vigilanceArea.endingOccurrencesNumber, - frequency = vigilanceArea.frequency, geom = vigilanceArea.geom, - isAtAllTimes = vigilanceArea.isAtAllTimes, isArchived = vigilanceArea.isArchived, isDraft = vigilanceArea.isDraft, links = vigilanceArea.links, @@ -60,11 +44,11 @@ data class VigilanceAreasDataOutput( name = vigilanceArea.name, seaFront = vigilanceArea.seaFront, sources = vigilanceArea.sources.map { VigilanceAreaSourceOutput.fromVigilanceAreaSourceEntity(it) }, - startDatePeriod = vigilanceArea.startDatePeriod, themes = vigilanceArea.themes.map { fromThemeEntity(it) }, visibility = vigilanceArea.visibility, tags = vigilanceArea.tags.map { fromTagEntity(it) }, validatedAt = vigilanceArea.validatedAt, + periods = vigilanceArea.periods.map { VigilanceAreaPeriodDataOutput.fromVigilanceAreaPeriod(it) }, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt index d8a35f4aeb..7ea873ef93 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt @@ -2,8 +2,6 @@ package fr.gouv.cacem.monitorenv.infrastructure.database.model import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonSerialize -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum @@ -32,8 +30,6 @@ import org.hibernate.dialect.PostgreSQLEnumJdbcType import org.locationtech.jts.geom.MultiPolygon import org.n52.jackson.datatype.jts.GeometryDeserializer import org.n52.jackson.datatype.jts.GeometrySerializer -import java.time.Instant -import java.time.ZoneOffset.UTC import java.time.ZonedDateTime @Entity @@ -45,20 +41,10 @@ data class VigilanceAreaModel( val id: Int? = null, @OneToMany(fetch = FetchType.LAZY, mappedBy = "vigilanceArea") val sources: List, + @OneToMany(fetch = FetchType.LAZY, mappedBy = "vigilanceArea") + val periods: List, @Column(name = "comments") val comments: String? = null, - @Column(name = "computed_end_date") val computedEndDate: Instant? = null, @Column(name = "created_by") val createdBy: String? = null, - @Column(name = "end_date_period") val endDatePeriod: Instant? = null, - @Column(name = "ending_condition", columnDefinition = "vigilance_area_ending_condition") - @Enumerated(EnumType.STRING) - @JdbcType(PostgreSQLEnumJdbcType::class) - val endingCondition: EndingConditionEnum? = null, - @Column(name = "ending_occurrence_date") val endingOccurrenceDate: Instant? = null, - @Column(name = "ending_occurrence_number") val endingOccurrencesNumber: Int? = null, - @Column(name = "frequency", columnDefinition = "vigilance_area_frequency") - @Enumerated(EnumType.STRING) - @JdbcType(PostgreSQLEnumJdbcType::class) - val frequency: FrequencyEnum? = null, @param:JsonSerialize(using = GeometrySerializer::class) @param:JsonDeserialize(contentUsing = GeometryDeserializer::class) @Column(name = "geom") @@ -73,7 +59,6 @@ data class VigilanceAreaModel( var images: MutableList = mutableListOf(), @Column(name = "is_archived", nullable = false) val isArchived: Boolean, @Column(name = "is_deleted", nullable = false) val isDeleted: Boolean, - @Column(name = "is_at_all_times", nullable = false) val isAtAllTimes: Boolean, @Column(name = "is_draft") val isDraft: Boolean, @Column(name = "links", columnDefinition = "jsonb") @Type(JsonBinaryType::class) @@ -83,7 +68,6 @@ data class VigilanceAreaModel( @Column(name = "linked_regulatory_areas", columnDefinition = "int[]") val linkedRegulatoryAreas: List? = listOf(), @Column(name = "name", nullable = false) val name: String, - @Column(name = "start_date_period") val startDatePeriod: Instant? = null, @Column(name = "sea_front") val seaFront: String? = null, @Column(name = "visibility", columnDefinition = "vigilance_area_visibility") @Enumerated(EnumType.STRING) @@ -111,16 +95,9 @@ data class VigilanceAreaModel( VigilanceAreaModel( id = vigilanceArea.id, comments = vigilanceArea.comments, - computedEndDate = vigilanceArea.computedEndDate?.toInstant(), createdBy = vigilanceArea.createdBy, - endingCondition = vigilanceArea.endingCondition, - endingOccurrenceDate = vigilanceArea.endingOccurrenceDate?.toInstant(), - endingOccurrencesNumber = vigilanceArea.endingOccurrencesNumber, - frequency = vigilanceArea.frequency, - endDatePeriod = vigilanceArea.endDatePeriod?.toInstant(), geom = vigilanceArea.geom, isArchived = vigilanceArea.isArchived, - isAtAllTimes = vigilanceArea.isAtAllTimes, isDeleted = vigilanceArea.isDeleted, isDraft = vigilanceArea.isDraft, links = vigilanceArea.links, @@ -128,13 +105,13 @@ data class VigilanceAreaModel( linkedRegulatoryAreas = vigilanceArea.linkedRegulatoryAreas, name = vigilanceArea.name, seaFront = vigilanceArea.seaFront, - startDatePeriod = vigilanceArea.startDatePeriod?.toInstant(), themes = listOf(), visibility = vigilanceArea.visibility, createdAt = vigilanceArea.createdAt, updatedAt = vigilanceArea.updatedAt, tags = listOf(), sources = listOf(), + periods = listOf(), validatedAt = vigilanceArea.validatedAt, ) @@ -146,15 +123,8 @@ data class VigilanceAreaModel( VigilanceAreaEntity( id = id, comments = comments, - computedEndDate = computedEndDate?.atZone(UTC), createdBy = createdBy, - endingCondition = endingCondition, - endingOccurrenceDate = endingOccurrenceDate?.atZone(UTC), - endingOccurrencesNumber = endingOccurrencesNumber, - frequency = frequency, - endDatePeriod = endDatePeriod?.atZone(UTC), geom = geom, - isAtAllTimes = isAtAllTimes, isArchived = isArchived, isDeleted = isDeleted, isDraft = isDraft, @@ -165,13 +135,13 @@ data class VigilanceAreaModel( name = name, seaFront = seaFront, sources = toVigilanceAreaSources(sources), - startDatePeriod = startDatePeriod?.atZone(UTC), themes = toThemeEntities(themes), visibility = visibility, createdAt = createdAt, updatedAt = updatedAt, tags = toTagEntities(tags), validatedAt = validatedAt, + periods = periods.map { it.toVigilanceAreaPeriodEntity() }, ) @PrePersist diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaPeriodModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaPeriodModel.kt new file mode 100644 index 0000000000..4ed6a732fa --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaPeriodModel.kt @@ -0,0 +1,94 @@ +package fr.gouv.cacem.monitorenv.infrastructure.database.model + +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import org.hibernate.Hibernate +import org.hibernate.annotations.JdbcType +import org.hibernate.dialect.PostgreSQLEnumJdbcType +import java.time.Instant +import java.time.ZoneOffset.UTC +import java.util.UUID + +@Entity +@Table(name = "vigilance_area_period") +data class VigilanceAreaPeriodModel( + @Id + @Column(name = "id", nullable = false, unique = true) + @GeneratedValue(strategy = GenerationType.UUID) + val id: UUID?, + @Column(name = "computed_end_date") val computedEndDate: Instant?, + @Column(name = "end_date_period") val endDatePeriod: Instant?, + @Column(name = "ending_condition", columnDefinition = "vigilance_area_ending_condition") + @Enumerated(EnumType.STRING) + @JdbcType(PostgreSQLEnumJdbcType::class) + val endingCondition: EndingConditionEnum?, + @Column(name = "ending_occurrence_date") val endingOccurrenceDate: Instant?, + @Column(name = "ending_occurrence_number") val endingOccurrencesNumber: Int?, + @Column(name = "frequency", columnDefinition = "vigilance_area_frequency") + @Enumerated(EnumType.STRING) + @JdbcType(PostgreSQLEnumJdbcType::class) + val frequency: FrequencyEnum?, + @Column(name = "is_at_all_times", nullable = false) val isAtAllTimes: Boolean, + @Column(name = "start_date_period") val startDatePeriod: Instant?, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "vigilance_areas_id", nullable = false) + val vigilanceArea: VigilanceAreaModel, +) { + companion object { + fun fromVigilanceAreaPeriod( + vigilanceArea: VigilanceAreaModel, + vigilanceAreaPeriod: VigilanceAreaPeriodEntity, + ): VigilanceAreaPeriodModel { + val vigilanceAreaModel = + VigilanceAreaPeriodModel( + id = vigilanceAreaPeriod.id, + vigilanceArea = vigilanceArea, + computedEndDate = vigilanceAreaPeriod.computedEndDate?.toInstant(), + endingCondition = vigilanceAreaPeriod.endingCondition, + endingOccurrenceDate = vigilanceAreaPeriod.endingOccurrenceDate?.toInstant(), + endingOccurrencesNumber = vigilanceAreaPeriod.endingOccurrencesNumber, + frequency = vigilanceAreaPeriod.frequency, + endDatePeriod = vigilanceAreaPeriod.endDatePeriod?.toInstant(), + isAtAllTimes = vigilanceAreaPeriod.isAtAllTimes, + startDatePeriod = vigilanceAreaPeriod.startDatePeriod?.toInstant(), + ) + + return vigilanceAreaModel + } + } + + fun toVigilanceAreaPeriodEntity(): VigilanceAreaPeriodEntity = + VigilanceAreaPeriodEntity( + id = id, + computedEndDate = computedEndDate?.atZone(UTC), + endingCondition = endingCondition, + endingOccurrenceDate = endingOccurrenceDate?.atZone(UTC), + endingOccurrencesNumber = endingOccurrencesNumber, + frequency = frequency, + endDatePeriod = endDatePeriod?.atZone(UTC), + isAtAllTimes = isAtAllTimes, + startDatePeriod = startDatePeriod?.atZone(UTC), + ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false + other as VigilanceAreaPeriodModel + + return id != null && id == other.id + } + + override fun hashCode(): Int = javaClass.hashCode() +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepository.kt index de699a0927..78ea8626a2 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepository.kt @@ -5,6 +5,7 @@ import fr.gouv.cacem.monitorenv.domain.entities.themes.ThemeEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.ImageEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.SourceTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaSourceEntity import fr.gouv.cacem.monitorenv.domain.repositories.IVigilanceAreaRepository import fr.gouv.cacem.monitorenv.infrastructure.database.model.TagVigilanceAreaModel @@ -14,10 +15,13 @@ import fr.gouv.cacem.monitorenv.infrastructure.database.model.ThemeVigilanceArea import fr.gouv.cacem.monitorenv.infrastructure.database.model.ThemeVigilanceAreaModel.Companion.fromThemeEntities import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaImageModel import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaModel +import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaPeriodModel +import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaPeriodModel.Companion.fromVigilanceAreaPeriod import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaSourceModel import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBControlUnitContactRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBTagVigilanceAreaRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBThemeVigilanceAreaRepository +import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBVigilanceAreaPeriodRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBVigilanceAreaRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBVigilanceAreaSourceRepository import org.locationtech.jts.geom.Geometry @@ -29,6 +33,7 @@ import org.springframework.transaction.annotation.Transactional class JpaVigilanceAreaRepository( private val dbVigilanceAreaRepository: IDBVigilanceAreaRepository, private val dbVigilanceAreaSourceRepository: IDBVigilanceAreaSourceRepository, + private val dbVigilanceAreaPeriodRepository: IDBVigilanceAreaPeriodRepository, private val controlUnitContactRepository: IDBControlUnitContactRepository, private val dbTagVigilanceAreaRepository: IDBTagVigilanceAreaRepository, private val dbThemeVigilanceAreaRepository: IDBThemeVigilanceAreaRepository, @@ -45,9 +50,10 @@ class JpaVigilanceAreaRepository( val savedTags = saveTags(savedVigilanceArea, vigilanceArea.tags) val savedThemes = saveThemes(savedVigilanceArea, vigilanceArea.themes) val savedSources = saveSources(savedVigilanceArea, vigilanceArea.sources) + val savedPeriods = savePeriods(savedVigilanceArea, vigilanceArea.periods) return savedVigilanceArea - .copy(tags = savedTags, themes = savedThemes, sources = savedSources) + .copy(tags = savedTags, themes = savedThemes, sources = savedSources, periods = savedPeriods) .toVigilanceAreaEntity() } @@ -78,6 +84,19 @@ class JpaVigilanceAreaRepository( return dbVigilanceAreaSourceRepository.saveAll(vigilanceAreaSourceModels) } + private fun savePeriods( + vigilanceAreaModel: VigilanceAreaModel, + periods: List, + ): List { + vigilanceAreaModel.id?.let { + dbVigilanceAreaSourceRepository.deleteAllByVigilanceAreaId(it) + } + val vigilanceAreaPeriodModels = + periods.map { fromVigilanceAreaPeriod(vigilanceArea = vigilanceAreaModel, vigilanceAreaPeriod = it) } + + return dbVigilanceAreaPeriodRepository.saveAll(vigilanceAreaPeriodModels) + } + private fun fromVigilanceAreaSources( vigilanceAreaModel: VigilanceAreaModel, sources: List, @@ -105,6 +124,7 @@ class JpaVigilanceAreaRepository( vigilanceAreaModel, ), ) + else -> emptyList() } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBVigilanceAreaPeriodRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBVigilanceAreaPeriodRepository.kt new file mode 100644 index 0000000000..5204fac28c --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBVigilanceAreaPeriodRepository.kt @@ -0,0 +1,7 @@ +package fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces + +import fr.gouv.cacem.monitorenv.infrastructure.database.model.VigilanceAreaPeriodModel +import org.springframework.data.jpa.repository.JpaRepository +import java.util.UUID + +interface IDBVigilanceAreaPeriodRepository : JpaRepository diff --git a/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql b/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql new file mode 100644 index 0000000000..2c2c9baa15 --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql @@ -0,0 +1,38 @@ +CREATE TABLE VIGILANCE_AREA_PERIOD +( + ID UUID PRIMARY KEY, + VIGILANCE_AREAS_ID INT, + END_DATE_PERIOD TIMESTAMP, + ENDING_CONDITION VIGILANCE_AREA_ENDING_CONDITION, + ENDING_OCCURRENCE_DATE TIMESTAMP, + ENDING_OCCURRENCE_NUMBER INTEGER, + FREQUENCY VIGILANCE_AREA_FREQUENCY, + START_DATE_PERIOD TIMESTAMP, + COMPUTED_END_DATE TIMESTAMP, + IS_AT_ALL_TIMES BOOLEAN DEFAULT FALSE +); + +INSERT INTO VIGILANCE_AREA_PERIOD (ID, VIGILANCE_AREAS_ID, END_DATE_PERIOD, ENDING_CONDITION, ENDING_OCCURRENCE_DATE, + ENDING_OCCURRENCE_NUMBER, FREQUENCY, START_DATE_PERIOD, COMPUTED_END_DATE, + IS_AT_ALL_TIMES) +SELECT uuid_generate_v4(), + ID, + END_DATE_PERIOD, + ENDING_CONDITION, + ENDING_OCCURRENCE_DATE, + ENDING_OCCURRENCE_NUMBER, + FREQUENCY, + START_DATE_PERIOD, + COMPUTED_END_DATE, + IS_AT_ALL_TIMES +FROM VIGILANCE_AREAS; + +ALTER TABLE VIGILANCE_AREAS + DROP COLUMN END_DATE_PERIOD, + DROP COLUMN ENDING_CONDITION, + DROP COLUMN ENDING_OCCURRENCE_DATE, + DROP COLUMN ENDING_OCCURRENCE_NUMBER, + DROP COLUMN FREQUENCY, + DROP COLUMN START_DATE_PERIOD, + DROP COLUMN COMPUTED_END_DATE, + DROP COLUMN IS_AT_ALL_TIMES; \ No newline at end of file diff --git a/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql b/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql index 232374ac61..7c5556a234 100644 --- a/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql +++ b/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql @@ -25,14 +25,10 @@ $$ /* PUBLISHED VIGILANCE AREAS */ -- period : at this moment - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (1, 'Commentaire sur la zone de vigilance', 'ABC', - today + INTERVAL '1 day', NULL, NULL, NULL, 'NONE', '0106000020E61000000100000001030000000100000021000000B7785F507915FABF5CBF7F7E61214740418F3AB4C128FABF90B809BB2A20474004908E66B530FABF784D6412E51E474030BC4D2C062DFABFC41331079D1D474038BAE144D81DFABFC4DB42355F1C474017509105C103FABF00257DD5371B474038325F1CC1DFF9BF34C24045321A4740541BD7B23AB3F9BF78E914965819474007ECD3D1E37FF9BF04AFF229B31847407F6A248CB547F9BF103B216048184740FECEA995D80CF9BFF8D0EE551C18474062A5E30590D1F8BF5819CCBD301847405A56DE162398F8BF742F6CCE8418474072BD73BBC662F8BF3874964A15194740B7645DE98733F8BFB0F559A1DC194740FDF2AE6C370CF8BF54985825D31A47401B09310B58EEF7BF6439F558EF1B474090F255A70FDBF7BF94E85D4C261D4740CEF101F51BD3F7BF48E7BA096C1E4740A2C5422FCBD6F7BFCC33430BB41F47409AC7AE16F9E5F7BFE0C69CB6F1204740BB31FF551000F8BF5CC2C3D818224740994F313F1024F8BF20E3B81D1E2347407D66B9A89650F8BF18D36A7FF7234740CA95BC89ED83F8BF6006ACA79C24474052176CCF1BBCF8BF34D38A4107254740D4B2E6C5F8F6F8BF58AA05373325474070DCAC554132F9BFD04ED3D81E254740772BB244AE6BF9BF88DCC7EECA24474060C41CA00AA1F9BFC4513CB03A2447401A1D337249D0F9BFA411C0A473234740D58EE1EE99F7F9BF18583B6E7D224740B7785F507915FABF5CBF7F7E61214740', false, '[ { @@ -40,32 +36,46 @@ $$ "linkUrl": "www.google.fr" } ]', 'Zone de vigilance 1', - today - INTERVAL '1 day', - '{"Dragage","Extraction granulats"}', 'PUBLIC', '{"12", "6"}', '{}', - today + INTERVAL '1 day'); + '{"Dragage","Extraction granulats"}', 'PUBLIC', '{"12", "6"}', '{}'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, comments, is_anonymous) - VALUES (uuid_generate_v4(), 1, 'Unité BSN Ste Maxime', 'OTHER'::vigilance_area_source_type, 'Unité de surveillance locale', true); + VALUES (uuid_generate_v4(), 1, 'Unité BSN Ste Maxime', 'OTHER'::vigilance_area_source_type, + 'Unité de surveillance locale', true); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 1, today + INTERVAL '1 day', NULL, NULL, NULL, 'NONE', today - INTERVAL '1 day', + today + INTERVAL '1 day'); - INSERT INTO vigilance_areas_source (id, vigilance_areas_id, control_unit_contacts_id, type, comments, is_anonymous) - (SELECT DISTINCT uuid_generate_v4(), 1, 1, 'CONTROL_UNIT'::vigilance_area_source_type, 'On nous a appelé pour nous signaler un problème dans cette zone', false); + INSERT INTO vigilance_areas_source (id, vigilance_areas_id, control_unit_contacts_id, type, comments, + is_anonymous) + (SELECT DISTINCT uuid_generate_v4(), + 1, + 1, + 'CONTROL_UNIT'::vigilance_area_source_type, + 'On nous a appelé pour nous signaler un problème dans cette zone', + false); -- period : within 3 months - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) + + + VALUES (2, 'Des dauphins partout', 'DEF', + '0106000020E610000001000000010300000001000000050000009E64CAC84F5DFDBF447087DF4CDA4840FAA68553DF2CFDBFE8B528CA7AD84840B2FDB77AA327FCBF30AA4C29CDD64840D9ADABB07A86FBBFE4090ED908D748409E64CAC84F5DFDBF447087DF4CDA4840', + false, NULL, 'Zone de vigilance 2', + '{"AMP","Mixte"}', + 'PRIVATE', '{}', '{"625", "425"}'); - VALUES (2, 'Des dauphins partout', 'DEF', date_within_year_and_3_months + INTERVAL '1 day', + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 2, date_within_year_and_3_months + INTERVAL '1 day', 'END_DATE', date_within_year_and_3_months + INTERVAL '6 year', NULL, - 'ALL_WEEKS', - '0106000020E610000001000000010300000001000000050000009E64CAC84F5DFDBF447087DF4CDA4840FAA68553DF2CFDBFE8B528CA7AD84840B2FDB77AA327FCBF30AA4C29CDD64840D9ADABB07A86FBBFE4090ED908D748409E64CAC84F5DFDBF447087DF4CDA4840', - false, NULL, 'Zone de vigilance 2', - date_within_year_and_3_months - three_months, '{"AMP","Mixte"}', - 'PRIVATE', '{}', '{"625", "425"}', + 'ALL_WEEKS', date_within_year_and_3_months - three_months, date_within_year_and_3_months + INTERVAL '6 year'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) @@ -73,13 +83,9 @@ $$ -- period : within this quarter - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) - VALUES (3, 'comments', 'GHI', date_within_quarter + INTERVAL '1 day', - 'OCCURENCES_NUMBER', NULL, 12, 'ALL_YEARS', + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) + VALUES (3, 'comments', 'GHI', '0106000020E61000000100000001030000000100000008000000E9F870A11E280940CC0CF854053C4540FEE4ACCF8B240940140A8203AD434540857F3863C68D084094EE39718444454008B2439CBF550840586D9AA47F564540601B5E44435E084014DFDE24936245404904E072BD1B0A404C55F4973E614540BDBC30567E890A4004B0B61F623B4540E9F870A11E280940CC0CF854053C4540', false, '[ { @@ -90,124 +96,133 @@ $$ "linkText": "lien vers arrêté réfectoral 2", "linkUrl": "www.google.fr" } - ]', 'Zone de vigilance 3', - date_within_quarter - INTERVAL '1 day', '{"PN","SAGE"}', - 'PUBLIC', '{}', '{}', - date_within_quarter + INTERVAL '11 year - 1 microsecond'); + ]', 'Zone de vigilance 3', '{"PN","SAGE"}', + 'PUBLIC', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 3, date_within_quarter + INTERVAL '1 day', + 'OCCURENCES_NUMBER', NULL, 12, 'ALL_YEARS', + date_within_quarter - INTERVAL '1 day', date_within_quarter + INTERVAL '11 year - 1 microsecond'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) - VALUES (uuid_generate_v4(), 3, 'Particulier qui était sur les lieux', 'OTHER'::vigilance_area_source_type, true); + VALUES (uuid_generate_v4(), 3, 'Particulier qui était sur les lieux', 'OTHER'::vigilance_area_source_type, + true); -- period : within this year - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et mattis est. Integer sed scelerisque nulla, eget placerat felis. Maecenas dui dui, bibendum volutpat nisl sit amet, porttitor suscipit tellus', - 'JKL', date_within_year_not_in_quarter_nor_3_months + INTERVAL '1 day', 'END_DATE', - date_trunc('year', CURRENT_DATE) + INTERVAL '2 year - 1 microsecond', NULL, 'ALL_MONTHS', + 'JKL', '0106000020E6100000010000000103000000010000000B000000C1530E9390B809C09CCB8AE08ED247405138845383AE09C0D81EAD51ADD347408D43A734D2B309C0A0F87963B8D44740EF71916193BF09C0D461DE5673D547403F9EF361F0B809C0686F442B00D647408F77DBF258AB09C0B41BA857BED547403907773443A209C038D1EC9120D54740203FE7F454AB09C0E034792DECD447401AB051C7C0A009C0888761251DD347404075E4CAF1AF09C0C80268E341D24740C1530E9390B809C09CCB8AE08ED24740', false, NULL, 'Zone de vigilance 4', + '{"AMP","PN"}', 'PRIVATE', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 4, date_within_year_not_in_quarter_nor_3_months + INTERVAL '1 day', 'END_DATE', + date_trunc('year', CURRENT_DATE) + INTERVAL '2 year - 1 microsecond', NULL, 'ALL_MONTHS', date_within_year_not_in_quarter_nor_3_months - INTERVAL '1 day', - '{"AMP","PN"}', 'PRIVATE', '{}', '{}', date_within_year_not_in_quarter_nor_3_months + INTERVAL '1 day'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) VALUES (uuid_generate_v4(), 4, 'CACEM', 'INTERNAL'::vigilance_area_source_type, false); -- period : outer from current year - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (9, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'JKL', - date_trunc('year', CURRENT_DATE) + INTERVAL '3 year', - 'OCCURENCES_NUMBER', NULL, 16, 'ALL_WEEKS', '0106000020E61000000100000001030000000100000007000000C830399AA466F6BF9453A87E18EB45400D9F2DAA9D00F6BF04622FF852F5454096E4A71113B8F5BF6044913A01FD454037F7FB26B761F7BFB81A5FD2BEFD45406BB5ABD19B11F9BF48492DD6E6E54540B63B859AABB1F6BFD83F624C07E34540C830399AA466F6BF9453A87E18EB4540', false, NULL, 'Zone de vigilance 9', - date_trunc('year', CURRENT_DATE) + INTERVAL '2 year', '{"Extraction granulats"}', 'PUBLIC', - '{}', '{}', date_trunc('year', CURRENT_DATE) + INTERVAL '3 year'); + '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 9, date_trunc('year', CURRENT_DATE) + INTERVAL '3 year', + 'OCCURENCES_NUMBER', NULL, 16, 'ALL_WEEKS', + date_trunc('year', CURRENT_DATE) + INTERVAL '2 year', + date_trunc('year', CURRENT_DATE) + INTERVAL '3 year'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) VALUES (uuid_generate_v4(), 9, 'CACEM', 'INTERNAL'::vigilance_area_source_type, true); /* DRAFT VIGILANCE AREAS */ -- outdated - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (5, 'Proin maximus luctus urna, sit amet pellentesque diam porta ac. Praesent nisi urna, volutpat vitae consectetur et, aliquet non nisi. Sed molestie metus nec bibendum dignissim. In hac habitasse platea dictumst. Donec eu egestas nulla.', - 'ABC', today - INTERVAL '1 day', NULL, NULL, NULL, - 'NONE', + 'ABC', '0106000020E6100000010000000103000000010000000B000000ACA99227121E2140E08421C47D154540D3E6DE3F5031214070E2EDE388104540E761CC7E6A522140482E4E78E80C45404B4DD06A416921409CD57F36A00F454083F4C40A9B7E21402C1321F33E054540BACCC914244F21406CFF3FB5A0014540EF8500D4FD552140F43FE7898EFE4440B027A867692D214000C738CB7FFA44404AECC68D9AB32040585E4F4873ED4440DA4B567959602040588B590BDC064540ACA99227121E2140E08421C47D154540', - true, NULL, 'Zone de vigilance 5', today - INTERVAL '1 month', - '{"AMP","PN"}', 'PRIVATE', '{}', '{}', - today - INTERVAL '1 day'); - - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + true, NULL, 'Zone de vigilance 5', + '{"AMP","PN"}', 'PRIVATE', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 5, today - INTERVAL '1 day', NULL, NULL, NULL, + 'NONE', today - INTERVAL '1 month', today - INTERVAL '1 day'); + + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) -- period : within 3 months - VALUES (6, NULL, 'DEF', date_within_year_and_3_months + INTERVAL '1 day', 'NEVER', - NULL, - NULL, 'ALL_YEARS', + VALUES (6, NULL, 'DEF', '0106000020E61000000100000001030000000100000021000000D813E79DDB53FABF24C5A378DA31474058DBE0BC1A50FABFB8E3D82B08314740353ADF06FD44FABF5CCC00EE3D304740999ECED7EF32FABF0CDBC585832F4740B3BE98C6A41AFABF8CA28D1EE02E4740585907D20AFDF9BF3494B4015A2E4740C3D48B3145DBF9BF08A26E58F62D47403BE34624A0B6F9BFD0C1BAF8B82D47406F92692C8490F9BF88476A3FA42D4740A4418C34686AF9BFD0C1BAF8B82D47401C504727C345F9BF08A26E58F62D474086CBCB86FD23F9BF3494B4015A2E47402C663A926306F9BF8CA28D1EE02E47404786048118EEF8BF0CDBC585832F4740ABEAF3510BDCF8BF5CCC00EE3D3047408849F29BEDD0F8BFB8E3D82B083147400811ECBA2CCDF8BF24C5A378DA3147408849F29BEDD0F8BF944E1ABFAC324740ABEAF3510BDCF8BFC80CECEA763347404786048118EEF8BFEC212D38313447402C663A926306F9BF1056937FD434474086CBCB86FD23F9BF20609A7C5A3547401C504727C345F9BF5476E60ABE354740A4418C34686AF9BF94FD9358FB3547406F92692C8490F9BFFC1F900B103647403BE34624A0B6F9BF94FD9358FB354740C3D48B3145DBF9BF5476E60ABE354740585907D20AFDF9BF20609A7C5A354740B3BE98C6A41AFABF1056937FD4344740999ECED7EF32FABFEC212D3831344740353ADF06FD44FABFC80CECEA7633474058DBE0BC1A50FABF944E1ABFAC324740D813E79DDB53FABF24C5A378DA314740', true, '[ { "linkText": "lien vers arrêté réfectoral", "linkUrl": "www.google.fr" } - ]', 'Zone de vigilance 6', - date_within_year_and_3_months - three_months, - NULL, 'PUBLIC', '{}', '{}', NULL); + ]', 'Zone de vigilance 6', NULL, 'PUBLIC', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 6, date_within_year_and_3_months + INTERVAL '1 day', 'NEVER', + NULL, NULL, 'ALL_YEARS', date_within_year_and_3_months - three_months, null); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) VALUES (uuid_generate_v4(), 6, 'Unité BSN Ste Maxime', 'OTHER'::vigilance_area_source_type, true); -- period : within this quarter - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (7, 'Proin lobortis, sem quis malesuada mollis, dui orci condimentum nisl, vestibulum porttitor urna nisi non risus.', - 'GHI', date_within_quarter + INTERVAL '1 day', 'OCCURENCES_NUMBER', NULL, - 6, - 'ALL_WEEKS', + 'GHI', '0106000020E6100000010000000103000000010000000500000057F77C6FBF2D15C038E1B84BCD2E48403A42C339BDB014C038E1B84BCD2E48403A42C339BDB014C010F24763BA37484057F77C6FBF2D15C010F24763BA37484057F77C6FBF2D15C038E1B84BCD2E4840', true, NULL, 'Zone de vigilance 7', - date_within_quarter - INTERVAL '1 day', - NULL, 'PUBLIC', '{}', '{}', - date_within_quarter + INTERVAL '4 months'); + NULL, 'PUBLIC', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 7, date_within_quarter + INTERVAL '1 day', 'OCCURENCES_NUMBER', NULL, + 6, 'ALL_WEEKS', date_within_quarter - INTERVAL '1 day', date_within_quarter + INTERVAL '4 months'); INSERT INTO vigilance_areas_source(id, vigilance_areas_id, name, type, is_anonymous) VALUES (uuid_generate_v4(), 7, 'Sémaphore de Fécamp', 'OTHER'::vigilance_area_source_type, false); -- period : outer this year - INSERT INTO public.vigilance_areas(id, comments, created_by, end_date_period, ending_condition, - ending_occurrence_date, - ending_occurrence_number, frequency, geom, is_draft, links, name, - start_date_period, themes, visibility, linked_amps, linked_regulatory_areas, - computed_end_date) + INSERT INTO public.vigilance_areas(id, comments, created_by, geom, is_draft, links, name, + themes, visibility, linked_amps, linked_regulatory_areas) VALUES (8, 'Phasellus ac elit eget ex blandit varius.', 'JKL', - date_within_year_not_in_quarter_nor_3_months + INTERVAL '1 day', 'END_DATE', - '2099-12-31 23:59:59.99999', NULL, 'ALL_MONTHS', '0106000020E61000000100000001030000000100000021000000E8D31F574509F4BF300B11820653464076C82E867DD3F3BFF85436DC6D5146404A78F0B2BC90F3BFBCB1EDB718504640717A2B949343F3BFE8DD6D3B144F4640E7A8D043F9EEF2BF4CA042726A4E4640C385FD122E96F2BF844D1FE9214E4640AD77B48D9B3CF2BFB8B4776C3D4E4640C4A5F6E8B2E5F1BFBC9B88ECBB4E46404FAE8125CB94F1BFF8D6F387984F46407CD65834004DF1BF002481BCCA504640302659601411F1BF044607BC465246402EA0C32955E3F0BF102C0BE2FD534640CE18C49E84C5F0BFEC3A5645DF554640A584D30FC8B8F0BFA4A0AF5FD85746400A5A1CCA9CBDF0BFB4D611C5D5594640AC72C546D3D3F0BF40312CE3C35B46408AB982FE90FAF0BF3489C3C18F5D4640FDC473CF5830F1BF74DF93BC275F46402815B2A21973F1BFD827AB2F7C604640021377C142C0F1BF8480CC0F806146408CE4D111DD14F2BF74683A6929624640AF07A542A86DF2BF987044C071624640C515EEC73AC7F2BFB851175056624640AEE7AB6C231EF3BF882F8B25D861464023DF20300B6FF3BF2C3DF914FC604640F6B64921D6B6F3BF184C768BCA5F4640436749F5C1F2F3BF8486213D4F5E464044EDDE2B8120F4BFD8217CB3985C4640A574DEB6513EF4BFE46FEEBFB75A4640CD08CF450E4BF4BFCC58AED7BE5846406833868B3946F4BFCCD8205FC1564640C61ADD0E0330F4BF3CB381EBD2544640E8D31F574509F4BF300B118206534640', false, NULL, 'Zone de vigilance 8', - date_within_year_not_in_quarter_nor_3_months, '{"Extraction granulats","Dragage"}', - 'PRIVATE', '{}', '{}', '2099-12-31 23:59:59.99999'); - + 'PRIVATE', '{}', '{}'); + + INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, + ending_occurrence_date, + ending_occurrence_number, frequency, start_date_period, computed_end_date) + VALUES (uuid_generate_v4(), 8, date_within_year_not_in_quarter_nor_3_months + INTERVAL '1 day', 'END_DATE', + '2099-12-31 23:59:59.99999', NULL, 'ALL_MONTHS', date_within_year_not_in_quarter_nor_3_months, + '2099-12-31 23:59:59.99999'); END $$; diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt index f048b744f8..19a8c9bd6a 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt @@ -1,7 +1,5 @@ package fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argThat import com.nhaarman.mockitokotlin2.given import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.times @@ -16,7 +14,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.boot.test.system.CapturedOutput import org.springframework.boot.test.system.OutputCaptureExtension -import java.time.ZonedDateTime @ExtendWith(OutputCaptureExtension::class) class CreateOrUpdateVigilanceAreaUTests { @@ -49,12 +46,12 @@ class CreateOrUpdateVigilanceAreaUTests { images = listOf(image), createdAt = null, updatedAt = null, - isAtAllTimes = false, name = "test_name", tags = emptyList(), themes = emptyList(), sources = listOf(aVigilanceAreaSource(name = "test")), validatedAt = null, + periods = emptyList(), ) val expectedVigilanceArea = newVigilanceArea.copy(id = 0) @@ -68,63 +65,4 @@ class CreateOrUpdateVigilanceAreaUTests { assertThat(log.out).contains("Attempt to CREATE or UPDATE vigilance area ${newVigilanceArea.id}") assertThat(log.out).contains("Vigilance area ${result.id} created or updated") } - - @Test - fun `execute should set archive to false then save vigilance area when it is archived and computedEndDate is in the future`() { - val isArchived = true - val computedEndDate = ZonedDateTime.now().plusHours(1) - val archivedVigilanceArea = - VigilanceAreaEntity( - id = 0, - comments = "Comments", - isArchived = isArchived, - isDeleted = false, - isDraft = true, - computedEndDate = computedEndDate, - images = listOf(), - createdAt = null, - updatedAt = null, - isAtAllTimes = false, - name = "test_name", - tags = emptyList(), - themes = emptyList(), - sources = emptyList(), - validatedAt = null, - ) - - given(vigilanceAreaRepository.save(any())).willReturn(archivedVigilanceArea) - - createOrUpdateVigilanceArea.execute(archivedVigilanceArea) - - verify(vigilanceAreaRepository, times(1)).save(argThat { vigilanceArea -> !vigilanceArea.isArchived }) - } - - @Test - fun `execute should set archive to false then save vigilance area when it is archived and is at all times`() { - val isArchived = true - val isAtAllTimes = true - val archivedVigilanceArea = - VigilanceAreaEntity( - id = 0, - comments = "Comments", - isArchived = isArchived, - isDeleted = false, - isDraft = true, - images = listOf(), - createdAt = null, - updatedAt = null, - isAtAllTimes = isAtAllTimes, - name = "test_name", - tags = emptyList(), - themes = emptyList(), - sources = emptyList(), - validatedAt = null, - ) - - given(vigilanceAreaRepository.save(any())).willReturn(archivedVigilanceArea) - - createOrUpdateVigilanceArea.execute(archivedVigilanceArea) - - verify(vigilanceAreaRepository, times(1)).save(argThat { vigilanceArea -> !vigilanceArea.isArchived }) - } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt index 5fa3020d2f..893b4d7c59 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt @@ -7,9 +7,11 @@ import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.ImageEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum import fr.gouv.cacem.monitorenv.domain.use_cases.tags.fixtures.TagFixture.Companion.aTag import fr.gouv.cacem.monitorenv.domain.use_cases.themes.fixtures.ThemeFixture.Companion.aTheme +import fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea.fixtures.VigilanceAreaPeriodFixture.Companion.aVigilanceAreaPeriodEntity import org.locationtech.jts.geom.MultiPolygon import org.locationtech.jts.io.WKTReader import java.time.ZonedDateTime @@ -37,18 +39,24 @@ class VigilanceAreaFixture { isAtAllTimes: Boolean = false, geom: MultiPolygon? = polygon, comments: String? = "Basic area comments", + periods: List = + listOf( + aVigilanceAreaPeriodEntity( + startDate = startDate, + endDate = endDate, + frequency = frequency, + endingOccurrencesNumber = endingOccurrencesNumber, + endCondition = endCondition, + endingOccurenceDate = endingOccurenceDate, + isAtAllTimes = isAtAllTimes, + ), + ), ): VigilanceAreaEntity = VigilanceAreaEntity( id = id, comments = comments, - computedEndDate = ZonedDateTime.parse("2024-01-25T00:00:00Z"), createdBy = createdBy, - endDatePeriod = endDate, - endingCondition = endCondition, - endingOccurrenceDate = endingOccurenceDate, - endingOccurrencesNumber = endingOccurrencesNumber, images = null, - frequency = frequency, geom = geom, isArchived = false, isDeleted = false, @@ -58,26 +66,20 @@ class VigilanceAreaFixture { linkedRegulatoryAreas = listOf(1, 2), name = "Basic Area", sources = emptyList(), - startDatePeriod = startDate, themes = themes, visibility = VisibilityEnum.PUBLIC, createdAt = null, updatedAt = null, - isAtAllTimes = isAtAllTimes, tags = tags, validatedAt = null, + periods = periods, ) fun aVigilanceAreaEntityWithImagesAndLink(): VigilanceAreaEntity = VigilanceAreaEntity( id = 2, comments = "Basic area comments", - computedEndDate = null, createdBy = "ABC", - endDatePeriod = ZonedDateTime.parse("2024-01-15T23:59:59Z"), - endingCondition = null, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, images = listOf( ImageEntity( @@ -93,7 +95,6 @@ class VigilanceAreaFixture { size = 2048, ), ), - frequency = FrequencyEnum.NONE, geom = null, isArchived = false, isDeleted = false, @@ -113,28 +114,21 @@ class VigilanceAreaFixture { linkedRegulatoryAreas = listOf(1, 2), name = "Basic Area", sources = emptyList(), - startDatePeriod = ZonedDateTime.parse("2024-01-15T00:00:00Z"), themes = listOf(aTheme(id = 1, name = "AMP")), visibility = VisibilityEnum.PRIVATE, createdAt = null, updatedAt = null, - isAtAllTimes = false, tags = listOf(aTag(name = "AMP")), validatedAt = null, + periods = listOf(aVigilanceAreaPeriodEntity()), ) fun anArchivedVigilanceAreaEntity(): VigilanceAreaEntity = VigilanceAreaEntity( id = 3, comments = "Basic area comments", - computedEndDate = null, createdBy = "ABC", - endDatePeriod = ZonedDateTime.parse("2024-01-15T23:59:59Z"), - endingCondition = null, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, images = listOf(), - frequency = FrequencyEnum.NONE, geom = null, isArchived = true, isDeleted = false, @@ -144,14 +138,13 @@ class VigilanceAreaFixture { linkedRegulatoryAreas = listOf(1, 2), name = "Basic Area", sources = emptyList(), - startDatePeriod = ZonedDateTime.parse("2024-01-15T00:00:00Z"), themes = listOf(aTheme(id = 1, name = "AMP")), visibility = VisibilityEnum.PRIVATE, createdAt = ZonedDateTime.parse("2024-01-01T00:00:00Z"), updatedAt = ZonedDateTime.parse("2024-01-01T12:00:00Z"), - isAtAllTimes = false, tags = listOf(aTag(name = "AMP")), validatedAt = null, + periods = listOf(aVigilanceAreaPeriodEntity()), ) } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaPeriodFixture.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaPeriodFixture.kt new file mode 100644 index 0000000000..50b1146c65 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaPeriodFixture.kt @@ -0,0 +1,31 @@ +package fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea.fixtures + +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity +import java.time.ZonedDateTime + +class VigilanceAreaPeriodFixture { + companion object { + fun aVigilanceAreaPeriodEntity( + startDate: ZonedDateTime? = ZonedDateTime.parse("2024-01-15T00:00:00Z"), + endDate: ZonedDateTime? = ZonedDateTime.parse("2024-01-15T23:59:59Z"), + frequency: FrequencyEnum? = FrequencyEnum.ALL_WEEKS, + endingOccurenceDate: ZonedDateTime? = null, + endCondition: EndingConditionEnum = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrencesNumber: Int? = 2, + isAtAllTimes: Boolean = false, + ): VigilanceAreaPeriodEntity = + VigilanceAreaPeriodEntity( + id = null, + computedEndDate = ZonedDateTime.parse("2024-01-25T00:00:00Z"), + endDatePeriod = endDate, + endingCondition = endCondition, + endingOccurrenceDate = endingOccurenceDate, + endingOccurrencesNumber = endingOccurrencesNumber, + frequency = frequency, + startDatePeriod = startDate, + isAtAllTimes = isAtAllTimes, + ) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt index 4bc1d2c33b..3bfc3fea3e 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt @@ -9,6 +9,7 @@ import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.ImageEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.SourceTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum import fr.gouv.cacem.monitorenv.domain.use_cases.tags.fixtures.TagFixture.Companion.aTag import fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea.CreateOrUpdateVigilanceArea @@ -22,6 +23,7 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea.fixtures.Vigilanc import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.tags.TagInput import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.vigilanceArea.ImageDataInput import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.vigilanceArea.VigilanceAreaDataInput +import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.vigilanceArea.VigilanceAreaDataPeriodInput import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.nullValue import org.junit.jupiter.api.Test @@ -91,11 +93,20 @@ class VigilanceAreasITests { isDraft = false, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", - endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, - frequency = FrequencyEnum.ALL_WEEKS, - endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + periods = + listOf( + VigilanceAreaPeriodEntity( + endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrenceDate = null, + endingOccurrencesNumber = 2, + frequency = FrequencyEnum.ALL_WEEKS, + endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), + isAtAllTimes = false, + computedEndDate = null, + id = null, + ), + ), geom = polygon, images = listOf( @@ -122,12 +133,10 @@ class VigilanceAreasITests { comments = "Commentaires sur la source", ), ), - startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), themes = listOf(), visibility = VisibilityEnum.PRIVATE, createdAt = ZonedDateTime.parse(createdAt), updatedAt = ZonedDateTime.parse(updatedAt), - isAtAllTimes = false, tags = listOf( aTag( @@ -158,11 +167,6 @@ class VigilanceAreasITests { isDraft = true, comments = null, createdBy = "DEF", - endingCondition = EndingConditionEnum.NEVER, - endingOccurrenceDate = null, - endingOccurrencesNumber = null, - frequency = FrequencyEnum.ALL_WEEKS, - endDatePeriod = ZonedDateTime.parse("2024-12-31T23:59:59Z"), geom = polygon, images = listOf(), links = null, @@ -175,14 +179,26 @@ class VigilanceAreasITests { type = SourceTypeEnum.INTERNAL, ), ), - startDatePeriod = ZonedDateTime.parse("2024-12-01T00:00:00Z"), themes = listOf(), visibility = VisibilityEnum.PUBLIC, createdAt = ZonedDateTime.parse(createdAt), updatedAt = ZonedDateTime.parse(updatedAt), - isAtAllTimes = true, tags = listOf(), validatedAt = ZonedDateTime.parse("2025-01-01T00:00:00Z"), + periods = + listOf( + VigilanceAreaPeriodEntity( + endingCondition = EndingConditionEnum.NEVER, + endingOccurrenceDate = null, + endingOccurrencesNumber = null, + frequency = FrequencyEnum.ALL_WEEKS, + endDatePeriod = ZonedDateTime.parse("2024-12-31T23:59:59Z"), + startDatePeriod = ZonedDateTime.parse("2024-12-01T00:00:00Z"), + isAtAllTimes = true, + computedEndDate = null, + id = null, + ), + ), ) @Test @@ -201,11 +217,13 @@ class VigilanceAreasITests { .andExpect( jsonPath("$[0].comments", equalTo("Commentaires sur la zone de vigilance")), ).andExpect(jsonPath("$[0].createdBy", equalTo("ABC"))) - .andExpect(jsonPath("$[0].endingCondition", equalTo("OCCURENCES_NUMBER"))) - .andExpect(jsonPath("$[0].endingOccurrenceDate").doesNotExist()) - .andExpect(jsonPath("$[0].endingOccurrencesNumber", equalTo(2))) - .andExpect(jsonPath("$[0].frequency", equalTo("ALL_WEEKS"))) - .andExpect(jsonPath("$[0].endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$[0].periods[0].endingCondition", equalTo("OCCURENCES_NUMBER"))) + .andExpect(jsonPath("$[0].periods[0].endingOccurrenceDate").doesNotExist()) + .andExpect(jsonPath("$[0].periods[0].endingOccurrencesNumber", equalTo(2))) + .andExpect(jsonPath("$[0].periods[0].frequency", equalTo("ALL_WEEKS"))) + .andExpect(jsonPath("$[0].periods[0].endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$[0].periods[0].isAtAllTimes", equalTo(false))) + .andExpect(jsonPath("$[0].periods[0].startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$[0].geom.type", equalTo("MultiPolygon"))) .andExpect( jsonPath("$[0].links").doesNotExist(), @@ -213,21 +231,21 @@ class VigilanceAreasITests { .andExpect(jsonPath("$[0].sources[0].isAnonymous", equalTo(true))) .andExpect(jsonPath("$[0].sources[0].comments", equalTo("Commentaires sur la source"))) .andExpect(jsonPath("$[0].sources[0].type", equalTo("OTHER"))) - .andExpect(jsonPath("$[0].startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$[0].themes").isEmpty()) .andExpect(jsonPath("$[0].visibility", equalTo("PRIVATE"))) .andExpect(jsonPath("$[0].validatedAt").doesNotExist()) - .andExpect(jsonPath("$[1].isAtAllTimes", equalTo(true))) .andExpect(jsonPath("$[1].id", equalTo(2))) .andExpect(jsonPath("$[1].name", equalTo("Vigilance Area 2"))) .andExpect(jsonPath("$[1].isDraft", equalTo(true))) .andExpect(jsonPath("$[1].comments").doesNotExist()) .andExpect(jsonPath("$[1].createdBy", equalTo("DEF"))) - .andExpect(jsonPath("$[1].endingCondition", equalTo("NEVER"))) - .andExpect(jsonPath("$[1].endingOccurrenceDate").doesNotExist()) - .andExpect(jsonPath("$[1].endingOccurrencesNumber").doesNotExist()) - .andExpect(jsonPath("$[1].frequency", equalTo("ALL_WEEKS"))) - .andExpect(jsonPath("$[1].endDatePeriod", equalTo("2024-12-31T23:59:59Z"))) + .andExpect(jsonPath("$[1].periods[0].endingCondition", equalTo("NEVER"))) + .andExpect(jsonPath("$[1].periods[0].endingOccurrenceDate").doesNotExist()) + .andExpect(jsonPath("$[1].periods[0].endingOccurrencesNumber").doesNotExist()) + .andExpect(jsonPath("$[1].periods[0].frequency", equalTo("ALL_WEEKS"))) + .andExpect(jsonPath("$[1].periods[0].endDatePeriod", equalTo("2024-12-31T23:59:59Z"))) + .andExpect(jsonPath("$[1].periods[0].startDatePeriod", equalTo("2024-12-01T00:00:00Z"))) + .andExpect(jsonPath("$[1].periods[0].isAtAllTimes", equalTo(true))) .andExpect(jsonPath("$[1].geom.type", equalTo("MultiPolygon"))) .andExpect( jsonPath("$[0].links").doesNotExist(), @@ -235,10 +253,8 @@ class VigilanceAreasITests { .andExpect(jsonPath("$[1].sources[0].isAnonymous", equalTo(false))) .andExpect(jsonPath("$[1].sources[0].link", equalTo("https://example.com"))) .andExpect(jsonPath("$[1].sources[0].type", equalTo("INTERNAL"))) - .andExpect(jsonPath("$[1].startDatePeriod", equalTo("2024-12-01T00:00:00Z"))) .andExpect(jsonPath("$[1].themes").isEmpty()) .andExpect(jsonPath("$[1].visibility", equalTo("PUBLIC"))) - .andExpect(jsonPath("$[1].isAtAllTimes", equalTo(true))) .andExpect(jsonPath("$[1].validatedAt", equalTo("2025-01-01T00:00:00Z"))) } @@ -256,11 +272,13 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.isDraft", equalTo(false))) .andExpect(jsonPath("$.comments", equalTo("Commentaires sur la zone de vigilance"))) .andExpect(jsonPath("$.createdBy", equalTo("ABC"))) - .andExpect(jsonPath("$.endingCondition", equalTo("OCCURENCES_NUMBER"))) - .andExpect(jsonPath("$.endingOccurrenceDate").doesNotExist()) - .andExpect(jsonPath("$.endingOccurrencesNumber", equalTo(2))) - .andExpect(jsonPath("$.frequency", equalTo("ALL_WEEKS"))) - .andExpect(jsonPath("$.endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].endingCondition", equalTo("OCCURENCES_NUMBER"))) + .andExpect(jsonPath("$.periods[0].endingOccurrenceDate").doesNotExist()) + .andExpect(jsonPath("$.periods[0].endingOccurrencesNumber", equalTo(2))) + .andExpect(jsonPath("$.periods[0].frequency", equalTo("ALL_WEEKS"))) + .andExpect(jsonPath("$.periods[0].endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].isAtAllTimes", equalTo(false))) + .andExpect(jsonPath("$.periods[0].startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$.geom.type", equalTo("MultiPolygon"))) .andExpect( jsonPath("$[0].links").doesNotExist(), @@ -268,7 +286,6 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.sources[0].isAnonymous", equalTo(true))) .andExpect(jsonPath("$.sources[0].comments", equalTo("Commentaires sur la source"))) .andExpect(jsonPath("$.sources[0].type", equalTo("OTHER"))) - .andExpect(jsonPath("$.startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$.themes").isEmpty()) .andExpect(jsonPath("$.visibility", equalTo("PRIVATE"))) .andExpect(jsonPath("$.images[0].name", equalTo("image1.jpg"))) @@ -294,11 +311,6 @@ class VigilanceAreasITests { isDraft = false, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", - endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, - frequency = FrequencyEnum.ALL_WEEKS, - endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), geom = polygon, images = listOf( @@ -326,12 +338,10 @@ class VigilanceAreasITests { comments = "Commentaires sur la source", ), ), - startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), themes = listOf(), visibility = VisibilityEnum.PRIVATE, createdAt = ZonedDateTime.parse(createdAt), updatedAt = ZonedDateTime.parse(updatedAt), - isAtAllTimes = false, tags = listOf( TagInput( @@ -351,6 +361,20 @@ class VigilanceAreasITests { ), ), ), + periods = + listOf( + VigilanceAreaDataPeriodInput( + endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrenceDate = null, + endingOccurrencesNumber = 2, + frequency = FrequencyEnum.ALL_WEEKS, + endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), + isAtAllTimes = false, + computedEndDate = null, + id = null, + ), + ), ) given(createOrUpdateVigilanceArea.execute(any())).willReturn(vigilanceArea1) @@ -368,11 +392,13 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.isDraft", equalTo(false))) .andExpect(jsonPath("$.comments", equalTo("Commentaires sur la zone de vigilance"))) .andExpect(jsonPath("$.createdBy", equalTo("ABC"))) - .andExpect(jsonPath("$.endingCondition", equalTo("OCCURENCES_NUMBER"))) - .andExpect(jsonPath("$.endingOccurrenceDate").doesNotExist()) - .andExpect(jsonPath("$.endingOccurrencesNumber", equalTo(2))) - .andExpect(jsonPath("$.frequency", equalTo("ALL_WEEKS"))) - .andExpect(jsonPath("$.endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].endingCondition", equalTo("OCCURENCES_NUMBER"))) + .andExpect(jsonPath("$.periods[0].endingOccurrenceDate").doesNotExist()) + .andExpect(jsonPath("$.periods[0].endingOccurrencesNumber", equalTo(2))) + .andExpect(jsonPath("$.periods[0].frequency", equalTo("ALL_WEEKS"))) + .andExpect(jsonPath("$.periods[0].endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) + .andExpect(jsonPath("$.periods[0].isAtAllTimes", equalTo(false))) .andExpect(jsonPath("$.geom.type", equalTo("MultiPolygon"))) .andExpect( jsonPath("$[0].links").doesNotExist(), @@ -380,7 +406,6 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.sources[0].type", equalTo("OTHER"))) .andExpect(jsonPath("$.sources[0].comments", equalTo("Commentaires sur la source"))) .andExpect(jsonPath("$.sources[0].isAnonymous", equalTo(true))) - .andExpect(jsonPath("$.startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$.themes").isEmpty()) .andExpect(jsonPath("$.visibility", equalTo("PRIVATE"))) .andExpect(jsonPath("$.images[0].name", equalTo("image1.jpg"))) @@ -393,7 +418,6 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.images[1].content", equalTo("BAUG"))) .andExpect(jsonPath("$.createdAt", equalTo(createdAt))) .andExpect(jsonPath("$.updatedAt", equalTo(updatedAt))) - .andExpect(jsonPath("$.isAtAllTimes", equalTo(false))) .andExpect(jsonPath("$.tags[0].id", equalTo(1))) .andExpect(jsonPath("$.tags[0].name", equalTo("tag1"))) .andExpect(jsonPath("$.tags[0].startedAt", equalTo("2024-01-01T00:00:00Z"))) @@ -415,11 +439,6 @@ class VigilanceAreasITests { isDraft = false, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", - endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, - frequency = FrequencyEnum.ALL_WEEKS, - endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), geom = polygon, images = emptyList(), links = null, @@ -433,13 +452,25 @@ class VigilanceAreasITests { comments = "Commentaires sur la source", ), ), - startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), themes = listOf(), visibility = VisibilityEnum.PRIVATE, createdAt = ZonedDateTime.parse(createdAt), updatedAt = ZonedDateTime.parse(updatedAt), - isAtAllTimes = false, tags = listOf(), + periods = + listOf( + VigilanceAreaDataPeriodInput( + endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrenceDate = null, + endingOccurrencesNumber = 2, + frequency = FrequencyEnum.ALL_WEEKS, + endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), + isAtAllTimes = false, + computedEndDate = null, + id = null, + ), + ), ) val updatedVigilanceArea = @@ -463,21 +494,21 @@ class VigilanceAreasITests { .andExpect(jsonPath("$.isDraft", equalTo(false))) .andExpect(jsonPath("$.comments", equalTo("Commentaires sur la zone de vigilance"))) .andExpect(jsonPath("$.createdBy", equalTo("ABC"))) - .andExpect(jsonPath("$.endingCondition", equalTo("OCCURENCES_NUMBER"))) - .andExpect(jsonPath("$.endingOccurrenceDate").doesNotExist()) - .andExpect(jsonPath("$.endingOccurrencesNumber", equalTo(2))) - .andExpect(jsonPath("$.frequency", equalTo("ALL_WEEKS"))) - .andExpect(jsonPath("$.endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].endingCondition", equalTo("OCCURENCES_NUMBER"))) + .andExpect(jsonPath("$.periods[0].endingOccurrenceDate").doesNotExist()) + .andExpect(jsonPath("$.periods[0].endingOccurrencesNumber", equalTo(2))) + .andExpect(jsonPath("$.periods[0].frequency", equalTo("ALL_WEEKS"))) + .andExpect(jsonPath("$.periods[0].endDatePeriod", equalTo("2024-08-08T23:59:59Z"))) + .andExpect(jsonPath("$.periods[0].isAtAllTimes", equalTo(false))) + .andExpect(jsonPath("$.periods[0].startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$.geom.type", equalTo("MultiPolygon"))) .andExpect( jsonPath("$[0].links").doesNotExist(), ).andExpect(jsonPath("$.sources[0].name", equalTo("Source de la zone de vigilance"))) - .andExpect(jsonPath("$.startDatePeriod", equalTo("2024-08-18T00:00:00Z"))) .andExpect(jsonPath("$.themes").isEmpty()) .andExpect(jsonPath("$.visibility", equalTo("PRIVATE"))) .andExpect(jsonPath("$.createdAt", equalTo(createdAt))) .andExpect(jsonPath("$.updatedAt", equalTo(updatedAt))) - .andExpect(jsonPath("$.isAtAllTimes", equalTo(false))) .andExpect(jsonPath("$.images").isEmpty()) .andExpect(jsonPath("$.tags").isEmpty()) } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt index 57ecf98db1..062a00931f 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt @@ -3,10 +3,16 @@ package fr.gouv.cacem.monitorenv.infrastructure.database.repositories import fr.gouv.cacem.monitorenv.config.CustomQueryCountListener import fr.gouv.cacem.monitorenv.config.DataSourceProxyBeanPostProcessor import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitContactEntity -import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.* +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.EndingConditionEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.FrequencyEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.ImageEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.SourceTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaPeriodEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VigilanceAreaSourceEntity +import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.VisibilityEnum import fr.gouv.cacem.monitorenv.domain.use_cases.tags.fixtures.TagFixture.Companion.aTag import fr.gouv.cacem.monitorenv.domain.use_cases.themes.fixtures.ThemeFixture.Companion.aTheme -import fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea.fixtures.VigilanceAreaFixture import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -54,7 +60,6 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(vigilanceArea?.id).isEqualTo(expectedVigilanceAreaId) assertThat(vigilanceArea?.comments).isEqualTo("Commentaire sur la zone de vigilance") assertThat(vigilanceArea?.createdBy).isEqualTo("ABC") - assertThat(vigilanceArea?.endingCondition).isEqualTo(null) assertThat(vigilanceArea?.geom).isNotNull() assertThat(vigilanceArea?.isDeleted).isFalse() assertThat(vigilanceArea?.isDraft).isFalse() @@ -87,6 +92,8 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(vigilanceArea?.tags?.get(1)?.name).isEqualTo("Extraction granulats") assertThat(vigilanceArea?.tags?.get(2)?.id).isEqualTo(7) assertThat(vigilanceArea?.tags?.get(2)?.name).isEqualTo("Dragage") + assertThat(vigilanceArea?.periods).hasSize(1) + assertThat(vigilanceArea?.periods?.get(0)?.endingCondition).isNull() } @Test @@ -101,11 +108,6 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { isDraft = true, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", - endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, - endingOccurrenceDate = null, - endingOccurrencesNumber = 2, - frequency = FrequencyEnum.ALL_WEEKS, - endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), geom = null, images = listOf( @@ -131,14 +133,26 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { isAnonymous = false, ), ), - startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), visibility = VisibilityEnum.PRIVATE, createdAt = null, updatedAt = null, - isAtAllTimes = false, tags = listOf(aTag(id = 5)), themes = listOf(aTheme(id = 9)), validatedAt = ZonedDateTime.parse("2025-01-01T00:00:00Z"), + periods = + listOf( + VigilanceAreaPeriodEntity( + id = null, + computedEndDate = null, + endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrenceDate = null, + endingOccurrencesNumber = 2, + frequency = FrequencyEnum.ALL_WEEKS, + isAtAllTimes = false, + startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), + ), + ), ) // When @@ -148,22 +162,15 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(savedVigilanceArea.id).isNotZero() // id should be generated after save assertThat(savedVigilanceArea.comments).isEqualTo("Commentaires sur la zone de vigilance") assertThat(savedVigilanceArea.createdBy).isEqualTo("ABC") - assertThat(savedVigilanceArea.endDatePeriod) - .isEqualTo(ZonedDateTime.parse("2024-08-08T23:59:59Z")) - assertThat(savedVigilanceArea.endingCondition).isEqualTo(EndingConditionEnum.OCCURENCES_NUMBER) - assertThat(savedVigilanceArea.frequency).isEqualTo(FrequencyEnum.ALL_WEEKS) assertThat(savedVigilanceArea.geom).isNull() assertThat(savedVigilanceArea.isDeleted).isFalse() assertThat(savedVigilanceArea.isDraft).isTrue() assertThat(savedVigilanceArea.links).isNull() assertThat(savedVigilanceArea.sources[0].name).isEqualTo("Source de la zone de vigilance") assertThat(savedVigilanceArea.name).isEqualTo("Nouvelle zone de vigilance") - assertThat(savedVigilanceArea.startDatePeriod) - .isEqualTo(ZonedDateTime.parse("2024-08-18T00:00:00Z")) assertThat(savedVigilanceArea.visibility).isEqualTo(VisibilityEnum.PRIVATE) assertThat(savedVigilanceArea.createdAt).isNotNull() assertThat(savedVigilanceArea.updatedAt).isNull() - assertThat(savedVigilanceArea.isAtAllTimes).isFalse() assertThat(savedVigilanceArea.tags).hasSize(1) assertThat(savedVigilanceArea.tags[0].name).isEqualTo("Mouillage") assertThat(savedVigilanceArea.tags[0].id).isEqualTo(5) @@ -172,6 +179,12 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(savedVigilanceArea.themes[0].id).isEqualTo(9) assertThat(savedVigilanceArea.validatedAt) .isEqualTo(ZonedDateTime.parse("2025-01-01T00:00:00Z")) + assertThat(savedVigilanceArea.periods).hasSize(1) + assertThat(savedVigilanceArea.periods[0].isAtAllTimes).isFalse() + assertThat(savedVigilanceArea.periods[0].startDatePeriod).isEqualTo(ZonedDateTime.parse("2024-08-18T00:00:00Z")) + assertThat(savedVigilanceArea.periods[0].endingCondition).isEqualTo(EndingConditionEnum.OCCURENCES_NUMBER) + assertThat(savedVigilanceArea.periods[0].frequency).isEqualTo(FrequencyEnum.ALL_WEEKS) + assertThat(savedVigilanceArea.periods[0].endDatePeriod).isEqualTo(ZonedDateTime.parse("2024-08-08T23:59:59Z")) } @Test @@ -207,6 +220,20 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { ), ), isDraft = false, + periods = + listOf( + VigilanceAreaPeriodEntity( + id = vigilanceArea.periods[0].id, + computedEndDate = null, + endDatePeriod = ZonedDateTime.parse("2024-08-08T23:59:59Z"), + endingCondition = EndingConditionEnum.OCCURENCES_NUMBER, + endingOccurrenceDate = null, + endingOccurrencesNumber = 2, + frequency = FrequencyEnum.ALL_WEEKS, + isAtAllTimes = false, + startDatePeriod = ZonedDateTime.parse("2024-08-18T00:00:00Z"), + ), + ), ) // When @@ -219,8 +246,6 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { .isEqualTo( "Proin maximus luctus urna, sit amet pellentesque diam porta ac. Praesent nisi urna, volutpat vitae consectetur et, aliquet non nisi. Sed molestie metus nec bibendum dignissim. In hac habitasse platea dictumst. Donec eu egestas nulla.", ) - assertThat(savedVigilanceArea.frequency).isEqualTo(FrequencyEnum.NONE) - assertThat(savedVigilanceArea.endingCondition).isNull() assertThat(savedVigilanceArea.isDeleted).isFalse() assertThat(savedVigilanceArea.isDraft).isFalse() assertThat(savedVigilanceArea.links).isNull() @@ -231,6 +256,12 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(savedVigilanceArea.themes).isEmpty() assertThat(savedVigilanceArea.visibility).isEqualTo(VisibilityEnum.PRIVATE) assertThat(savedVigilanceArea.updatedAt).isNotNull() + assertThat(savedVigilanceArea.periods).hasSize(1) + assertThat(savedVigilanceArea.periods[0].isAtAllTimes).isFalse() + assertThat(savedVigilanceArea.periods[0].startDatePeriod).isEqualTo(ZonedDateTime.parse("2024-08-18T00:00:00Z")) + assertThat(savedVigilanceArea.periods[0].endingCondition).isEqualTo(EndingConditionEnum.OCCURENCES_NUMBER) + assertThat(savedVigilanceArea.periods[0].frequency).isEqualTo(FrequencyEnum.ALL_WEEKS) + assertThat(savedVigilanceArea.periods[0].endDatePeriod).isEqualTo(ZonedDateTime.parse("2024-08-08T23:59:59Z")) } @Test @@ -250,32 +281,32 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { assertThat(deletedVigilanceArea?.isDeleted).isTrue() } - @Test - @Transactional - fun `archive should archive outdated vigilance areas`() { - // Given - val existingVigilanceArea = jpaVigilanceAreaRepository.findById(5) - assertThat(existingVigilanceArea?.isArchived).isEqualTo(false) - // When - jpaVigilanceAreaRepository.archiveOutdatedVigilanceAreas() - // Then - val archivedVigilanceArea = jpaVigilanceAreaRepository.findById(5) - assertThat(archivedVigilanceArea?.isArchived).isEqualTo(true) - } +// @Test +// @Transactional +// fun `archive should archive outdated vigilance areas`() { +// // Given +// val existingVigilanceArea = jpaVigilanceAreaRepository.findById(5) +// assertThat(existingVigilanceArea?.isArchived).isEqualTo(false) +// // When +// jpaVigilanceAreaRepository.archiveOutdatedVigilanceAreas() +// // Then +// val archivedVigilanceArea = jpaVigilanceAreaRepository.findById(5) +// assertThat(archivedVigilanceArea?.isArchived).isEqualTo(true) +// } - @Test - @Transactional - fun `archive should not archive limitless vigilance areas`() { - // Given - val limitlessVigilanceArea = - VigilanceAreaFixture.aVigilanceAreaEntity().copy(id = null, isAtAllTimes = true) - val savedVigilanceArea = jpaVigilanceAreaRepository.save(limitlessVigilanceArea) - // When - jpaVigilanceAreaRepository.archiveOutdatedVigilanceAreas() - // Then - val archivedVigilanceArea = jpaVigilanceAreaRepository.findById(savedVigilanceArea.id!!) - assertThat(archivedVigilanceArea?.isArchived).isEqualTo(false) - } +// @Test +// @Transactional +// fun `archive should not archive limitless vigilance areas`() { +// // Given +// val limitlessVigilanceArea = +// VigilanceAreaFixture.aVigilanceAreaEntity().copy(id = null, isAtAllTimes = true) +// val savedVigilanceArea = jpaVigilanceAreaRepository.save(limitlessVigilanceArea) +// // When +// jpaVigilanceAreaRepository.archiveOutdatedVigilanceAreas() +// // Then +// val archivedVigilanceArea = jpaVigilanceAreaRepository.findById(savedVigilanceArea.id!!) +// assertThat(archivedVigilanceArea?.isArchived).isEqualTo(false) +// } @Test fun `findAllByGeometry should return all vigilance areas that intersect the geometry `() { From bd16a64df3bbdedbe53c7b5abdcaf2023c166e91 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Wed, 17 Dec 2025 17:42:54 +0100 Subject: [PATCH 02/12] feat: rework vigilancearea periods frontend --- .../create_vigilance_area.spec.ts | 10 +- .../components/Pdf/VigilanceAreas/index.tsx | 36 +-- frontend/src/features/Dashboard/types.ts | 14 +- .../Dashboard/useCases/exportBrief.ts | 10 +- .../components/VigilanceAreaForm/Form.tsx | 14 +- .../VigilanceAreaForm/Frequency.tsx | 105 ++++----- .../Panel/PanelPeriodAndThemes.tsx | 25 ++- .../Planning/PlanningBody.tsx | 7 +- .../VigilanceAreaForm/Planning/utils.test.ts | 112 ++++++---- .../VigilanceAreaForm/Planning/utils.ts | 26 +-- .../components/VigilanceAreaForm/Schema.ts | 109 +++++----- .../components/VigilanceAreaForm/utils.ts | 9 +- frontend/src/features/VigilanceArea/types.ts | 30 ++- .../useCases/saveVigilanceArea.ts | 33 ++- .../overlays/OverlayContent.tsx | 20 +- ...getFilteredVigilanceAreasPerPeriod.test.ts | 205 +++++++++++------- .../getFilteredVigilanceAreasPerPeriod.ts | 71 +++--- 17 files changed, 471 insertions(+), 365 deletions(-) diff --git a/frontend/cypress/e2e/main_window/vigilance_area/create_vigilance_area.spec.ts b/frontend/cypress/e2e/main_window/vigilance_area/create_vigilance_area.spec.ts index b74311aeb3..e0d859e3ca 100644 --- a/frontend/cypress/e2e/main_window/vigilance_area/create_vigilance_area.spec.ts +++ b/frontend/cypress/e2e/main_window/vigilance_area/create_vigilance_area.spec.ts @@ -101,12 +101,14 @@ describe('Create Vigilance Area', () => { const endDateDay = endDate[2] < 10 ? `0${endDate[2]}` : endDate[2] // Check the response expect(createdVigilanceArea.name).equal('Nouvelle zone de vigilance') - expect(createdVigilanceArea.startDatePeriod).equal( + expect(createdVigilanceArea.periods[0].startDatePeriod).equal( `${startDate[0]}-${startDateMonth}-${startDateDay}T00:00:00.000Z` ) - expect(createdVigilanceArea.endDatePeriod).equal(`${endDate[0]}-${endDateMonth}-${endDateDay}T23:59:59.000Z`) - expect(createdVigilanceArea.frequency).equal(VigilanceArea.Frequency.ALL_WEEKS) - expect(createdVigilanceArea.endingCondition).equal(VigilanceArea.EndingCondition.NEVER) + expect(createdVigilanceArea.periods[0].endDatePeriod).equal( + `${endDate[0]}-${endDateMonth}-${endDateDay}T23:59:59.000Z` + ) + expect(createdVigilanceArea.periods[0].frequency).equal(VigilanceArea.Frequency.ALL_WEEKS) + expect(createdVigilanceArea.periods[0].endingCondition).equal(VigilanceArea.EndingCondition.NEVER) expect(createdVigilanceArea.geom.type).equal('MultiPolygon') expect(createdVigilanceArea.themes[0].id).equal(9) expect(createdVigilanceArea.themes[0].name).equal('Pêche à pied') diff --git a/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx b/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx index 778306d58b..8d08325fe4 100644 --- a/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx @@ -1,4 +1,4 @@ -import { Orientation, type ImageFront } from '@components/Form/types' +import { type ImageFront, Orientation } from '@components/Form/types' import { Dashboard } from '@features/Dashboard/types' import { getVigilanceAreaColorWithAlpha } from '@features/VigilanceArea/components/VigilanceAreaLayer/style' import { EMPTY_VALUE } from '@features/VigilanceArea/constants' @@ -93,12 +93,10 @@ export function VigilanceAreas({ {vigilanceAreas.map(vigilanceArea => { - const formattedStartPeriod = vigilanceArea.startDatePeriod - ? customDayjs(vigilanceArea.startDatePeriod).utc().format('DD/MM/YYYY') - : undefined - const formattedEndPeriod = vigilanceArea.endDatePeriod - ? customDayjs(vigilanceArea.endDatePeriod).utc().format('DD/MM/YYYY') - : undefined + const formattedStartPeriod = (startDatePeriod?: string) => + startDatePeriod ? customDayjs(startDatePeriod).utc().format('DD/MM/YYYY') : undefined + const formattedEndPeriod = (endDatePeriod?: string) => + endDatePeriod ? customDayjs(endDatePeriod).utc().format('DD/MM/YYYY') : undefined const amps = linkedAMPs.filter(amp => vigilanceArea.linkedAMPs?.includes(amp.id)) const regulatoryAreas = linkedRegulatoryAreas.filter(regulatoryArea => @@ -127,17 +125,21 @@ export function VigilanceAreas({ - Période - - - - {formattedStartPeriod ? `Du ${formattedStartPeriod} au ${formattedEndPeriod}` : EMPTY_VALUE} - - {frequencyText(vigilanceArea?.frequency)} - - {endingOccurenceText(vigilanceArea?.endingCondition, vigilanceArea?.computedEndDate)} - + Périodes + {vigilanceArea.periods?.map(period => ( + + + {formattedStartPeriod(period.startDatePeriod) + ? `Du ${formattedStartPeriod(period.startDatePeriod)} au ${formattedEndPeriod( + period.endDatePeriod + )}` + : EMPTY_VALUE} + + {frequencyText(period?.frequency)} + {endingOccurenceText(period?.endingCondition, period?.computedEndDate)} + + ))} diff --git a/frontend/src/features/Dashboard/types.ts b/frontend/src/features/Dashboard/types.ts index dc2c5861e1..797aa8f519 100644 --- a/frontend/src/features/Dashboard/types.ts +++ b/frontend/src/features/Dashboard/types.ts @@ -30,6 +30,7 @@ export namespace Dashboard { themes: ThemeFromAPI[] vigilanceAreas: VigilanceArea.VigilanceAreaFromApi[] | VigilanceArea.VigilanceAreaLayer[] } + export interface ExtractedAreaFromApi { ampIds: number[] inseeCode: string @@ -39,6 +40,7 @@ export namespace Dashboard { themes: ThemeFromAPI[] vigilanceAreaIds: number[] } + export type Dashboard = { ampIds: number[] comments?: string @@ -199,9 +201,6 @@ export namespace Dashboard { type VigilanceAreaForEditableBrief = { color: string comments?: string - endDatePeriod?: string - endingOccurenceDate: string - frequency: string id: number image?: string imagesAttachments?: ImageApi[] @@ -210,10 +209,17 @@ export namespace Dashboard { links?: Link[] minimap?: string name: string - startDatePeriod?: string + periods?: VigilanceAreaPeriodForEditableBrief[] themes?: string visibility?: string } + + type VigilanceAreaPeriodForEditableBrief = { + endDatePeriod?: string + endingOccurenceDate: string + frequency: string + } + type RecentActivityForEditableBrief = { image?: string period: string diff --git a/frontend/src/features/Dashboard/useCases/exportBrief.ts b/frontend/src/features/Dashboard/useCases/exportBrief.ts index b61fa40747..0fcb51d17e 100644 --- a/frontend/src/features/Dashboard/useCases/exportBrief.ts +++ b/frontend/src/features/Dashboard/useCases/exportBrief.ts @@ -126,19 +126,19 @@ export const exportBrief = return { color: getVigilanceAreaColorWithAlpha(vigilanceArea.name, vigilanceArea.comments), comments: vigilanceArea.comments, - endDatePeriod: vigilanceArea.endDatePeriod, - endingOccurenceDate: endingOccurenceText(vigilanceArea.endingCondition, vigilanceArea.computedEndDate), - frequency: frequencyText(vigilanceArea.frequency), id: vigilanceArea.id as number, image, imagesAttachments, - isAtAllTimes: vigilanceArea.isAtAllTimes, linkedAMPs: filteredAmps, linkedRegulatoryAreas: filteredRegulatoryAreas, links: vigilanceArea.links, minimap, name: vigilanceArea.name as string, - startDatePeriod: vigilanceArea.startDatePeriod, + periods: vigilanceArea.periods?.map(period => ({ + ...period, + endingOccurenceDate: endingOccurenceText(period.endingCondition, period.computedEndDate), + frequency: frequencyText(period.frequency) + })), themes: displayThemes(vigilanceArea.themes), visibility: VigilanceArea.VisibilityLabel[vigilanceArea?.visibility ?? VigilanceArea.VisibilityLabel.PUBLIC] } diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Form.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Form.tsx index 24eabbf232..9afca33240 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Form.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Form.tsx @@ -140,8 +140,8 @@ export function Form() { } const setPeriod = (period: DateAsStringRange | undefined) => { - setFieldValue('startDatePeriod', period ? period[0] : undefined) - setFieldValue('endDatePeriod', period ? period[1] : undefined) + setFieldValue('periods[0].startDatePeriod', period ? period[0] : undefined) + setFieldValue('periods[0].endDatePeriod', period ? period[1] : undefined) } return ( @@ -183,12 +183,12 @@ export function Form() { - + diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx index bde0426af2..46dfa9e519 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx @@ -10,82 +10,85 @@ export function Frequency() { const endingConditionOptions = getOptionsFromLabelledEnum(VigilanceArea.EndingConditionLabel) const updateFrequency = (nextFrequency: string | undefined) => { - setFieldValue('frequency', nextFrequency) + setFieldValue('periods[0].frequency', nextFrequency) if (nextFrequency === VigilanceArea.Frequency.NONE) { - setFieldValue('endingCondition', undefined) - setFieldValue('endingOccurrenceDate', undefined) - setFieldValue('endingOccurrencesNumber', undefined) + setFieldValue('periods[0].endingCondition', undefined) + setFieldValue('periods[0].endingOccurrenceDate', undefined) + setFieldValue('periods[0].endingOccurrencesNumber', undefined) } } const updateEndingCondition = (nextEndingCondition: string | undefined) => { - setFieldValue('endingCondition', nextEndingCondition) + setFieldValue('periods[0].endingCondition', nextEndingCondition) if (nextEndingCondition === VigilanceArea.EndingCondition.NEVER) { - setFieldValue('endingOccurrenceDate', undefined) - setFieldValue('endingOccurrencesNumber', undefined) + setFieldValue('periods[0].endingOccurrenceDate', undefined) + setFieldValue('periods[0].endingOccurrencesNumber', undefined) } } return ( <> - {values.endingCondition === VigilanceArea.EndingCondition.OCCURENCES_NUMBER && ( - + + + {editedPeriod.frequency && editedPeriod.frequency !== VigilanceArea.Frequency.NONE && ( + + {editedPeriod.endingCondition === VigilanceArea.EndingCondition.OCCURENCES_NUMBER && ( ('periods') + const [field, meta, helper] = useField('periods') const periods = field.value - const setIsAtAllTimes = ( + const setIsAtAllTimes = async ( isChecked: boolean | undefined, isCritical: boolean, push: (vigilanceArea: VigilanceArea.VigilanceAreaPeriod) => void, remove: (index: number) => void ) => { + async function removeRemainingPeriod() { + await helper.setValue(field.value.filter(period => period.isCritical !== isCritical)) + } + const vigilanceAreaPeriod: VigilanceArea.VigilanceAreaPeriod = { ...getVigilanceAreaPeriodInitialValues(), isAtAllTimes: true, isCritical } const index = field.value.findIndex(period => period.isAtAllTimes && period.isCritical === isCritical) + if (isChecked && index === -1) { + await removeRemainingPeriod() push(vigilanceAreaPeriod) } + if (!isChecked && index !== -1) { remove(index) } @@ -39,27 +46,28 @@ export function Periods() { return ( - - - - setIsAtAllTimes(isChecked, false, push, remove)} - /> - - - - setIsAtAllTimes(isChecked, true, push, remove)} - /> - + + + + setIsAtAllTimes(isChecked, false, push, remove)} + /> + + + + setIsAtAllTimes(isChecked, true, push, remove)} + /> + + {periods .filter((period: VigilanceArea.VigilanceAreaPeriod) => !period.isAtAllTimes) .map((period: VigilanceArea.VigilanceAreaPeriod) => { @@ -118,6 +126,8 @@ const Wrapper = styled.div` display: flex; flex-direction: column; gap: 8px; + border-top: 1px solid ${p => p.theme.color.lightGray}; + padding-top: 8px; ` export const CriticalCheckbox = styled(Checkbox)` @@ -130,6 +140,13 @@ export const CheckboxWrapper = styled.div` position: relative; ` +export const CheckboxesWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px 0; +` + export const StyledCircle = styled.div<{ $isCritical?: boolean }>` height: 10px; width: 10px; diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx index 2764be7894..4f188898d4 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx @@ -45,7 +45,7 @@ export function Sources() { onClick={() => push({ comments: undefined, - controlUnitContacts: [], + controlUnitContacts: undefined, isAnonymous: false, type: VigilanceArea.VigilanceAreaSourceType.CONTROL_UNIT }) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx index 413a21382d..697d43c148 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx @@ -21,7 +21,7 @@ import { Form } from './Form' import { VigilanceAreaPanel } from './Panel' import { VigilanceAreaSchema } from './Schema' import { Header, SubHeaderContainer, Title, TitleContainer } from './style' -import { getVigilanceAreaInitialValues } from './utils' +import { getVigilanceAreaInitialValues, isWithinPeriod } from './utils' type VigilanceAreaFormProps = { isOpen: boolean @@ -91,7 +91,10 @@ export function VigilanceAreaForm({ isOpen, isReadOnly = false, vigilanceAreaId
{ return { comments: undefined, @@ -42,3 +45,16 @@ export const isFormValid = (vigilanceArea: VigilanceArea.VigilanceArea | undefin return SchemaToValidate.isValidSync(vigilanceArea, { abortEarly: false }) } + +export function isWithinPeriod( + periods: VigilanceArea.VigilanceAreaPeriod[] | undefined, + isCritical: boolean | undefined +) { + return !!periods?.some(period => { + const dateRanges = computeOccurenceWithinCurrentYear(period) + + return dateRanges.some( + dateRange => dateRange.isCritical === isCritical && customDayjs.utc().isBetween(dateRange.start, dateRange.end) + ) + }) +} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts index 5cd4b487ec..41617fb1df 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts @@ -1,14 +1,25 @@ +import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' +import { VigilanceArea } from '@features/VigilanceArea/types' import { THEME } from '@mtes-mct/monitor-ui' +import { getColorWithAlpha, stringToColorInGroup } from '@utils/utils' import { Fill, Stroke, Style } from 'ol/style' import { Layers } from '../../../../domain/entities/layers/constants' -import { getColorWithAlpha, stringToColorInGroup } from '../../../../utils/utils' -const getStyle = (color: string, isSelected: boolean | undefined, asMinimap: boolean, isFilled: boolean = true) => { +const getStyle = ( + color: string, + isSelected: boolean | undefined, + asMinimap: boolean, + isFilled: boolean = true, + isWithinCriticalPeriod: boolean = false +) => { const strokeColor = () => { if (asMinimap) { return getColorWithAlpha(THEME.color.charcoal, 1) } + if (isWithinCriticalPeriod) { + return getColorWithAlpha(THEME.color.maximumRed, 1) + } return isSelected ? getColorWithAlpha('#FF4433', 1) : getColorWithAlpha(THEME.color.rufous, 1) } @@ -19,7 +30,7 @@ const getStyle = (color: string, isSelected: boolean | undefined, asMinimap: boo }), stroke: new Stroke({ color: strokeColor(), - width: isSelected || asMinimap ? 3 : 1 + width: isSelected || asMinimap || isWithinCriticalPeriod ? 3 : 1 }) }) } @@ -27,9 +38,9 @@ const getStyle = (color: string, isSelected: boolean | undefined, asMinimap: boo export const getVigilanceAreaColorWithAlpha = ( name: string | null = '', comments: string | null = '', - isArchived = false + withoutPeriod = false ) => { - if (isArchived) { + if (withoutPeriod) { return THEME.color.white } @@ -37,9 +48,20 @@ export const getVigilanceAreaColorWithAlpha = ( } export const getVigilanceAreaLayerStyle = feature => { - const isArchived = feature.get('isArchived') + const periods = feature.get('periods') as Array | undefined + const isWithinCriticalPeriod = isWithinPeriod(periods, true) - const colorWithAlpha = getVigilanceAreaColorWithAlpha(feature.get('name'), feature.get('comments'), isArchived) + const colorWithAlpha = getVigilanceAreaColorWithAlpha( + feature.get('name'), + feature.get('comments'), + periods?.length === 0 + ) - return getStyle(colorWithAlpha, feature.get('isSelected'), feature.get('asMinimap'), feature.get('isFilled')) + return getStyle( + colorWithAlpha, + feature.get('isSelected'), + feature.get('asMinimap'), + feature.get('isFilled'), + isWithinCriticalPeriod + ) } diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper.ts index ec6370aebf..f448c3f05a 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper.ts @@ -20,9 +20,7 @@ export const getVigilanceAreaZoneFeature = ( }) const area = geometry && getArea(geometry) - const feature = new Feature({ - geometry - }) + const feature = new Feature({ geometry }) const isolatedLayerIsVigilanceArea = isolatedLayer?.type.includes('VIGILANCE_AREA') ?? false const isLayerFilled = isolatedLayer diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx new file mode 100644 index 0000000000..ecb831f5df --- /dev/null +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx @@ -0,0 +1,44 @@ +import { OptionValue } from '@features/Reportings/Filters/style' +import { vigilanceAreaFiltersActions } from '@features/VigilanceArea/components/VigilanceAreasList/Filters/slice' +import { useAppDispatch } from '@hooks/useAppDispatch' +import { useAppSelector } from '@hooks/useAppSelector' +import { CheckPicker, type Option } from '@mtes-mct/monitor-ui' +import React from 'react' + +import { VigilanceArea } from '../types' + +export function VigilanceAreaTypeFilter({ style }: { style?: React.CSSProperties }) { + const dispatch = useAppDispatch() + const vigilanceAreaTypeOptions = Object.entries(VigilanceArea.VigilanceAreaFilterTypeLabel).map(([value, label]) => ({ + label, + value + })) as Option[] + + const filteredVigilanceAreaType = useAppSelector(state => state.vigilanceAreaFilters.type) + + const handleSetFilteredVigilanceType = ( + nextVigilanceAreaType: VigilanceArea.VigilanceAreaFilterType[] | undefined + ) => { + dispatch(vigilanceAreaFiltersActions.updateFilters({ key: 'type', value: nextVigilanceAreaType ?? [] })) + } + + return ( + + filteredVigilanceAreaType && ( + {`Type de zone de vigilance (${filteredVigilanceAreaType.length})`} + ) + } + style={style} + value={filteredVigilanceAreaType} + /> + ) +} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/index.tsx index 7830ce21ed..62632b84c9 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/index.tsx @@ -13,6 +13,7 @@ import { setSearchExtent, setShouldFilterSearchOnMapExtent } from '@features/layersSelector/search/slice' +import { VigilanceAreaTypeFilter } from '@features/VigilanceArea/components/VigilanceAreaTypeFilter' import { VigilanceArea } from '@features/VigilanceArea/types' import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' @@ -140,6 +141,7 @@ export function VigilanceAreasFilters() { + diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/slice.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/slice.ts index c94a8de177..7426fae2e7 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/slice.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Filters/slice.ts @@ -26,8 +26,10 @@ export type VigilanceAreaSliceState = { seaFronts: string[] specificPeriod: DateAsStringRange | undefined status: VigilanceArea.Status[] + type: VigilanceArea.VigilanceAreaFilterType[] visibility: VigilanceArea.Visibility[] } + export const INITIAL_STATE: VigilanceAreaSliceState = { createdBy: [], nbOfFiltersSetted: 0, @@ -35,8 +37,10 @@ export const INITIAL_STATE: VigilanceAreaSliceState = { seaFronts: [], specificPeriod: undefined, status: [VigilanceArea.Status.DRAFT, VigilanceArea.Status.PUBLISHED], + type: [], visibility: [VigilanceArea.Visibility.PUBLIC, VigilanceArea.Visibility.PRIVATE] } + export const vigilanceAreaFiltersSlice = createSlice({ initialState: INITIAL_STATE, name: 'vigilanceAreaFilters', diff --git a/frontend/src/features/VigilanceArea/hooks/useGetFilteredVigilanceAreasQuery.ts b/frontend/src/features/VigilanceArea/hooks/useGetFilteredVigilanceAreasQuery.ts index 9c4f1326b7..6aede689fb 100644 --- a/frontend/src/features/VigilanceArea/hooks/useGetFilteredVigilanceAreasQuery.ts +++ b/frontend/src/features/VigilanceArea/hooks/useGetFilteredVigilanceAreasQuery.ts @@ -1,6 +1,7 @@ import { useGetVigilanceAreasQuery } from '@api/vigilanceAreasAPI' import { getFilterVigilanceAreasPerPeriod } from '@features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod' import { getIntersectingLayers } from '@features/layersSelector/utils/getIntersectingLayerIds' +import { isVigilanceAreaPartOfType } from '@features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType' import { useAppSelector } from '@hooks/useAppSelector' import { CustomSearch } from '@mtes-mct/monitor-ui' import { useMemo } from 'react' @@ -17,7 +18,7 @@ import { isVigilanceAreaPartOfVisibility } from '../useCases/filters/isVigilance export const useGetFilteredVigilanceAreasQuery = () => { const isSuperUser = useAppSelector(state => state.account.isSuperUser) - const { createdBy, period, seaFronts, specificPeriod, status, visibility } = useAppSelector( + const { createdBy, period, seaFronts, specificPeriod, status, type, visibility } = useAppSelector( state => state.vigilanceAreaFilters ) @@ -43,7 +44,8 @@ export const useGetFilteredVigilanceAreasQuery = () => { isVigilanceAreaPartOfStatus(vigilanceArea, isSuperUser ? status : [VigilanceArea.Status.PUBLISHED]) && isVigilanceAreaPartOfTag(vigilanceArea, filteredRegulatoryTags) && isVigilanceAreaPartOfTheme(vigilanceArea, filteredRegulatoryThemes) && - isVigilanceAreaPartOfVisibility(vigilanceArea, visibility) + isVigilanceAreaPartOfVisibility(vigilanceArea, visibility) && + isVigilanceAreaPartOfType(vigilanceArea, type) ), [ vigilanceAreas, @@ -53,13 +55,14 @@ export const useGetFilteredVigilanceAreasQuery = () => { status, filteredRegulatoryTags, filteredRegulatoryThemes, - visibility + visibility, + type ] ) const vigilanceAreasByPeriod = useMemo( - () => getFilterVigilanceAreasPerPeriod(tempVigilanceAreas, period, specificPeriod, isSuperUser), - [tempVigilanceAreas, period, specificPeriod, isSuperUser] + () => getFilterVigilanceAreasPerPeriod(tempVigilanceAreas, period, specificPeriod, type, isSuperUser), + [tempVigilanceAreas, period, specificPeriod, type, isSuperUser] ) const filteredVigilanceAreas = useMemo(() => { diff --git a/frontend/src/features/VigilanceArea/types.ts b/frontend/src/features/VigilanceArea/types.ts index b582e4775d..4cc2f5c1bc 100644 --- a/frontend/src/features/VigilanceArea/types.ts +++ b/frontend/src/features/VigilanceArea/types.ts @@ -146,12 +146,17 @@ export namespace VigilanceArea { SPECIFIC_PERIOD = 'Période spécifique' } - export type VigilanceAreaFilterPeriodType = - | 'AT_THE_MOMENT' - | 'NEXT_THREE_MONTHS' - | 'CURRENT_QUARTER' - | 'CURRENT_YEAR' - | 'SPECIFIC_PERIOD' + export enum VigilanceAreaFilterType { + SIMPLE = 'SIMPLE', + CRITICAL = 'CRITICAL', + NONE = 'NONE' + } + + export enum VigilanceAreaFilterTypeLabel { + SIMPLE = 'Période de vigilance simple en cours', + CRITICAL = 'Période de vigilance critique en cours', + NONE = 'Aucune période de vigilance en cours' + } export type VigilanceAreaProperties = Omit & { id: number @@ -161,8 +166,6 @@ export namespace VigilanceArea { export type VigilanceAreaLayer = VigilanceArea.VigilanceAreaFromApi & { bbox: number[] } - export type StatusType = 'DRAFT' | 'PUBLISHED' - export enum Status { DRAFT = 'DRAFT', PUBLISHED = 'PUBLISHED' @@ -178,10 +181,4 @@ export namespace VigilanceArea { OTHER = 'OTHER', INTERNAL = 'INTERNAL' } - - export enum VigilanceAreaSourceTypeLabel { - CONTROL_UNIT = 'Unité', - OTHER = 'Autre', - INTERNAL = 'Interne CACEM' - } } diff --git a/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts b/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts new file mode 100644 index 0000000000..4b633f5c99 --- /dev/null +++ b/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts @@ -0,0 +1,31 @@ +import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' +import { VigilanceArea } from '@features/VigilanceArea/types' + +export function getFilterInformativeVigilanceArea( + typeFilter: VigilanceArea.VigilanceAreaFilterType[] | undefined, + vigilanceArea: VigilanceArea.VigilanceArea +) { + return ( + (typeFilter?.includes(VigilanceArea.VigilanceAreaFilterType.NONE) && (vigilanceArea.periods ?? []).length === 0) === + true + ) +} + +export function isVigilanceAreaPartOfType( + vigilanceArea: VigilanceArea.VigilanceArea, + typeFilter?: VigilanceArea.VigilanceAreaFilterType[] +): boolean { + if (!typeFilter || typeFilter.length === 0) { + return true + } + + const filterSimpleVigilanceArea = + typeFilter.includes(VigilanceArea.VigilanceAreaFilterType.SIMPLE) && isWithinPeriod(vigilanceArea.periods, false) + + const filterInformativeVigilanceArea = getFilterInformativeVigilanceArea(typeFilter, vigilanceArea) + + const filterCriticalVigilanceArea = + typeFilter.includes(VigilanceArea.VigilanceAreaFilterType.CRITICAL) && isWithinPeriod(vigilanceArea.periods, true) + + return filterSimpleVigilanceArea || filterCriticalVigilanceArea || filterInformativeVigilanceArea +} diff --git a/frontend/src/features/layersSelector/myVigilanceAreas/MyVigilanceAreaLayerZone.tsx b/frontend/src/features/layersSelector/myVigilanceAreas/MyVigilanceAreaLayerZone.tsx index e57a95a2ea..55320276ea 100644 --- a/frontend/src/features/layersSelector/myVigilanceAreas/MyVigilanceAreaLayerZone.tsx +++ b/frontend/src/features/layersSelector/myVigilanceAreas/MyVigilanceAreaLayerZone.tsx @@ -1,5 +1,6 @@ import { useGetVigilanceAreasQuery } from '@api/vigilanceAreasAPI' import { StyledTransparentButton } from '@components/style' +import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { vigilanceAreaActions } from '@features/VigilanceArea/slice' import { useAppSelector } from '@hooks/useAppSelector' import { Accent, Icon, IconButton, OPENLAYERS_PROJECTION, Size, THEME, WSG84_PROJECTION } from '@mtes-mct/monitor-ui' @@ -76,7 +77,8 @@ export function MyVigilanceAreaLayerZone({ )} + + + + Ce champ est utilisé uniquement comme critère de recherche pour les zones de vigilance. + {(filteredRegulatoryTags.length > 0 || filteredAmpTypes?.length > 0 || diff --git a/frontend/src/features/layersSelector/search/ResultsList/VigilanceAreaLayer/index.tsx b/frontend/src/features/layersSelector/search/ResultsList/VigilanceAreaLayer/index.tsx index d7bdb1ebd7..20d913ac11 100644 --- a/frontend/src/features/layersSelector/search/ResultsList/VigilanceAreaLayer/index.tsx +++ b/frontend/src/features/layersSelector/search/ResultsList/VigilanceAreaLayer/index.tsx @@ -1,4 +1,5 @@ import { StyledTransparentButton } from '@components/style' +import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { vigilanceAreaActions } from '@features/VigilanceArea/slice' import { useTracking } from '@hooks/useTracking' import { Accent, Icon, IconButton, OPENLAYERS_PROJECTION, THEME, WSG84_PROJECTION } from '@mtes-mct/monitor-ui' @@ -97,7 +98,8 @@ export function VigilanceAreaLayer({ layer, searchedText }: RegulatoryLayerProps > + return ( + + ) case MonitorEnvLayers.LOCALIZED_AREAS: return default: diff --git a/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts b/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts index b065968bb5..c285276f56 100644 --- a/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts +++ b/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts @@ -1,4 +1,5 @@ import { VigilanceArea } from '@features/VigilanceArea/types' +import { getFilterInformativeVigilanceArea } from '@features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType' import { customDayjs } from '@mtes-mct/monitor-ui' import type { Dayjs } from 'dayjs' @@ -98,6 +99,7 @@ export const getFilterVigilanceAreasPerPeriod = ( vigilanceAreas: (VigilanceArea.VigilanceAreaLayer | VigilanceArea.VigilanceAreaFromApi)[], periodFilter: VigilanceArea.VigilanceAreaFilterPeriod | undefined, vigilanceAreaSpecificPeriodFilter?: string[], + vigilanceAreaTypeFilter?: VigilanceArea.VigilanceAreaFilterType[], isSuperUser: boolean = true ): VigilanceArea.VigilanceAreaLayer[] => { const { endDate: endDateFilter, startDate: startDateFilter } = calculatePeriodBounds( @@ -113,18 +115,22 @@ export const getFilterVigilanceAreasPerPeriod = ( if (!isSuperUser && (vigilanceArea.isDraft || vigilanceArea.visibility === VigilanceArea.Visibility.PRIVATE)) { return false } - if (!vigilanceArea.periods || !vigilanceArea) { + if (!vigilanceArea) { return false } - if (vigilanceArea.periods.some(period => period.isAtAllTimes)) { + if (getFilterInformativeVigilanceArea(vigilanceAreaTypeFilter, vigilanceArea)) { return true } - if (vigilanceArea.periods.every(period => !period.startDatePeriod || !period.endDatePeriod)) { + if (vigilanceArea.periods?.some(period => period.isAtAllTimes)) { + return true + } + + if (vigilanceArea.periods?.every(period => !period.startDatePeriod || !period.endDatePeriod)) { return false } - return vigilanceArea.periods.some(period => { + return vigilanceArea.periods?.some(period => { const startDate = customDayjs(period.startDatePeriod).utc() const endDate = customDayjs(period.endDatePeriod).utc() From df7b77aa87b460eabafea4698afccc3d934f1155 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Tue, 6 Jan 2026 17:08:05 +0100 Subject: [PATCH 05/12] feat: rework vigilance area list table --- .../components/VigilanceAreaForm/Period.tsx | 4 +- .../components/VigilanceAreaForm/Periods.tsx | 27 +++++-- .../VigilanceAreasList/Cells/PeriodCell.tsx | 30 +++++++ .../VigilanceAreasList/Cells/PeriodsCell.tsx | 50 ++++++++++++ .../VigilanceAreasList/Columns/index.tsx | 78 +++++++------------ frontend/src/features/VigilanceArea/utils.ts | 21 +++++ .../overlays/OverlayContent.tsx | 37 ++------- .../getFilteredVigilanceAreasPerPeriod.ts | 17 ++-- 8 files changed, 165 insertions(+), 99 deletions(-) create mode 100644 frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx create mode 100644 frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx index e8b1ddb309..e108a14d03 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx @@ -1,7 +1,7 @@ import { CheckboxWrapper, CriticalCheckbox, - StyledCircle + PeriodCircle } from '@features/VigilanceArea/components/VigilanceAreaForm/Periods' import { PublishedVigilanceAreaPeriodSchema } from '@features/VigilanceArea/components/VigilanceAreaForm/Schema' import { ValidateButton } from '@features/VigilanceArea/components/VigilanceAreaForm/style' @@ -120,7 +120,7 @@ export function Period({ hasError, index, initialPeriod, onValidate, remove }: P onChange={setPeriod} /> - + + Type de période de vigilance en tout temps - + - + ` +export const BasePeriodCircle = styled.div<{ $isCritical?: boolean }>` height: 10px; width: 10px; - margin-right: 6px; background-color: ${p => (p.$isCritical ? 'rgba(194, 81, 65, 0.75)' : 'rgba(194, 81, 65, 0.5)')}; border: ${p => (p.$isCritical ? '2px solid #E1000F' : '1px solid rgba(147, 63, 32, 0.75)')}; border-radius: 50%; display: inline-block; +` + +export const PeriodCircle = styled(BasePeriodCircle)` + margin-right: 6px; position: absolute; left: 22px; top: 0; transform: translateY(50%); ` + +const HiddenLegend = styled(Legend)` + width: 1px; + height: 1px; + padding: 0; + margin: 0; + overflow: hidden; + position: absolute; + left: -10000px; +}` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx new file mode 100644 index 0000000000..7712885034 --- /dev/null +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx @@ -0,0 +1,30 @@ +import { Tooltip } from '@components/Tooltip' +import { StyledPeriodCircle } from '@features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell' +import { VigilanceArea } from '@features/VigilanceArea/types' +import { computeVigilanceAreaPeriod, endingOccurenceText, frequencyText } from '@features/VigilanceArea/utils' +import { Icon } from '@mtes-mct/monitor-ui' +import styled from 'styled-components' + +export function PeriodCell({ period }: { period: VigilanceArea.VigilanceAreaPeriod | undefined }) { + return ( + + {!period?.isCritical && } + {period?.isCritical && } + {computeVigilanceAreaPeriod(period, false)} + {period?.frequency && ( + + {[ + frequencyText(period?.frequency, false), + endingOccurenceText(period?.endingCondition, period?.computedEndDate, false) + ].join(', ')} + + )} + + ) +} + +const StyledCell = styled.span` + display: flex; + align-items: center; + gap: 8px; +` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx new file mode 100644 index 0000000000..7b8a2516a7 --- /dev/null +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx @@ -0,0 +1,50 @@ +import { Tooltip } from '@components/Tooltip' +import { BasePeriodCircle } from '@features/VigilanceArea/components/VigilanceAreaForm/Periods' +import { PeriodCell } from '@features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell' +import { VigilanceArea } from '@features/VigilanceArea/types' +import styled from 'styled-components' + +export function PeriodsCell({ periods }: { periods: VigilanceArea.VigilanceAreaPeriod[] | undefined }) { + if (!periods || periods.length === 0) { + return - + } + const hasSimplePeriod = periods.some(period => !period.isCritical) + const hasCriticalPeriod = periods.some(period => period.isCritical) + + return ( + + {periods.length > 1 && ( + +
+ {hasSimplePeriod && } + {hasCriticalPeriod && } +
+ Périodes multiples{' '} + + {periods.map(period => ( + + ))} + +
+ )} + {periods.length === 1 && } +
+ ) +} + +const StyledCell = styled.span` + display: flex; + align-items: center; + gap: 8px; +` + +export const StyledPeriodCircle = styled(BasePeriodCircle)` + margin-right: 4px; +` + +const StyledTooltip = styled(Tooltip)` + display: flex; + flex-direction: column; + justify-content: center; + gap: 8px; +` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx index 8a2b28aad5..b694cdb347 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx @@ -1,9 +1,7 @@ -import { DateCell } from '@components/Table/DateCell' import { StyledSkeletonRow } from '@features/commonComponents/Skeleton' -import { VigilanceArea } from '@features/VigilanceArea/types' +import { PeriodsCell } from '@features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell' import { EditCell } from '../Cells/EditCell' -import { FrequencyCell } from '../Cells/FrequencyCell' import { HighlightCell } from '../Cells/HighlightCell' import { LocalizeCell } from '../Cells/LocalizeCell' import { StatusCell } from '../Cells/StatusCell' @@ -11,62 +9,30 @@ import { TagsCell } from '../Cells/TagsCell' import { ValidationDateCell } from '../Cells/ValidationDateCell' import { VisibilityCell } from '../Cells/VisibilityCell' -import type { Row } from '@tanstack/react-table' - export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = false) => [ - { - accessorFn: row => row.startDatePeriod, - cell: info => (isFetching ? : ), - enableSorting: true, - header: () => 'Début', - id: 'startDatePeriod', - size: 90 + legacyFirefoxOffset - }, - { - accessorFn: row => row.endDatePeriod, - cell: info => (isFetching ? : ), - enableSorting: true, - header: () => 'Fin', - id: 'endDatePeriod', - size: 90 + legacyFirefoxOffset - }, - { - accessorFn: row => row.frequency, - cell: info => (isFetching ? : ), - enableSorting: true, - header: () => 'Récurrence', - id: 'frequency', - size: 106 + legacyFirefoxOffset, - sortingFn: (rowA: Row, rowB: Row) => { - const labelA = VigilanceArea.FrequencyLabelForList[rowA.original.frequency] ?? '' - const labelB = VigilanceArea.FrequencyLabelForList[rowB.original.frequency] ?? '' - - return labelA.localeCompare(labelB) - } - }, - { - accessorFn: row => row.validatedAt, - cell: info => (isFetching ? : ), - enableSorting: true, - header: () => 'Validée le', - id: 'validatedAt', - size: 96 + legacyFirefoxOffset - }, { accessorFn: row => row.name, cell: info => (isFetching ? : ), enableSorting: true, header: () => 'Nom de la zone', id: 'name', - size: 270 + legacyFirefoxOffset + size: 250 + legacyFirefoxOffset + }, + { + accessorFn: row => row.periods, + cell: info => (isFetching ? : ), + enableSorting: true, + header: () => 'Période(s) de vigilance', + id: 'periods', + size: 220 + legacyFirefoxOffset }, { accessorFn: row => row.tags, cell: info => (isFetching ? : ), enableSorting: true, header: () => 'Tags', - id: 'themes', - size: 260 + legacyFirefoxOffset + id: 'tags', + size: 230 + legacyFirefoxOffset }, { accessorFn: row => row.comments, @@ -74,7 +40,7 @@ export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = f enableSorting: true, header: () => 'Commentaire', id: 'comments', - size: 310 + legacyFirefoxOffset + size: 330 + legacyFirefoxOffset }, { accessorFn: row => row.seaFront, @@ -85,11 +51,11 @@ export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = f size: 100 + legacyFirefoxOffset }, { - accessorFn: row => row.visibility, - cell: info => (isFetching ? : ), + accessorFn: row => row.validatedAt, + cell: info => (isFetching ? : ), enableSorting: true, - header: () => 'Visibilité', - id: 'visibility', + header: () => 'Validée le', + id: 'validatedAt', size: 100 + legacyFirefoxOffset }, { @@ -106,7 +72,15 @@ export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = f enableSorting: true, header: () => 'Créée par', id: 'createdBy', - size: 97 + legacyFirefoxOffset + size: 95 + legacyFirefoxOffset + }, + { + accessorFn: row => row.visibility, + cell: info => (isFetching ? : ), + enableSorting: true, + header: () => 'Visibilité', + id: 'visibility', + size: 105 + legacyFirefoxOffset }, { accessorFn: row => row.geom, diff --git a/frontend/src/features/VigilanceArea/utils.ts b/frontend/src/features/VigilanceArea/utils.ts index d53f9a9b8c..a5213f1159 100644 --- a/frontend/src/features/VigilanceArea/utils.ts +++ b/frontend/src/features/VigilanceArea/utils.ts @@ -50,3 +50,24 @@ export const frequencyText = (frequency?: VigilanceArea.Frequency, capitalize = } const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1) + +export function computeVigilanceAreaPeriod( + period: VigilanceArea.VigilanceAreaPeriod | undefined, + withReccurenceText = true +) { + if (period?.isAtAllTimes) { + return 'En tout temps' + } + if (period?.startDatePeriod) { + return `${[ + `${period?.startDatePeriod ? `Du ${customDayjs(period?.startDatePeriod).utc().format('DD/MM/YYYY')}` : ''} + ${period?.endDatePeriod ? `au ${customDayjs(period?.endDatePeriod).utc().format('DD/MM/YYYY')}` : ''}`, + withReccurenceText ? frequencyText(period?.frequency, false) : '', + withReccurenceText ? endingOccurenceText(period?.endingCondition, period?.computedEndDate, false) : '' + ] + .filter(Boolean) + .join(', ')}` + } + + return '' +} diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index eb1c6c222a..2dfbeded63 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -7,10 +7,10 @@ import { vigilanceAreaActions } from '@features/VigilanceArea/slice' import { VigilanceArea } from '@features/VigilanceArea/types' -import { endingOccurenceText, frequencyText } from '@features/VigilanceArea/utils' +import { computeVigilanceAreaPeriod } from '@features/VigilanceArea/utils' import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' -import { Accent, customDayjs, Icon, IconButton, Size, THEME } from '@mtes-mct/monitor-ui' +import { Accent, Icon, IconButton, Size, THEME } from '@mtes-mct/monitor-ui' import { MonitorEnvLayers, type RegulatoryOrAMPOrViglanceAreaLayerType } from 'domain/entities/layers/constants' import { type RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import { layerSidebarActions } from 'domain/shared_slices/LayerSidebar' @@ -64,31 +64,6 @@ function isVigilanceAreaLayer(type: RegulatoryOrAMPOrViglanceAreaLayerType) { ) } -function computeVigilanceAreaPeriod(properties: VigilanceArea.VigilanceAreaProperties) { - const { periods } = properties - if (!periods || periods.length === 0) { - return '' - } - const firstPeriod = periods[0] - if (firstPeriod?.isAtAllTimes) { - return 'En tout temps' - } - if (firstPeriod?.startDatePeriod) { - return `${[ - `${ - firstPeriod?.startDatePeriod ? `Du ${customDayjs(firstPeriod?.startDatePeriod).utc().format('DD/MM/YYYY')}` : '' - } - ${firstPeriod?.endDatePeriod ? `au ${customDayjs(firstPeriod?.endDatePeriod).utc().format('DD/MM/YYYY')}` : ''}`, - frequencyText(firstPeriod?.frequency, false), - endingOccurenceText(firstPeriod?.endingCondition, firstPeriod?.computedEndDate, false) - ] - .filter(Boolean) - .join(', ')}` - } - - return '' -} - export function OverlayContent({ items }: OverlayContentProps) { const dispatch = useAppDispatch() @@ -211,8 +186,10 @@ export function OverlayContent({ items }: OverlayContentProps) { const isDisabled = id !== isolatedLayer?.id && !!isolatedLayer?.id const vigilanceAreaPeriod = isVigilanceArea - ? computeVigilanceAreaPeriod(item.properties as VigilanceArea.VigilanceAreaProperties) - : '' + ? (item.properties as VigilanceArea.VigilanceAreaProperties).periods?.map(period => + computeVigilanceAreaPeriod(period) + ) + : [] const isArchived = (item.properties as VigilanceArea.VigilanceAreaProperties)?.isArchived ?? false @@ -284,7 +261,7 @@ export function OverlayContent({ items }: OverlayContentProps) { )} - {items.length === 1 && isVigilanceArea && {vigilanceAreaPeriod}} + {items.length === 1 && isVigilanceArea && vigilanceAreaPeriod?.map(period => {period})} ) })} diff --git a/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts b/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts index c285276f56..c0a18e6328 100644 --- a/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts +++ b/frontend/src/features/layersSelector/utils/getFilteredVigilanceAreasPerPeriod.ts @@ -112,17 +112,16 @@ export const getFilterVigilanceAreasPerPeriod = ( } return Object.values((vigilanceAreas as Array) ?? []).filter(vigilanceArea => { - if (!isSuperUser && (vigilanceArea.isDraft || vigilanceArea.visibility === VigilanceArea.Visibility.PRIVATE)) { + if ( + !vigilanceArea || + (!isSuperUser && (vigilanceArea.isDraft || vigilanceArea.visibility === VigilanceArea.Visibility.PRIVATE)) + ) { return false } - if (!vigilanceArea) { - return false - } - if (getFilterInformativeVigilanceArea(vigilanceAreaTypeFilter, vigilanceArea)) { - return true - } - - if (vigilanceArea.periods?.some(period => period.isAtAllTimes)) { + if ( + getFilterInformativeVigilanceArea(vigilanceAreaTypeFilter, vigilanceArea) || + vigilanceArea.periods?.some(period => period.isAtAllTimes) + ) { return true } From 39909ea93d4ecda3fe089c989d6e06abb2455159 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Tue, 6 Jan 2026 17:30:55 +0100 Subject: [PATCH 06/12] tech: remove is_archived from vigilance area --- .../vigilanceArea/VigilanceAreaEntity.kt | 1 - .../ArchiveOutdatedVigilanceAreas.kt | 21 -------------- .../vigilanceArea/VigilanceAreaDataInput.kt | 2 -- .../vigilanceArea/VigilanceAreaDataOutput.kt | 2 -- .../vigilanceArea/VigilanceAreasDataOutput.kt | 2 -- .../database/model/VigilanceAreaModel.kt | 3 -- .../V0.200__create_vigilance_area_period.sql | 3 +- .../ArchiveOutdatedVigilanceAreasUTest.kt | 29 ------------------- .../VigilanceAreaForm/Planning/utils.test.ts | 24 ++++++++++----- .../components/VigilanceAreaForm/utils.ts | 1 - frontend/src/features/VigilanceArea/types.ts | 2 -- .../overlays/OverlayContent.tsx | 4 +-- ...getFilteredVigilanceAreasPerPeriod.test.ts | 13 ++------- 13 files changed, 22 insertions(+), 85 deletions(-) delete mode 100644 backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt delete mode 100644 backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreasUTest.kt diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt index ad2c0a8d3f..055b94d353 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/vigilanceArea/VigilanceAreaEntity.kt @@ -11,7 +11,6 @@ data class VigilanceAreaEntity( val createdBy: String? = null, val geom: MultiPolygon? = null, val images: List? = listOf(), - val isArchived: Boolean, val isDeleted: Boolean, val isDraft: Boolean, val links: List? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt deleted file mode 100644 index 198d6c684f..0000000000 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreas.kt +++ /dev/null @@ -1,21 +0,0 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea - -import fr.gouv.cacem.monitorenv.config.UseCase -import fr.gouv.cacem.monitorenv.domain.repositories.IVigilanceAreaRepository -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -@UseCase -class ArchiveOutdatedVigilanceAreas( - private val vigilanceAreaRepository: IVigilanceAreaRepository, -) { - private val logger: Logger = LoggerFactory.getLogger(ArchiveOutdatedVigilanceAreas::class.java) - - // At every 6 hours, after 1 minute of initial delay -// @Scheduled(fixedDelay = 21600000, initialDelay = 6000) - fun execute() { - logger.info("Attempt to ARCHIVE vigilance areas") - val numberOfArchivedVigilanceAreas = vigilanceAreaRepository.archiveOutdatedVigilanceAreas() - logger.info("$numberOfArchivedVigilanceAreas vigilance areas archived") - } -} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt index 3e1e1eb2cb..494d1e6af2 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/inputs/vigilanceArea/VigilanceAreaDataInput.kt @@ -13,7 +13,6 @@ data class VigilanceAreaDataInput( val comments: String? = null, val createdBy: String? = null, val geom: MultiPolygon? = null, - val isArchived: Boolean, val isDraft: Boolean, val images: List? = listOf(), val links: List? = null, @@ -36,7 +35,6 @@ data class VigilanceAreaDataInput( comments = this.comments, createdBy = this.createdBy, geom = this.geom, - isArchived = this.isArchived, isDeleted = false, isDraft = this.isDraft, images = this.images?.map { image -> image.toImageEntity() }, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt index 6d81d3f244..a5c7beffb7 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreaDataOutput.kt @@ -15,7 +15,6 @@ data class VigilanceAreaDataOutput( val comments: String? = null, val createdBy: String? = null, val geom: MultiPolygon? = null, - val isArchived: Boolean, val isDraft: Boolean, val images: List = mutableListOf(), val links: List? = null, @@ -39,7 +38,6 @@ data class VigilanceAreaDataOutput( comments = vigilanceArea.comments, createdBy = vigilanceArea.createdBy, geom = vigilanceArea.geom, - isArchived = vigilanceArea.isArchived, isDraft = vigilanceArea.isDraft, images = vigilanceArea.images?.map { VigilanceAreaImageDataOutput.fromVigilanceAreaImage(it) } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt index 5a3d005a52..95876943b5 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/vigilanceArea/VigilanceAreasDataOutput.kt @@ -15,7 +15,6 @@ data class VigilanceAreasDataOutput( val comments: String? = null, val createdBy: String? = null, val geom: MultiPolygon? = null, - val isArchived: Boolean, val isDraft: Boolean, val links: List? = null, val linkedAMPs: List? = listOf(), @@ -36,7 +35,6 @@ data class VigilanceAreasDataOutput( comments = vigilanceArea.comments, createdBy = vigilanceArea.createdBy, geom = vigilanceArea.geom, - isArchived = vigilanceArea.isArchived, isDraft = vigilanceArea.isDraft, links = vigilanceArea.links, linkedAMPs = vigilanceArea.linkedAMPs, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt index 7ea873ef93..3ddbfda3f7 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/VigilanceAreaModel.kt @@ -57,7 +57,6 @@ data class VigilanceAreaModel( ) @OrderBy("id") var images: MutableList = mutableListOf(), - @Column(name = "is_archived", nullable = false) val isArchived: Boolean, @Column(name = "is_deleted", nullable = false) val isDeleted: Boolean, @Column(name = "is_draft") val isDraft: Boolean, @Column(name = "links", columnDefinition = "jsonb") @@ -97,7 +96,6 @@ data class VigilanceAreaModel( comments = vigilanceArea.comments, createdBy = vigilanceArea.createdBy, geom = vigilanceArea.geom, - isArchived = vigilanceArea.isArchived, isDeleted = vigilanceArea.isDeleted, isDraft = vigilanceArea.isDraft, links = vigilanceArea.links, @@ -125,7 +123,6 @@ data class VigilanceAreaModel( comments = comments, createdBy = createdBy, geom = geom, - isArchived = isArchived, isDeleted = isDeleted, isDraft = isDraft, images = images.map { it.toVigilanceAreaImage() }, diff --git a/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql b/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql index 492056e57c..b2ae6eb646 100644 --- a/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql +++ b/backend/src/main/resources/db/migration/internal/V0.200__create_vigilance_area_period.sql @@ -36,4 +36,5 @@ ALTER TABLE VIGILANCE_AREAS DROP COLUMN FREQUENCY, DROP COLUMN START_DATE_PERIOD, DROP COLUMN COMPUTED_END_DATE, - DROP COLUMN IS_AT_ALL_TIMES; \ No newline at end of file + DROP COLUMN IS_AT_ALL_TIMES, + DROP COLUMN IS_ARCHIVED; \ No newline at end of file diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreasUTest.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreasUTest.kt deleted file mode 100644 index 8279f7ac39..0000000000 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/ArchiveOutdatedVigilanceAreasUTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.vigilanceArea - -import com.nhaarman.mockitokotlin2.given -import fr.gouv.cacem.monitorenv.domain.repositories.IVigilanceAreaRepository -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.Mockito.mock -import org.springframework.boot.test.system.CapturedOutput -import org.springframework.boot.test.system.OutputCaptureExtension - -@ExtendWith(OutputCaptureExtension::class) -class ArchiveOutdatedVigilanceAreasUTest { - private val vigilanceAreaRepository: IVigilanceAreaRepository = mock() - private val archiveOutdatedVigilanceAreas = ArchiveOutdatedVigilanceAreas(vigilanceAreaRepository) - - @Test - fun `execute should archive vigilance areas`(log: CapturedOutput) { - // Given - given(vigilanceAreaRepository.archiveOutdatedVigilanceAreas()).willReturn(2) - - // When - archiveOutdatedVigilanceAreas.execute() - - // Then - assertThat(log.out).contains("Attempt to ARCHIVE vigilance areas") - assertThat(log.out).contains("2 vigilance areas archived") - } -} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/utils.test.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/utils.test.ts index d41017cc12..85ab98d0b0 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/utils.test.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/utils.test.ts @@ -8,7 +8,6 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma jest.useFakeTimers().setSystemTime(new Date('2024-05-13T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -21,12 +20,15 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma const occurenceDates = computeOccurenceWithinCurrentYear(vigilanceArea.periods![0]!) jest.useRealTimers() expect(occurenceDates).toEqual([ - { end: customDayjs('2024-12-31T23:59:59.999Z').utc(), start: customDayjs('2024-01-01T00:00:00.000Z').utc() } + { + end: customDayjs('2024-12-31T23:59:59.999Z').utc(), + isCritical: undefined, + start: customDayjs('2024-01-01T00:00:00.000Z').utc() + } ]) }) it('it should return empty when there is no starting date', () => { const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -41,8 +43,8 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma }) it('it should return startDatePeriod and endDatePeriod when frequency is NONE', () => { + jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -56,9 +58,11 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma seaFront: undefined } const occurenceDates = computeOccurenceWithinCurrentYear(vigilanceArea.periods![0]!) + jest.useRealTimers() expect(occurenceDates).toEqual([ { end: customDayjs('2025-04-19T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-01T00:00:00.000Z').utc() } ]) @@ -66,7 +70,6 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma it('it should return date range of the current year when frequency is ALL_YEARS', () => { jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -85,6 +88,7 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma expect(occurenceDates).toEqual([ { end: customDayjs('2025-04-19T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-01T00:00:00.000Z').utc() } ]) @@ -92,7 +96,6 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma it('it should return date range of the current year when frequency is ALL_WEEKS with occurences number', () => { jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -112,26 +115,32 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma expect(occurenceDates).toEqual([ { end: customDayjs('2025-01-06T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-05T00:00:00.000Z').utc() }, { end: customDayjs('2025-01-13T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-12T00:00:00.000Z').utc() }, { end: customDayjs('2025-01-20T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-19T00:00:00.000Z').utc() }, { end: customDayjs('2025-01-27T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-26T00:00:00.000Z').utc() }, { end: customDayjs('2025-02-03T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-02-02T00:00:00.000Z').utc() }, { end: customDayjs('2025-02-10T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-02-09T00:00:00.000Z').utc() } ]) @@ -140,7 +149,6 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma it('it should return empty when frequency is ALL_WEEKS out of the current year', () => { jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -162,7 +170,6 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma it('it should return date range until the given date when frequency is ALL_WEEKS and ending is a date', () => { jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00.000Z')) const vigilanceArea: VigilanceArea.VigilanceArea = { - isArchived: false, isDraft: false, name: undefined, periods: [ @@ -182,6 +189,7 @@ describe('computeOccurenceWithinCurrentYear should return all occurences that ma expect(occurenceDates).toEqual([ { end: customDayjs('2025-01-06T00:00:00.000Z').utc(), + isCritical: undefined, start: customDayjs('2025-01-05T00:00:00.000Z').utc() } ]) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts index 03c0a9304c..be0c519293 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts @@ -10,7 +10,6 @@ export function getVigilanceAreaInitialValues(): Omit { const todayMin2Days = { createdAt: undefined, id: 1, - isArchived: false, isAtAllTimes: false, isDraft: false, name: 'todayMin2Days', @@ -30,7 +29,6 @@ describe('filterVigilanceAreas', () => { const today = { createdAt: undefined, id: 2, - isArchived: false, isAtAllTimes: false, isDraft: false, name: 'Today', @@ -52,7 +50,6 @@ describe('filterVigilanceAreas', () => { const quarter = { createdAt: undefined, id: 3, - isArchived: false, isDraft: false, name: 'Quarter', periods: [ @@ -73,7 +70,6 @@ describe('filterVigilanceAreas', () => { const outsideFilteredDate = { createdAt: undefined, id: 4, - isArchived: false, isDraft: false, name: 'OutsideFilteredDate', periods: [ @@ -98,7 +94,6 @@ describe('filterVigilanceAreas', () => { const year = { createdAt: undefined, id: 5, - isArchived: false, isDraft: false, name: 'Year', periods: [ @@ -119,7 +114,6 @@ describe('filterVigilanceAreas', () => { createdAt: undefined, id: 6, - isArchived: false, isDraft: false, name: 'allYear', periods: [ @@ -141,7 +135,6 @@ describe('filterVigilanceAreas', () => { const infinite = { createdAt: undefined, id: 7, - isArchived: false, isDraft: false, name: 'Infinite', periods: [ @@ -163,7 +156,6 @@ describe('filterVigilanceAreas', () => { const last3Months = { createdAt: undefined, id: 8, - isArchived: false, isDraft: false, name: 'Last 3 months', periods: [ @@ -185,7 +177,6 @@ describe('filterVigilanceAreas', () => { const last12Months = { createdAt: undefined, id: 9, - isArchived: false, isAtAllTimes: false, isDraft: false, name: 'Last 12 months', @@ -214,12 +205,12 @@ describe('filterVigilanceAreas', () => { it('filters areas within current quarter', () => { const result = getFilterVigilanceAreasPerPeriod(areas, VigilanceArea.VigilanceAreaFilterPeriod.CURRENT_QUARTER) - expect(result).toEqual([todayMin2Days, today, quarter, year, allYear, infinite, last3Months]) + expect(result).toEqual([todayMin2Days, today, quarter, year, allYear, infinite]) }) it('filters areas within current year', () => { const result = getFilterVigilanceAreasPerPeriod(areas, VigilanceArea.VigilanceAreaFilterPeriod.CURRENT_YEAR) - expect(result).toEqual([todayMin2Days, today, quarter, year, allYear, infinite, last3Months, last12Months]) + expect(result).toEqual([todayMin2Days, today, quarter, year, allYear, infinite]) }) it('filters areas within next three months', () => { From c0fa108a3023d96872e2cc11db6951f9517882aa Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Tue, 6 Jan 2026 17:45:15 +0100 Subject: [PATCH 07/12] feat: add cypress test --- .../testdata/V666.16__insert_dummy_vigilance_area.sql | 5 +++-- .../side_window/vigilance_areas_list/filters.spec.ts | 11 +++++++++++ .../components/VigilanceAreaTypeFilter.tsx | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql b/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql index 7c5556a234..598d9ce85e 100644 --- a/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql +++ b/backend/src/main/resources/db/testdata/V666.16__insert_dummy_vigilance_area.sql @@ -44,9 +44,10 @@ $$ INSERT INTO vigilance_area_period(id, vigilance_areas_id, end_date_period, ending_condition, ending_occurrence_date, - ending_occurrence_number, frequency, start_date_period, computed_end_date) + ending_occurrence_number, frequency, start_date_period, computed_end_date, + is_critical) VALUES (uuid_generate_v4(), 1, today + INTERVAL '1 day', NULL, NULL, NULL, 'NONE', today - INTERVAL '1 day', - today + INTERVAL '1 day'); + today + INTERVAL '1 day', true); INSERT INTO vigilance_areas_source (id, vigilance_areas_id, control_unit_contacts_id, type, comments, is_anonymous) diff --git a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts index 221ea4ab99..5adf6d454c 100644 --- a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts @@ -85,4 +85,15 @@ context('Side Window > Vigilance Areas List > Filter Bar', () => { cy.fill('Publique', false) cy.getDataCy('vigilance-area-row').should('have.length', 2) }) + + it('Should filter vigilance areas by type', () => { + cy.fill('Type de zone de vigilance', ['Aucune période de vigilance en cours']) + cy.getDataCy('vigilance-area-row').should('have.length', 0) + + cy.fill('Type de zone de vigilance', ['Période de vigilance critique en cours']) + cy.getDataCy('vigilance-area-row').should('have.length', 1) + + cy.fill('Type de zone de vigilance', ['Période de vigilance simple en cours']) + cy.getDataCy('vigilance-area-row').should('have.length', 2) + }) }) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx index ecb831f5df..560394713a 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaTypeFilter.tsx @@ -27,7 +27,7 @@ export function VigilanceAreaTypeFilter({ style }: { style?: React.CSSProperties isCleanable isLabelHidden isTransparent - label="Période de vigilance" + label="Type de zone de vigilance" name="periodOfVigilanceArea" onChange={handleSetFilteredVigilanceType} options={vigilanceAreaTypeOptions} From 50c82814cdc1d46a605baa1818ddee39b280bf17 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Tue, 6 Jan 2026 18:00:27 +0100 Subject: [PATCH 08/12] fix: clean up and fix dashboard PDF --- .../VigilanceAreas/Panel/index.tsx | 43 +++-- .../components/Pdf/VigilanceAreas/index.tsx | 28 +-- .../VigilanceAreaForm/Frequency.tsx | 114 ------------ .../Panel/PanelPeriodAndThemes.tsx | 96 ----------- .../Panel/PanelThemesAndTags.tsx | 53 ++++++ .../VigilanceAreaForm/Panel/index.tsx | 4 +- .../components/VigilanceAreaForm/Periods.tsx | 162 +++++++++--------- .../VigilanceAreaForm/Planning/MonthBox.tsx | 18 +- .../Planning/PlanningBody.tsx | 2 +- .../VigilanceAreaForm/Planning/index.tsx | 2 +- .../components/VigilanceAreaForm/Schema.ts | 51 +++--- .../VigilanceAreaForm/Sources/index.tsx | 4 +- .../VigilanceAreasList/Cells/PeriodsCell.tsx | 2 +- .../VigilanceAreasList/Columns/index.tsx | 4 +- .../useCases/saveVigilanceArea.ts | 11 +- .../overlays/OverlayContent.tsx | 4 +- 16 files changed, 234 insertions(+), 364 deletions(-) delete mode 100644 frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx delete mode 100644 frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelPeriodAndThemes.tsx create mode 100644 frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelThemesAndTags.tsx diff --git a/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Panel/index.tsx b/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Panel/index.tsx index c780020835..95239070d5 100644 --- a/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Panel/index.tsx +++ b/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Panel/index.tsx @@ -6,7 +6,8 @@ import { PanelDates } from '@features/VigilanceArea/components/VigilanceAreaForm import { PanelImages } from '@features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelImages' import { PanelInternalCACEMSection } from '@features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelInternalCACEMSection' import { PanelLinks } from '@features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelLinks' -import { PanelPeriodAndThemes } from '@features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelPeriodAndThemes' +import { PanelThemesAndTags } from '@features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelThemesAndTags' +import { PlanningBody } from '@features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody' import { Header, PanelBody, @@ -38,24 +39,28 @@ export const Panel = forwardRef(({ layerId, ...props dispatch(dashboardActions.setDashboardPanel()) } + if (!vigilanceArea) { + return null + } + return ( // eslint-disable-next-line react/jsx-props-no-spreading
- {vigilanceArea?.name} + {vigilanceArea.name} - {vigilanceArea?.isDraft ? ( + {vigilanceArea.isDraft ? ( Brouillon @@ -76,21 +81,24 @@ export const Panel = forwardRef(({ layerId, ...props - - + + + + + - {vigilanceArea?.linkedRegulatoryAreas && vigilanceArea?.linkedRegulatoryAreas.length > 0 && ( + {vigilanceArea.linkedRegulatoryAreas && vigilanceArea.linkedRegulatoryAreas.length > 0 && ( )} - {vigilanceArea?.linkedAMPs && vigilanceArea?.linkedAMPs.length > 0 && ( + {vigilanceArea.linkedAMPs && vigilanceArea.linkedAMPs.length > 0 && ( )} - {vigilanceArea?.images && vigilanceArea?.images.length > 0 && ( - + {vigilanceArea.images && vigilanceArea?.images.length > 0 && ( + )} - {vigilanceArea?.links && vigilanceArea?.links.length > 0 && } - + {vigilanceArea.links && vigilanceArea.links.length > 0 && } + @@ -108,3 +116,8 @@ const Wrapper = styled.div` const StyledTitle = styled(Title)` max-width: 215px; ` + +const PlanningWrapper = styled.div` + padding: 16px; + border-bottom: 1px solid ${THEME.color.lightGray}; +` diff --git a/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx b/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx index 8d08325fe4..6def38971d 100644 --- a/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/VigilanceAreas/index.tsx @@ -125,19 +125,25 @@ export function VigilanceAreas({ - Périodes + Période(s) {vigilanceArea.periods?.map(period => ( - - - {formattedStartPeriod(period.startDatePeriod) - ? `Du ${formattedStartPeriod(period.startDatePeriod)} au ${formattedEndPeriod( - period.endDatePeriod - )}` - : EMPTY_VALUE} - - {frequencyText(period?.frequency)} - {endingOccurenceText(period?.endingCondition, period?.computedEndDate)} + + {period.isAtAllTimes ? ( + En tout temps + ) : ( + <> + + {formattedStartPeriod(period.startDatePeriod) + ? `Du ${formattedStartPeriod(period.startDatePeriod)} au ${formattedEndPeriod( + period.endDatePeriod + )}` + : EMPTY_VALUE} + + {frequencyText(period?.frequency)} + {endingOccurenceText(period?.endingCondition, period?.computedEndDate)} + + )} ))} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx deleted file mode 100644 index c474eac4b1..0000000000 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Frequency.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { VigilanceArea } from '@features/VigilanceArea/types' -import { FormikDatePicker, FormikNumberInput, getOptionsFromLabelledEnum, Select } from '@mtes-mct/monitor-ui' -import { useFormikContext } from 'formik' -import styled from 'styled-components' - -export function Frequency() { - const { errors, setFieldValue, values } = useFormikContext() - const frequencyOptions = getOptionsFromLabelledEnum(VigilanceArea.FrequencyLabel) - - const endingConditionOptions = getOptionsFromLabelledEnum(VigilanceArea.EndingConditionLabel) - - const updateFrequency = (nextFrequency: string | undefined) => { - setFieldValue('periods[0].frequency', nextFrequency) - if (nextFrequency === VigilanceArea.Frequency.NONE) { - setFieldValue('periods[0].endingCondition', undefined) - setFieldValue('periods[0].endingOccurrenceDate', undefined) - setFieldValue('periods[0].endingOccurrencesNumber', undefined) - } - } - - const updateEndingCondition = (nextEndingCondition: string | undefined) => { - setFieldValue('periods[0].endingCondition', nextEndingCondition) - if (nextEndingCondition === VigilanceArea.EndingCondition.NEVER) { - setFieldValue('periods[0].endingOccurrenceDate', undefined) - setFieldValue('periods[0].endingOccurrencesNumber', undefined) - } - } - - return ( - <> - - {values?.periods && - values.periods[0]?.endingCondition === VigilanceArea.EndingCondition.OCCURENCES_NUMBER && ( - - )} - {values?.periods && values.periods[0]?.endingCondition === VigilanceArea.EndingCondition.END_DATE && ( - - )} - - )} - - ) -} - -const FrequencyContainer = styled.div` - align-items: end; - display: flex; - flex-direction: row; - gap: 8px; -` - -const StyledFormikDatePicker = styled(FormikDatePicker)` - > .Element-Fieldset__InnerBox { - > .Field-DatePicker__CalendarPicker { - > .rs-picker-popup { - left: -130px !important; - } - } - } -` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelPeriodAndThemes.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelPeriodAndThemes.tsx deleted file mode 100644 index 44bffb2e1a..0000000000 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelPeriodAndThemes.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { EMPTY_VALUE } from '@features/VigilanceArea/constants' -import { VigilanceArea } from '@features/VigilanceArea/types' -import { endingOccurenceText, frequencyText } from '@features/VigilanceArea/utils' -import { customDayjs } from '@mtes-mct/monitor-ui' -import { displaySubTags, displayTags } from '@utils/getTagsAsOptions' -import { displaySubThemes, displayThemes } from '@utils/getThemesAsOptions' - -import { - PanelDateItem, - PanelInlineItem, - PanelInlineItemLabel, - PanelInlineItemValue, - PanelSubPart, - StyledPanelInlineItemValue -} from '../style' - -export function PanelPeriodAndThemes({ vigilanceArea }: { vigilanceArea: VigilanceArea.VigilanceArea | undefined }) { - const formattedStartPeriod = - vigilanceArea?.periods && vigilanceArea?.periods[0]?.startDatePeriod - ? customDayjs(vigilanceArea?.periods[0]?.startDatePeriod).utc().format('DD/MM/YYYY') - : undefined - const formattedEndPeriod = - vigilanceArea?.periods && vigilanceArea?.periods[0]?.endDatePeriod - ? customDayjs(vigilanceArea?.periods[0]?.endDatePeriod).utc().format('DD/MM/YYYY') - : undefined - - const subThemes = displaySubThemes(vigilanceArea?.themes) - const subTags = displaySubTags(vigilanceArea?.tags) - - return ( - <> - - - Période - - {vigilanceArea?.periods && vigilanceArea?.periods[0]?.isAtAllTimes ? ( - En tout temps - ) : ( - <> - - {formattedStartPeriod ? `Du ${formattedStartPeriod} au ${formattedEndPeriod}` : EMPTY_VALUE} - - - {frequencyText(vigilanceArea?.periods && vigilanceArea?.periods[0]?.frequency)} - - - {endingOccurenceText( - vigilanceArea?.periods && vigilanceArea?.periods[0]?.endingCondition, - vigilanceArea?.periods && vigilanceArea?.periods[0]?.computedEndDate - )} - - - )} - - - - Thématiques - - {vigilanceArea?.themes && vigilanceArea?.themes.length > 0 - ? displayThemes(vigilanceArea?.themes) - : EMPTY_VALUE} - - - {subThemes && ( - - Sous-thématiques - - {subThemes} - - - )} - - Tags - - {vigilanceArea?.tags && vigilanceArea?.tags.length > 0 ? displayTags(vigilanceArea?.tags) : EMPTY_VALUE} - - - {subTags && ( - - Sous-tags - - {subTags} - - - )} - - - Visibilité - - {vigilanceArea?.visibility ? VigilanceArea.VisibilityLabel[vigilanceArea?.visibility] : EMPTY_VALUE} - - - - - ) -} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelThemesAndTags.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelThemesAndTags.tsx new file mode 100644 index 0000000000..5047d2cd90 --- /dev/null +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/PanelThemesAndTags.tsx @@ -0,0 +1,53 @@ +import { EMPTY_VALUE } from '@features/VigilanceArea/constants' +import { VigilanceArea } from '@features/VigilanceArea/types' +import { displaySubTags, displayTags } from '@utils/getTagsAsOptions' +import { displaySubThemes, displayThemes } from '@utils/getThemesAsOptions' + +import { PanelInlineItem, PanelInlineItemLabel, PanelInlineItemValue, PanelSubPart } from '../style' + +export function PanelThemesAndTags({ vigilanceArea }: { vigilanceArea: VigilanceArea.VigilanceArea | undefined }) { + const subThemes = displaySubThemes(vigilanceArea?.themes) + const subTags = displaySubTags(vigilanceArea?.tags) + + return ( + + + Thématique(s) + + {vigilanceArea?.themes && vigilanceArea?.themes.length > 0 + ? displayThemes(vigilanceArea?.themes) + : EMPTY_VALUE} + + + {subThemes && ( + + Sous-thém. + + {subThemes} + + + )} + + Tag(s) + + {vigilanceArea?.tags && vigilanceArea?.tags.length > 0 ? displayTags(vigilanceArea?.tags) : EMPTY_VALUE} + + + {subTags && ( + + Sous-tag(s) + + {subTags} + + + )} + + + Visibilité + + {vigilanceArea?.visibility ? VigilanceArea.VisibilityLabel[vigilanceArea?.visibility] : EMPTY_VALUE} + + + + ) +} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/index.tsx index 67c307e187..e28f5684fb 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Panel/index.tsx @@ -20,7 +20,7 @@ import { PanelComments } from './PanelComments' import { PanelImages } from './PanelImages' import { PanelInternalCACEMSection } from './PanelInternalCACEMSection' import { PanelLinks } from './PanelLinks' -import { PanelPeriodAndThemes } from './PanelPeriodAndThemes' +import { PanelThemesAndTags } from './PanelThemesAndTags' import { AMPList } from '../AddAMPs/AMPList' import { RegulatoryAreas } from '../AddRegulatoryAreas/RegulatoryAreas' import { @@ -118,7 +118,7 @@ export function VigilanceAreaPanel({ vigilanceArea }: { vigilanceArea: Vigilance )} - + {values?.linkedRegulatoryAreas && values?.linkedRegulatoryAreas.length > 0 && ( diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx index 9276d23a5b..b6217726f0 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx @@ -37,89 +37,87 @@ export function Periods() { } return ( - <> - { - const hasSimpleIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && !period.isCritical) - const hasCriticalIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && period.isCritical) + { + const hasSimpleIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && !period.isCritical) + const hasCriticalIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && period.isCritical) - return ( - - - Type de période de vigilance en tout temps - - - setIsAtAllTimes(isChecked, false, push, remove)} - /> - - - - setIsAtAllTimes(isChecked, true, push, remove)} - /> - - - {periods - .filter((period: VigilanceArea.VigilanceAreaPeriod) => !period.isAtAllTimes) - .map((period: VigilanceArea.VigilanceAreaPeriod) => { - const index = field.value.indexOf(period) + return ( + + + Type de période de vigilance en tout temps + + + setIsAtAllTimes(isChecked, false, push, remove)} + /> + + + + setIsAtAllTimes(isChecked, true, push, remove)} + /> + + + {periods + .filter((period: VigilanceArea.VigilanceAreaPeriod) => !period.isAtAllTimes) + .map((period: VigilanceArea.VigilanceAreaPeriod) => { + const index = field.value.indexOf(period) - return ( - { - remove(index) - push(vigilanceAreaPeriod) - }} - remove={remove} - /> - ) - })} - - - - ) - }} - validateOnChange={false} - /> - + return ( + { + remove(index) + push(vigilanceAreaPeriod) + }} + remove={remove} + /> + ) + })} + + + + ) + }} + validateOnChange={false} + /> ) } @@ -174,4 +172,4 @@ const HiddenLegend = styled(Legend)` overflow: hidden; position: absolute; left: -10000px; -}` +` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx index 7c7544317f..b418b1f9a0 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx @@ -66,7 +66,11 @@ export function MonthBox({ dateRanges, label, monthIndex }: MonthBoxProps) { ))} {isCurrentMonth && } - {isCurrentMonth && } + {isCurrentMonth && ( + + + + )} ) @@ -76,6 +80,12 @@ const Wrapper = styled.div` position: relative; ` +const IconWrapper = styled.div` + display: flex; + width: 100%; + justify-content: center; +` + const Label = styled.span<{ $isBold: boolean }>` ${p => p.$isBold && `font-weight: 700; color:${p.theme.color.charcoal}`} ` @@ -111,8 +121,6 @@ const BackgroundBox = styled.div` ` const StyledIcon = styled(Icon.FilledArrow)` - position: absolute; - bottom: -4px; - left: 50%; - transform: translateX(-50%) rotate(-90deg); + margin: 0 auto; + transform: rotate(-90deg); ` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx index e5f4ab8d6b..20842f79be 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx @@ -29,7 +29,7 @@ export function PlanningBody({ vigilanceArea }: PlanningBodyProps) { return acc }, []) - .flatMap(items => items), + .flat(), [vigilanceArea] ) const hasSimplePeriods = useMemo(() => occurences.some(occ => !occ.isCritical), [occurences]) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/index.tsx index a67a9147bc..acad0bef44 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/index.tsx @@ -37,7 +37,7 @@ export function Planning({ occurences }: PlanningProps) { const PlanningWrapper = styled.ol` display: grid; grid-template-columns: repeat(6, 1fr); - gap: 10px 4px; + column-gap: 10px; font-size: 11px; color: ${p => p.theme.color.slateGray}; diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Schema.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Schema.ts index 5a59557d91..b047e78363 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Schema.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Schema.ts @@ -81,34 +81,32 @@ export const DraftVigilanceAreaPeriodSchema: Yup.Schema -> = Yup.object() - .shape({ - comments: Yup.string().optional(), - createdBy: Yup.string() - .min(3, 'Minimum 3 lettres pour le trigramme') - .max(3, 'Maximum 3 lettres pour le trigramme') - .optional(), - geom: Yup.mixed().optional(), - id: Yup.number().optional(), - images: Yup.array().optional(), - isArchived: Yup.boolean().required(), - isDraft: Yup.boolean().required(), - linkedAMPs: Yup.array().optional(), - linkedRegulatoryAreas: Yup.array().optional(), - links: Yup.array().optional(), - name: Yup.string().required(), - periods: Yup.array().ensure().of(DraftVigilanceAreaPeriodSchema).optional(), - sources: Yup.array().ensure().of(VigilanceAreaSourceSchema).ensure(), - tags: Yup.array().ensure().optional(), - themes: Yup.array().ensure().optional(), - visibility: Yup.mixed().optional() - }) - .required() +export const DraftSchema: Yup.Schema> = + Yup.object() + .shape({ + comments: Yup.string().optional(), + createdBy: Yup.string() + .min(3, 'Minimum 3 lettres pour le trigramme') + .max(3, 'Maximum 3 lettres pour le trigramme') + .optional(), + geom: Yup.mixed().optional(), + id: Yup.number().optional(), + images: Yup.array().optional(), + isDraft: Yup.boolean().required(), + linkedAMPs: Yup.array().optional(), + linkedRegulatoryAreas: Yup.array().optional(), + links: Yup.array().optional(), + name: Yup.string().required(), + periods: Yup.array().ensure().of(DraftVigilanceAreaPeriodSchema).optional(), + sources: Yup.array().ensure().of(VigilanceAreaSourceSchema).ensure(), + tags: Yup.array().ensure().optional(), + themes: Yup.array().ensure().optional(), + visibility: Yup.mixed().optional() + }) + .required() export const PublishedSchema: Yup.Schema< - Omit + Omit > = Yup.object() .shape({ comments: Yup.string().required(), @@ -125,7 +123,6 @@ export const PublishedSchema: Yup.Schema< .required(), id: Yup.number().optional(), images: Yup.array().optional(), - isArchived: Yup.boolean().required(), isDraft: Yup.boolean().required(), linkedAMPs: Yup.array().optional(), linkedRegulatoryAreas: Yup.array().optional(), diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx index 4f188898d4..4f4032c7f6 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Sources/index.tsx @@ -28,7 +28,7 @@ export function Sources() { return ( { @@ -77,7 +77,7 @@ export function Sources() { return ( { diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx index 7b8a2516a7..a2ec1a7d0a 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodsCell.tsx @@ -22,7 +22,7 @@ export function PeriodsCell({ periods }: { periods: VigilanceArea.VigilanceAreaP Périodes multiples{' '} {periods.map(period => ( - + ))} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx index b694cdb347..49d0825964 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Columns/index.tsx @@ -24,7 +24,7 @@ export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = f enableSorting: true, header: () => 'Période(s) de vigilance', id: 'periods', - size: 220 + legacyFirefoxOffset + size: 250 + legacyFirefoxOffset }, { accessorFn: row => row.tags, @@ -72,7 +72,7 @@ export const Columns = (legacyFirefoxOffset: number = 0, isFetching: boolean = f enableSorting: true, header: () => 'Créée par', id: 'createdBy', - size: 95 + legacyFirefoxOffset + size: 97 + legacyFirefoxOffset }, { accessorFn: row => row.visibility, diff --git a/frontend/src/features/VigilanceArea/useCases/saveVigilanceArea.ts b/frontend/src/features/VigilanceArea/useCases/saveVigilanceArea.ts index f2bfc9d467..2eb42d50c7 100644 --- a/frontend/src/features/VigilanceArea/useCases/saveVigilanceArea.ts +++ b/frontend/src/features/VigilanceArea/useCases/saveVigilanceArea.ts @@ -16,10 +16,13 @@ export const saveVigilanceArea = ? vigilanceAreasAPI.endpoints.createVigilanceArea : vigilanceAreasAPI.endpoints.updateVigilanceArea - const realEndDate = values.periods && values.periods[0] ? computeRealEndDate(values.periods[0]) : '' - const computedEndDate = realEndDate ?? undefined - const periods = values.periods?.map(period => ({ ...period, computedEndDate })) ?? [] - const vigilanceAreaToSave = { ...values, periods } + const periodsWithComputedEndDate: VigilanceArea.VigilanceAreaPeriod[] | undefined = values.periods?.map(period => { + const realEndDate = computeRealEndDate(period) + const computedEndDate = realEndDate ?? undefined + + return { ...period, computedEndDate } + }) + const vigilanceAreaToSave = { ...values, periods: periodsWithComputedEndDate } try { const response = await dispatch(vigilanceAreaEnpoint.initiate(vigilanceAreaToSave)) diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index 022df76e9c..dce4b66af0 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -261,7 +261,9 @@ export function OverlayContent({ items }: OverlayContentProps) { )} - {items.length === 1 && isVigilanceArea && vigilanceAreaPeriod?.map(period => {period})} + {items.length === 1 && + isVigilanceArea && + vigilanceAreaPeriod?.map(period => {period})} ) })} From 21b07f83faa9b444eafc9974cba749c354584e9a Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Wed, 7 Jan 2026 11:33:04 +0100 Subject: [PATCH 09/12] fix: editable brief (ODT) with multiple periods --- .../EditableBriefVigilanceAreaEntity.kt | 42 ++++++++++--------- .../EditableBriefVigilanceAreaPeriodEntity.kt | 17 +++++++- .../CreateOrUpdateVigilanceAreaUTests.kt | 1 - .../GetVigilanceAreaByIdUTests.kt | 2 +- .../fixtures/VigilanceAreaFixture.kt | 26 ------------ .../endpoints/bff/v1/VigilanceAreasITests.kt | 4 -- .../JpaVigilanceAreaRepositoryITests.kt | 1 - 7 files changed, 38 insertions(+), 55 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaEntity.kt index dabb9e7810..f51a1fb2d2 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaEntity.kt @@ -4,18 +4,11 @@ import fr.gouv.cacem.monitorenv.domain.entities.vigilanceArea.LinkEntity import fr.gouv.cacem.monitorenv.utils.WordUtils import org.apache.poi.xwpf.usermodel.XWPFDocument import org.apache.poi.xwpf.usermodel.XWPFTableCell -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.Locale data class EditableBriefVigilanceAreaEntity( val color: String, val comments: String? = null, - val endDatePeriod: ZonedDateTime? = null, - val endingOccurenceDate: String, - val frequency: String, val id: Int, - val isAtAllTimes: Boolean, override val image: String?, val imagesAttachments: List? = null, override val minimap: String?, @@ -23,9 +16,9 @@ data class EditableBriefVigilanceAreaEntity( val linkedRegulatoryAreas: String? = null, val links: List? = null, val name: String, - val startDatePeriod: ZonedDateTime? = null, val themes: String? = null, val visibility: String? = null, + val periods: List? = null, ) : DetailWithImagesRenderable { override val title = name @@ -34,12 +27,19 @@ data class EditableBriefVigilanceAreaEntity( private const val LINK_ROW_INDEX = 6 } - override fun buildDetailsRows(): List> { - val formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy", Locale.FRENCH) - val periodDate = "Du ${startDatePeriod?.format(formatter)} au ${endDatePeriod?.format(formatter)}" - - return listOf( - listOf("Période", if (isAtAllTimes) "En tout temps" else periodDate), + override fun buildDetailsRows(): List> = + listOf( + listOf( + "Période(s)", + periods?.joinToString("\n") { period -> + listOf( + period.getPeriodText(), + period.frequency, + period.endingOccurenceDate, + ).filter { it.isNotEmpty() }.joinToString(", ") + } + ?: "", + ), listOf("Thématique", themes ?: ""), listOf("Visibilité", visibility ?: ""), listOf("Commentaires", comments ?: ""), @@ -47,7 +47,6 @@ data class EditableBriefVigilanceAreaEntity( listOf("Amps en lien", linkedAMPs ?: ""), listOf("Liens utiles", ""), ) - } override fun customizeValueCell( rowIndex: Int, @@ -62,11 +61,14 @@ data class EditableBriefVigilanceAreaEntity( val cellRun = cell.addParagraph().createRun() cellRun.fontFamily = "Arial" cellRun.fontSize = 10 - cellRun.setText(buildDetailsRows()[0][1]) - cellRun.addBreak() - cellRun.setText(frequency) - cellRun.addBreak() - cellRun.setText(endingOccurenceDate) + val periodsText = buildDetailsRows()[0][1] + val lines = periodsText.split("\n") + lines.forEachIndexed { index, period -> + cellRun.setText(period) + if (index < lines.size - 1) { + cellRun.addBreak() + } + } } LINK_ROW_INDEX -> { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt index 18399a43f2..eef388e06c 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/dashboard/EditableBriefVigilanceAreaPeriodEntity.kt @@ -1,9 +1,22 @@ package fr.gouv.cacem.monitorenv.domain.entities.dashboard import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.Locale +import java.util.UUID data class EditableBriefVigilanceAreaPeriodEntity( - val endDatePeriod: ZonedDateTime?, + val id: UUID, + val isAtAllTimes: Boolean, + val startDatePeriod: ZonedDateTime? = null, + val endDatePeriod: ZonedDateTime? = null, val endingOccurenceDate: String, val frequency: String, -) +) { + fun getPeriodDate(): String { + val formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy", Locale.FRENCH) + return "Du ${startDatePeriod?.format(formatter)} au ${endDatePeriod?.format(formatter)}" + } + + fun getPeriodText(): String = if (isAtAllTimes) "En tout temps" else getPeriodDate() +} diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt index 19a8c9bd6a..8f7c515caf 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/CreateOrUpdateVigilanceAreaUTests.kt @@ -40,7 +40,6 @@ class CreateOrUpdateVigilanceAreaUTests { val newVigilanceArea = VigilanceAreaEntity( comments = "Comments", - isArchived = false, isDeleted = false, isDraft = true, images = listOf(image), diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/GetVigilanceAreaByIdUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/GetVigilanceAreaByIdUTests.kt index abc83c098f..9280ec1c8c 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/GetVigilanceAreaByIdUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/GetVigilanceAreaByIdUTests.kt @@ -17,7 +17,7 @@ class GetVigilanceAreaByIdUTests { @Test fun `execute should return vigilance area entity`(log: CapturedOutput) { val vigilanceAreaId = 3 - val expectedEntity = VigilanceAreaFixture.anArchivedVigilanceAreaEntity() + val expectedEntity = VigilanceAreaFixture.aVigilanceAreaEntity() given(vigilanceAreaRepository.findById(vigilanceAreaId)).willReturn(expectedEntity) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt index 893b4d7c59..d861ffa66a 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/vigilanceArea/fixtures/VigilanceAreaFixture.kt @@ -58,7 +58,6 @@ class VigilanceAreaFixture { createdBy = createdBy, images = null, geom = geom, - isArchived = false, isDeleted = false, isDraft = isDraft, links = listOf(), @@ -96,7 +95,6 @@ class VigilanceAreaFixture { ), ), geom = null, - isArchived = false, isDeleted = false, isDraft = true, links = @@ -122,29 +120,5 @@ class VigilanceAreaFixture { validatedAt = null, periods = listOf(aVigilanceAreaPeriodEntity()), ) - - fun anArchivedVigilanceAreaEntity(): VigilanceAreaEntity = - VigilanceAreaEntity( - id = 3, - comments = "Basic area comments", - createdBy = "ABC", - images = listOf(), - geom = null, - isArchived = true, - isDeleted = false, - isDraft = true, - links = listOf(), - linkedAMPs = listOf(1, 2), - linkedRegulatoryAreas = listOf(1, 2), - name = "Basic Area", - sources = emptyList(), - themes = listOf(aTheme(id = 1, name = "AMP")), - visibility = VisibilityEnum.PRIVATE, - createdAt = ZonedDateTime.parse("2024-01-01T00:00:00Z"), - updatedAt = ZonedDateTime.parse("2024-01-01T12:00:00Z"), - tags = listOf(aTag(name = "AMP")), - validatedAt = null, - periods = listOf(aVigilanceAreaPeriodEntity()), - ) } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt index 3c43051462..af95ce58e2 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/VigilanceAreasITests.kt @@ -88,7 +88,6 @@ class VigilanceAreasITests { VigilanceAreaEntity( id = 1, name = "Vigilance Area 1", - isArchived = false, isDeleted = false, isDraft = false, comments = "Commentaires sur la zone de vigilance", @@ -163,7 +162,6 @@ class VigilanceAreasITests { VigilanceAreaEntity( id = 2, name = "Vigilance Area 2", - isArchived = false, isDeleted = false, isDraft = true, comments = null, @@ -311,7 +309,6 @@ class VigilanceAreasITests { VigilanceAreaDataInput( id = 1, name = "Vigilance Area 1", - isArchived = false, isDraft = false, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", @@ -441,7 +438,6 @@ class VigilanceAreasITests { VigilanceAreaDataInput( id = 1, name = "Vigilance Area 1", - isArchived = false, isDraft = false, comments = "Commentaires sur la zone de vigilance", createdBy = "ABC", diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt index 50aedb9007..cb8333d64b 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaVigilanceAreaRepositoryITests.kt @@ -103,7 +103,6 @@ class JpaVigilanceAreaRepositoryITests : AbstractDBTests() { val vigilanceArea = VigilanceAreaEntity( name = "Nouvelle zone de vigilance", - isArchived = false, isDeleted = false, isDraft = true, comments = "Commentaires sur la zone de vigilance", From 1f34794e1aa58f2474b94d22c09e0da4548e575c Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Wed, 7 Jan 2026 15:00:49 +0100 Subject: [PATCH 10/12] fix: e2e tests --- .../cypress/e2e/main_window/layerTree/search_zone.spec.ts | 4 ++-- .../e2e/main_window/layerTree/vigilance_area_layers.spec.ts | 4 ++-- .../vigilance_area/delete_vigilance_area.spec.ts | 6 +++++- .../e2e/side_window/vigilance_areas_list/filters.spec.ts | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/cypress/e2e/main_window/layerTree/search_zone.spec.ts b/frontend/cypress/e2e/main_window/layerTree/search_zone.spec.ts index 50ad65f4a0..486164b3c6 100644 --- a/frontend/cypress/e2e/main_window/layerTree/search_zone.spec.ts +++ b/frontend/cypress/e2e/main_window/layerTree/search_zone.spec.ts @@ -20,7 +20,7 @@ context('LayerTree > Search zone', () => { cy.getDataCy('amp-results-list-button').contains('7 résultats') cy.get('#isAmpSearchResultsVisible').should('be.checked') - cy.getDataCy('vigilance-area-results-list-button').contains('3 résultats') + cy.getDataCy('vigilance-area-results-list-button').contains('2 résultats') cy.get('#isVigilanceAreaSearchResultsVisible').should('be.checked') // reset search by zone @@ -33,7 +33,7 @@ context('LayerTree > Search zone', () => { cy.getDataCy('amp-results-list-button').contains('20 résultats') cy.get('#isAmpSearchResultsVisible').should('not.be.checked') - cy.getDataCy('vigilance-area-results-list-button').contains('5 résultats') + cy.getDataCy('vigilance-area-results-list-button').contains('4 résultats') cy.get('#isVigilanceAreaSearchResultsVisible').should('not.be.checked') }) }) diff --git a/frontend/cypress/e2e/main_window/layerTree/vigilance_area_layers.spec.ts b/frontend/cypress/e2e/main_window/layerTree/vigilance_area_layers.spec.ts index ec3b8347b5..1cb12bc57a 100644 --- a/frontend/cypress/e2e/main_window/layerTree/vigilance_area_layers.spec.ts +++ b/frontend/cypress/e2e/main_window/layerTree/vigilance_area_layers.spec.ts @@ -101,11 +101,11 @@ context('LayerTree > Vigilance Area Layers', () => { cy.getDataCy('vigilance-area-results-list-button').contains('0 résultat') }) it('Result list should be displayed by default but not checked and total should be visible', () => { - cy.getDataCy('vigilance-area-results-list-button').contains('5 résultats') + cy.getDataCy('vigilance-area-results-list-button').contains('4 résultats') cy.get('#isVigilanceAreaSearchResultsVisible').should('not.be.checked') cy.getDataCy('vigilance-area-results-list-button').click() - cy.getDataCy('vigilance-area-result-list').children().should('have.length', 5) + cy.getDataCy('vigilance-area-result-list').children().should('have.length', 4) cy.get('#isVigilanceAreaSearchResultsVisible').should('be.checked') }) }) diff --git a/frontend/cypress/e2e/main_window/vigilance_area/delete_vigilance_area.spec.ts b/frontend/cypress/e2e/main_window/vigilance_area/delete_vigilance_area.spec.ts index 806cbfe20b..43417952ed 100644 --- a/frontend/cypress/e2e/main_window/vigilance_area/delete_vigilance_area.spec.ts +++ b/frontend/cypress/e2e/main_window/vigilance_area/delete_vigilance_area.spec.ts @@ -23,14 +23,18 @@ describe('Create Vigilance Area', () => { const { asDatePickerDateTime } = getUtcDateInMultipleFormats() const vigilanceAreaEndDate = getFutureDate(5, 'day') - cy.fill('Période de validité', [asDatePickerDateTime, vigilanceAreaEndDate]) + + cy.clickButton('Ajouter une période de vigilance simple') + cy.fill('Période de vigilance', [asDatePickerDateTime, vigilanceAreaEndDate]) cy.fill('Récurrence', 'Aucune') + cy.clickButton('Valider') // Submit the form cy.clickButton('Enregistrer') cy.wait('@createVigilanceArea').then(() => { cy.clickButton('Fermer la zone de vigilance') cy.clickButton('Filtrer par type de zones') + cy.fill('Période de vigilance', 'En ce moment') cy.getDataCy('vigilance-area-results-list-button').click() cy.getDataCy('vigilance-area-result-zone').contains('Ma zone de vigilance à supprimer') diff --git a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts index 5adf6d454c..3f2b876310 100644 --- a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts @@ -83,7 +83,7 @@ context('Side Window > Vigilance Areas List > Filter Bar', () => { // with only PRIVATE visibility option checked cy.fill('Interne CACEM', true) cy.fill('Publique', false) - cy.getDataCy('vigilance-area-row').should('have.length', 2) + cy.getDataCy('vigilance-area-row').should('have.length', 1) }) it('Should filter vigilance areas by type', () => { From a576185c7b6bdff25c23f80e7bee79df53376cab Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Fri, 9 Jan 2026 14:28:20 +0100 Subject: [PATCH 11/12] feat: emilie's review --- .../vigilance_areas_list/filters.spec.ts | 2 +- .../components/VigilanceAreaForm/Period.tsx | 8 +-- .../components/VigilanceAreaForm/Periods.tsx | 70 ++++++++++--------- .../VigilanceAreaForm/Planning/MonthBox.tsx | 8 +-- .../Planning/PlanningBody.tsx | 55 +++++++++++---- .../components/VigilanceAreaForm/utils.ts | 16 ++++- .../components/VigilanceAreaLayer/style.ts | 9 +-- .../VigilanceAreasList/Cells/PeriodCell.tsx | 2 +- frontend/src/features/VigilanceArea/types.ts | 4 +- .../filters/isVigilanceAreaPartOfType.ts | 9 +-- .../overlays/OverlayContent.tsx | 6 +- 11 files changed, 114 insertions(+), 75 deletions(-) diff --git a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts index 3f2b876310..969d4dea0a 100644 --- a/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/vigilance_areas_list/filters.spec.ts @@ -88,7 +88,7 @@ context('Side Window > Vigilance Areas List > Filter Bar', () => { it('Should filter vigilance areas by type', () => { cy.fill('Type de zone de vigilance', ['Aucune période de vigilance en cours']) - cy.getDataCy('vigilance-area-row').should('have.length', 0) + cy.getDataCy('vigilance-area-row').should('have.length', 1) cy.fill('Type de zone de vigilance', ['Période de vigilance critique en cours']) cy.getDataCy('vigilance-area-row').should('have.length', 1) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx index e108a14d03..9652abb4f4 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Period.tsx @@ -36,9 +36,9 @@ export function Period({ hasError, index, initialPeriod, onValidate, remove }: P const frequencyOptions = getOptionsFromLabelledEnum(VigilanceArea.FrequencyLabel) const endingConditionOptions = getOptionsFromLabelledEnum(VigilanceArea.EndingConditionLabel) - const isNewlyCreatedPeriod = Object.values(omit(initialPeriod, ['isCritical'])).every( - value => value === undefined || value === false - ) + const isNewlyCreatedPeriod = Object.values( + omit(initialPeriod, ['isCritical', 'id', 'frequency', 'endingCondition']) + ).every(value => value === undefined || value === false) const [isEditing, setIsEditing] = useState(isNewlyCreatedPeriod) const [editedPeriod, setEditedPeriod] = useState(initialPeriod) const isValid = (value: VigilanceArea.VigilanceAreaPeriod) => PublishedVigilanceAreaPeriodSchema.isValidSync(value) @@ -208,7 +208,7 @@ export function Period({ hasError, index, initialPeriod, onValidate, remove }: P ) : (
- + {getDateAsLocalizedStringVeryCompact(initialPeriod.startDatePeriod, true)} au{' '} {getDateAsLocalizedStringVeryCompact(initialPeriod.endDatePeriod, true)} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx index b6217726f0..1450d9ef3d 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Periods.tsx @@ -1,47 +1,40 @@ import { Period } from '@features/VigilanceArea/components/VigilanceAreaForm/Period' import { getVigilanceAreaPeriodInitialValues } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { VigilanceArea } from '@features/VigilanceArea/types' -import { Accent, Button, Checkbox, Icon, Legend } from '@mtes-mct/monitor-ui' -import { FieldArray, useField } from 'formik' +import { Accent, Button, Checkbox, customDayjs, Icon, Legend } from '@mtes-mct/monitor-ui' +import { FieldArray, useFormikContext } from 'formik' import styled from 'styled-components' export function Periods() { - const [field, meta, helper] = useField('periods') - const periods = field.value - - const setIsAtAllTimes = async ( - isChecked: boolean | undefined, - isCritical: boolean, - push: (vigilanceArea: VigilanceArea.VigilanceAreaPeriod) => void, - remove: (index: number) => void - ) => { - async function removeRemainingPeriod() { - await helper.setValue(field.value.filter(period => period.isCritical !== isCritical)) - } + const { errors, setFieldValue, values } = useFormikContext() - const vigilanceAreaPeriod: VigilanceArea.VigilanceAreaPeriod = { - ...getVigilanceAreaPeriodInitialValues(), - isAtAllTimes: true, - isCritical - } - const index = field.value.findIndex(period => period.isAtAllTimes && period.isCritical === isCritical) + const { periods } = values + const setIsAtAllTimes = (isChecked: boolean | undefined, isCritical: boolean) => { + const filtered = periods?.filter(period => period.isCritical !== isCritical) + + const index = periods?.findIndex(period => period.isAtAllTimes && period.isCritical === isCritical) ?? -1 if (isChecked && index === -1) { - await removeRemainingPeriod() - push(vigilanceAreaPeriod) + const vigilanceAreaPeriod: VigilanceArea.VigilanceAreaPeriod = { + ...getVigilanceAreaPeriodInitialValues(), + isAtAllTimes: true, + isCritical + } + const newVigilanceAreaperiods = [...(filtered ?? []), vigilanceAreaPeriod] + setFieldValue('periods', newVigilanceAreaperiods) } if (!isChecked && index !== -1) { - remove(index) + setFieldValue('periods', filtered) } } return ( { - const hasSimpleIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && !period.isCritical) - const hasCriticalIsAtAllTimesPeriod = periods.some(period => period.isAtAllTimes && period.isCritical) + render={({ push, remove, replace }) => { + const hasSimpleIsAtAllTimesPeriod = periods?.some(period => period.isAtAllTimes && !period.isCritical) + const hasCriticalIsAtAllTimesPeriod = periods?.some(period => period.isAtAllTimes && period.isCritical) return ( @@ -54,7 +47,7 @@ export function Periods() { isLight label="Vigilance simple en tout temps" name="isSimpleAtAllTimes" - onChange={isChecked => setIsAtAllTimes(isChecked, false, push, remove)} + onChange={isChecked => setIsAtAllTimes(isChecked, false)} /> @@ -64,24 +57,33 @@ export function Periods() { isLight label="Vigilance critique en tout temps" name="isCriticalAtAllTimes" - onChange={isChecked => setIsAtAllTimes(isChecked, true, push, remove)} + onChange={isChecked => setIsAtAllTimes(isChecked, true)} /> {periods - .filter((period: VigilanceArea.VigilanceAreaPeriod) => !period.isAtAllTimes) + ?.filter((period: VigilanceArea.VigilanceAreaPeriod) => !period.isAtAllTimes) + .sort((a, b) => { + if (!a.startDatePeriod) { + return -1 + } + if (!b.startDatePeriod) { + return 1 + } + + return customDayjs(a.startDatePeriod).valueOf() - customDayjs(b.startDatePeriod).valueOf() + }) .map((period: VigilanceArea.VigilanceAreaPeriod) => { - const index = field.value.indexOf(period) + const index = periods?.indexOf(period) return ( { - remove(index) - push(vigilanceAreaPeriod) + replace(index, vigilanceAreaPeriod) }} remove={remove} /> @@ -93,6 +95,7 @@ export function Periods() { Icon={Icon.Plus} onClick={() => push({ + ...getVigilanceAreaPeriodInitialValues(), isAtAllTimes: false, isCritical: false }) @@ -106,6 +109,7 @@ export function Periods() { Icon={Icon.Plus} onClick={() => push({ + ...getVigilanceAreaPeriodInitialValues(), isAtAllTimes: false, isCritical: true }) diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx index b418b1f9a0..6491509d0c 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/MonthBox.tsx @@ -106,10 +106,10 @@ const DayBox = styled.div<{ $isCritical: boolean; $isEnd: boolean; $isHighlighte ${({ $isCritical, $isHighlighted }) => $isHighlighted && `background-color: ${$isCritical ? '#C25141BF' : '#C2514180'}; - border-top: ${$isCritical ? '2px solid rgba(194, 81, 65, 0.75)' : '1px solid #933F20'}; - border-bottom: ${$isCritical ? '2px solid rgba(194, 81, 65, 0.75)' : '1px solid #933F20'};`}; - ${p => p.$isStart && `border-left: ${p.$isCritical ? '2px solid rgba(194, 81, 65, 0.75)' : '1px solid #933F20'}`} - ${p => p.$isEnd && `border-right: ${p.$isCritical ? '2px solid rgba(194, 81, 65, 0.75)' : '1px solid #933F20'}`} + border-top: ${$isCritical ? '2px solid #E1000F' : '1px solid #933F20'}; + border-bottom: ${$isCritical ? '2px solid #E1000F' : '1px solid #933F20'};`}; + ${p => p.$isStart && `border-left: ${p.$isCritical ? '2px solid #E1000F' : '1px solid #933F20'}`} + ${p => p.$isEnd && `border-right: ${p.$isCritical ? '2px solid #E1000F' : '1px solid #933F20'}`} ` const BackgroundBox = styled.div` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx index 20842f79be..520a57e6f4 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/Planning/PlanningBody.tsx @@ -1,14 +1,15 @@ /* eslint-disable react/destructuring-assignment */ import { Bold } from '@components/style' +import { Tooltip } from '@components/Tooltip' import { Rectangle } from '@features/layersSelector/utils/LayerLegend.style' import { computeOccurenceWithinCurrentYear, type DateRange } from '@features/VigilanceArea/components/VigilanceAreaForm/Planning/utils' import { VigilanceArea } from '@features/VigilanceArea/types' +import { computeVigilanceAreaPeriod, endingOccurenceText, frequencyText } from '@features/VigilanceArea/utils' import { Icon, Size, THEME } from '@mtes-mct/monitor-ui' -import { getDateAsLocalizedStringVeryCompact } from '@utils/getDateAsLocalizedString' import { useMemo, useState } from 'react' import styled from 'styled-components' @@ -59,14 +60,23 @@ export function PlanningBody({ vigilanceArea }: PlanningBodyProps) { - {occurences - .filter(occ => !occ.isCritical) - .map(({ end, start }, index) => ( + {vigilanceArea.periods + ?.filter(period => !period.isCritical) + .map(period => ( // eslint-disable-next-line react/no-array-index-key -
  • - Du {getDateAsLocalizedStringVeryCompact(start.toISOString(), true)} au{' '} - {getDateAsLocalizedStringVeryCompact(end.toISOString(), true)} -
  • + + {computeVigilanceAreaPeriod(period, false)} + {period?.frequency && period.frequency !== VigilanceArea.Frequency.NONE && ( + + {[ + frequencyText(period.frequency, false), + endingOccurenceText(period?.endingCondition, period?.computedEndDate, false) + ] + .filter(value => !!value) + .join(', ')} + + )} + ))}
    @@ -87,14 +97,23 @@ export function PlanningBody({ vigilanceArea }: PlanningBodyProps) { - {occurences - .filter(occ => occ.isCritical) - .map(({ end, start }, index) => ( + {vigilanceArea.periods + ?.filter(occ => occ.isCritical) + .map(period => ( // eslint-disable-next-line react/no-array-index-key -
  • - Du {getDateAsLocalizedStringVeryCompact(start.toISOString(), true)} au{' '} - {getDateAsLocalizedStringVeryCompact(end.toISOString(), true)} -
  • + + {computeVigilanceAreaPeriod(period, false)} + {period?.frequency && period.frequency !== VigilanceArea.Frequency.NONE && ( + + {[ + frequencyText(period?.frequency, false), + endingOccurenceText(period?.endingCondition, period?.computedEndDate, false) + ] + .filter(value => !!value) + .join(', ')} + + )} + ))}
    @@ -140,6 +159,12 @@ const PeriodList = styled.ol` flex-wrap: wrap; ` +const PeriodItem = styled.li` + display: flex; + align-items: center; + gap: 8px; +` + const PeriodDescription = styled.p` font-style: italic; font-weight: 400; diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts index be0c519293..75ba3a37a4 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/utils.ts @@ -1,6 +1,7 @@ import { computeOccurenceWithinCurrentYear } from '@features/VigilanceArea/components/VigilanceAreaForm/Planning/utils' import { DraftSchema, PublishedSchema } from '@features/VigilanceArea/components/VigilanceAreaForm/Schema' import { VigilanceArea } from '@features/VigilanceArea/types' +import { v4 as uuidv4 } from 'uuid' import { customDayjs } from '../../../../../cypress/e2e/utils/customDayjs' @@ -25,14 +26,15 @@ export function getVigilanceAreaInitialValues(): Omit { +export function getVigilanceAreaPeriodInitialValues(): VigilanceArea.VigilanceAreaPeriod { return { computedEndDate: undefined, endDatePeriod: undefined, - endingCondition: undefined, + endingCondition: VigilanceArea.EndingCondition.NEVER, endingOccurrenceDate: undefined, endingOccurrencesNumber: undefined, - frequency: undefined, + frequency: VigilanceArea.Frequency.NONE, + id: uuidv4(), isAtAllTimes: false, isCritical: undefined, startDatePeriod: undefined @@ -57,3 +59,11 @@ export function isWithinPeriod( ) }) } + +export function isOutOfPeriod(periods: VigilanceArea.VigilanceAreaPeriod[] | undefined) { + return !!periods?.every(period => { + const dateRanges = computeOccurenceWithinCurrentYear(period) + + return dateRanges.every(dateRange => !customDayjs.utc().isBetween(dateRange.start, dateRange.end)) + }) +} diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts index 41617fb1df..794628b039 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaLayer/style.ts @@ -1,4 +1,4 @@ -import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' +import { isOutOfPeriod, isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { VigilanceArea } from '@features/VigilanceArea/types' import { THEME } from '@mtes-mct/monitor-ui' import { getColorWithAlpha, stringToColorInGroup } from '@utils/utils' @@ -51,11 +51,8 @@ export const getVigilanceAreaLayerStyle = feature => { const periods = feature.get('periods') as Array | undefined const isWithinCriticalPeriod = isWithinPeriod(periods, true) - const colorWithAlpha = getVigilanceAreaColorWithAlpha( - feature.get('name'), - feature.get('comments'), - periods?.length === 0 - ) + const isInformative = isOutOfPeriod(periods) + const colorWithAlpha = getVigilanceAreaColorWithAlpha(feature.get('name'), feature.get('comments'), isInformative) return getStyle( colorWithAlpha, diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx index 7712885034..fa7068ffe3 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreasList/Cells/PeriodCell.tsx @@ -11,7 +11,7 @@ export function PeriodCell({ period }: { period: VigilanceArea.VigilanceAreaPeri {!period?.isCritical && } {period?.isCritical && } {computeVigilanceAreaPeriod(period, false)} - {period?.frequency && ( + {period?.frequency && period.frequency !== VigilanceArea.Frequency.NONE && ( {[ frequencyText(period?.frequency, false), diff --git a/frontend/src/features/VigilanceArea/types.ts b/frontend/src/features/VigilanceArea/types.ts index fb8836b5c7..e5b86d0992 100644 --- a/frontend/src/features/VigilanceArea/types.ts +++ b/frontend/src/features/VigilanceArea/types.ts @@ -147,13 +147,13 @@ export namespace VigilanceArea { export enum VigilanceAreaFilterType { SIMPLE = 'SIMPLE', CRITICAL = 'CRITICAL', - NONE = 'NONE' + INFORMATIVE = 'INFORMATIVE' } export enum VigilanceAreaFilterTypeLabel { SIMPLE = 'Période de vigilance simple en cours', CRITICAL = 'Période de vigilance critique en cours', - NONE = 'Aucune période de vigilance en cours' + INFORMATIVE = 'Aucune période de vigilance en cours' } export type VigilanceAreaProperties = Omit & { diff --git a/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts b/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts index 4b633f5c99..983003a693 100644 --- a/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts +++ b/frontend/src/features/VigilanceArea/useCases/filters/isVigilanceAreaPartOfType.ts @@ -1,4 +1,4 @@ -import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' +import { isOutOfPeriod, isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { VigilanceArea } from '@features/VigilanceArea/types' export function getFilterInformativeVigilanceArea( @@ -6,8 +6,8 @@ export function getFilterInformativeVigilanceArea( vigilanceArea: VigilanceArea.VigilanceArea ) { return ( - (typeFilter?.includes(VigilanceArea.VigilanceAreaFilterType.NONE) && (vigilanceArea.periods ?? []).length === 0) === - true + (typeFilter?.includes(VigilanceArea.VigilanceAreaFilterType.INFORMATIVE) && + (vigilanceArea.periods ?? []).length === 0) === true ) } @@ -22,7 +22,8 @@ export function isVigilanceAreaPartOfType( const filterSimpleVigilanceArea = typeFilter.includes(VigilanceArea.VigilanceAreaFilterType.SIMPLE) && isWithinPeriod(vigilanceArea.periods, false) - const filterInformativeVigilanceArea = getFilterInformativeVigilanceArea(typeFilter, vigilanceArea) + const filterInformativeVigilanceArea = + typeFilter.includes(VigilanceArea.VigilanceAreaFilterType.INFORMATIVE) && isOutOfPeriod(vigilanceArea.periods) const filterCriticalVigilanceArea = typeFilter.includes(VigilanceArea.VigilanceAreaFilterType.CRITICAL) && isWithinPeriod(vigilanceArea.periods, true) diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index dce4b66af0..c96647e500 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -1,5 +1,6 @@ import { Tooltip } from '@components/Tooltip' import { Dashboard } from '@features/Dashboard/types' +import { isOutOfPeriod, isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { getIsLinkingAMPToVigilanceArea, getIsLinkingRegulatoryToVigilanceArea, @@ -191,7 +192,7 @@ export function OverlayContent({ items }: OverlayContentProps) { ) : [] - const isInformative = (item.properties as VigilanceArea.VigilanceAreaProperties)?.periods?.length === 0 + const { periods } = item.properties as VigilanceArea.VigilanceAreaProperties const isIsolatedLayerFilled = isolatedLayer?.id === id && isolatedLayer?.isFilled @@ -204,7 +205,8 @@ export function OverlayContent({ items }: OverlayContentProps) { > Date: Mon, 12 Jan 2026 12:45:41 +0100 Subject: [PATCH 12/12] refacto: clean-up after review --- .../DashboardForm/VigilanceAreas/Layer.tsx | 4 ++-- .../components/VigilanceAreaForm/Form.tsx | 2 +- .../{ => Periods}/Period.tsx | 14 ++++++------ .../{ => Periods}/Periods.tsx | 22 +++++++++++++------ .../VigilanceAreaForm/Planning/MonthBox.tsx | 10 ++++----- .../Planning/PlanningBody.tsx | 2 -- .../VigilanceAreaForm/Sources/index.tsx | 4 ++-- .../components/VigilanceAreaForm/index.tsx | 4 ++-- .../VigilanceAreasList/Cells/PeriodsCell.tsx | 2 +- .../MyVigilanceAreaLayerZone.tsx | 4 ++-- .../ResultsList/VigilanceAreaLayer/index.tsx | 4 ++-- 11 files changed, 39 insertions(+), 33 deletions(-) rename frontend/src/features/VigilanceArea/components/VigilanceAreaForm/{ => Periods}/Period.tsx (96%) rename frontend/src/features/VigilanceArea/components/VigilanceAreaForm/{ => Periods}/Periods.tsx (85%) diff --git a/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Layer.tsx b/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Layer.tsx index 77c9bc9588..4f0aa11c26 100644 --- a/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Layer.tsx +++ b/frontend/src/features/Dashboard/components/DashboardForm/VigilanceAreas/Layer.tsx @@ -3,7 +3,7 @@ import { dashboardActions, getOpenedPanel } from '@features/Dashboard/slice' import { Dashboard } from '@features/Dashboard/types' import { LayerLegend } from '@features/layersSelector/utils/LayerLegend.style' import { LayerSelector } from '@features/layersSelector/utils/LayerSelector.style' -import { isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' +import { isOutOfPeriod, isWithinPeriod } from '@features/VigilanceArea/components/VigilanceAreaForm/utils' import { VigilanceArea } from '@features/VigilanceArea/types' import { useAppSelector } from '@hooks/useAppSelector' import { Accent, Icon, IconButton, Tag, THEME } from '@mtes-mct/monitor-ui' @@ -72,7 +72,7 @@ export function Layer({ isPinned = false, isSelected = false, vigilanceArea }: V | undefined index: number initialPeriod: VigilanceArea.VigilanceAreaPeriod onValidate: (vigilanceAreaSource: VigilanceArea.VigilanceAreaPeriod) => void remove: (index: number) => void } -export function Period({ hasError, index, initialPeriod, onValidate, remove }: PeriodProps) { +export function Period({ error, index, initialPeriod, onValidate, remove }: PeriodProps) { const frequencyOptions = getOptionsFromLabelledEnum(VigilanceArea.FrequencyLabel) const endingConditionOptions = getOptionsFromLabelledEnum(VigilanceArea.EndingConditionLabel) - const isNewlyCreatedPeriod = Object.values( omit(initialPeriod, ['isCritical', 'id', 'frequency', 'endingCondition']) ).every(value => value === undefined || value === false) const [isEditing, setIsEditing] = useState(isNewlyCreatedPeriod) const [editedPeriod, setEditedPeriod] = useState(initialPeriod) const isValid = (value: VigilanceArea.VigilanceAreaPeriod) => PublishedVigilanceAreaPeriodSchema.isValidSync(value) - const cancel = () => { if (isNewlyCreatedPeriod) { remove(index) @@ -107,7 +107,7 @@ export function Period({ hasError, index, initialPeriod, onValidate, remove }: P : undefined } disabled={editedPeriod.isAtAllTimes} - error={hasError} + error={error?.startDatePeriod || error?.endDatePeriod} hasSingleCalendar isCompact isErrorMessageHidden @@ -133,7 +133,7 @@ export function Period({ hasError, index, initialPeriod, onValidate, remove }: P <>