diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 39aa13031..000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx lint-staged --cwd ./frontend diff --git a/Makefile b/Makefile index 1cfd243d9..36e3e7cdc 100644 --- a/Makefile +++ b/Makefile @@ -221,4 +221,7 @@ logs-db: # ALIASES +.PHONY: dev lint-back + dev: dev-run-back-with-infra +lint-back: dev-lint-backend diff --git a/backend/pom.xml b/backend/pom.xml index 1ef86532e..e2c3f149d 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -407,7 +407,7 @@ com.pinterest ktlint - 0.48.2 + 0.50.0 diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt index ef27f29b4..8492bea50 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionControlProperties.kt @@ -13,7 +13,14 @@ data class EnvActionControlProperties( val vehicleType: VehicleTypeEnum? = null, val infractions: List? = listOf(), ) { - fun toEnvActionControlEntity(id: UUID, actionStartDateTimeUtc: ZonedDateTime?, actionEndDateTimeUtc: ZonedDateTime?, facade: String?, department: String?, geom: Geometry?) = EnvActionControlEntity( + fun toEnvActionControlEntity( + id: UUID, + actionStartDateTimeUtc: ZonedDateTime?, + actionEndDateTimeUtc: ZonedDateTime?, + facade: String?, + department: String?, + geom: Geometry?, + ) = EnvActionControlEntity( id = id, actionStartDateTimeUtc = actionStartDateTimeUtc, actionEndDateTimeUtc = actionEndDateTimeUtc, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionNoteProperties.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionNoteProperties.kt index 5ce323622..4f72c37f7 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionNoteProperties.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionNoteProperties.kt @@ -6,7 +6,11 @@ import java.util.* data class EnvActionNoteProperties( val observations: String? = null, ) { - fun toEnvActionNoteEntity(id: UUID, actionStartDateTimeUtc: ZonedDateTime?, actionEndDateTimeUtc: ZonedDateTime?) = EnvActionNoteEntity( + fun toEnvActionNoteEntity( + id: UUID, + actionStartDateTimeUtc: ZonedDateTime?, + actionEndDateTimeUtc: ZonedDateTime?, + ) = EnvActionNoteEntity( id = id, actionStartDateTimeUtc = actionStartDateTimeUtc, actionEndDateTimeUtc = actionEndDateTimeUtc, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionSurveillanceProperties.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionSurveillanceProperties.kt index 5ef02588a..608f150c8 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionSurveillanceProperties.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/EnvActionSurveillanceProperties.kt @@ -9,7 +9,14 @@ data class EnvActionSurveillanceProperties( val observations: String? = null, val coverMissionZone: Boolean? = null, ) { - fun toEnvActionSurveillanceEntity(id: UUID, actionStartDateTimeUtc: ZonedDateTime?, actionEndDateTimeUtc: ZonedDateTime?, facade: String?, department: String?, geom: Geometry?) = EnvActionSurveillanceEntity( + fun toEnvActionSurveillanceEntity( + id: UUID, + actionStartDateTimeUtc: ZonedDateTime?, + actionEndDateTimeUtc: ZonedDateTime?, + facade: String?, + department: String?, + geom: Geometry?, + ) = EnvActionSurveillanceEntity( id = id, actionStartDateTimeUtc = actionStartDateTimeUtc, actionEndDateTimeUtc = actionEndDateTimeUtc, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/VesselSizeEnum.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/VesselSizeEnum.kt index f5c71db5b..cb43f2c28 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/VesselSizeEnum.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/entities/mission/VesselSizeEnum.kt @@ -1,10 +1,10 @@ +@file:Suppress("ktlint:standard:enum-entry-name-case") + package fr.gouv.cacem.monitorenv.domain.entities.mission -/* ktlint-disable enum-entry-name-case */ enum class VesselSizeEnum { LESS_THAN_12m, FROM_12_TO_24m, FROM_24_TO_46m, MORE_THAN_46m, } -/* ktlint-enable enum-entry-name-case */ diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt index 0fcaae7de..a91bad3c8 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/mappers/EnvActionMapper.kt @@ -23,9 +23,32 @@ object EnvActionMapper { return try { if (!value.isNullOrEmpty() && value != jsonbNullString) { when (actionType) { - ActionTypeEnum.SURVEILLANCE -> mapper.readValue(value, EnvActionSurveillanceProperties::class.java).toEnvActionSurveillanceEntity(id, actionStartDateTimeUtc, actionEndDateTimeUtc, facade, department, geom) - ActionTypeEnum.CONTROL -> mapper.readValue(value, EnvActionControlProperties::class.java).toEnvActionControlEntity(id, actionStartDateTimeUtc, actionEndDateTimeUtc, facade, department, geom) - ActionTypeEnum.NOTE -> mapper.readValue(value, EnvActionNoteProperties::class.java).toEnvActionNoteEntity(id, actionStartDateTimeUtc, actionEndDateTimeUtc) + ActionTypeEnum.SURVEILLANCE -> mapper.readValue( + value, + EnvActionSurveillanceProperties::class.java, + ).toEnvActionSurveillanceEntity( + id, + actionStartDateTimeUtc, + actionEndDateTimeUtc, + facade, + department, + geom, + ) + ActionTypeEnum.CONTROL -> mapper.readValue( + value, + EnvActionControlProperties::class.java, + ).toEnvActionControlEntity( + id, + actionStartDateTimeUtc, + actionEndDateTimeUtc, + facade, + department, + geom, + ) + ActionTypeEnum.NOTE -> mapper.readValue( + value, + EnvActionNoteProperties::class.java, + ).toEnvActionNoteEntity(id, actionStartDateTimeUtc, actionEndDateTimeUtc) } } else { throw EntityConversionException("No action value found.") diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IReportingRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IReportingRepository.kt index 30c73d5ba..bcccd9148 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IReportingRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IReportingRepository.kt @@ -3,19 +3,19 @@ package fr.gouv.cacem.monitorenv.domain.repositories import fr.gouv.cacem.monitorenv.domain.entities.reporting.ReportingEntity import fr.gouv.cacem.monitorenv.domain.entities.reporting.ReportingTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.reporting.SourceTypeEnum -import java.time.Instant import org.springframework.data.domain.Pageable +import java.time.Instant interface IReportingRepository { fun findById(reportingId: Int): ReportingEntity fun findAll( - pageable: Pageable, - reportingType: List?, - seaFronts: List?, - sourcesType: List?, - startedAfter: Instant, - startedBefore: Instant?, - status: List?, + pageable: Pageable, + reportingType: List?, + seaFronts: List?, + sourcesType: List?, + startedAfter: Instant, + startedBefore: Instant?, + status: List?, ): List fun save(reporting: ReportingEntity): ReportingEntity fun delete(reportingId: Int) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetAllControlThemes.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetAllControlThemes.kt index a4b24bc32..03d8f3e59 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetAllControlThemes.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetAllControlThemes.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.controlThemes // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.controlThemes import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.controlTheme.ControlThemeEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetControlThemeById.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetControlThemeById.kt index 1eab9ce4a..285f7b952 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetControlThemeById.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlThemes/GetControlThemeById.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.controlThemes // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.controlThemes import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.controlTheme.ControlThemeEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitContact.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitContact.kt new file mode 100644 index 000000000..29f39ab49 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitContact.kt @@ -0,0 +1,11 @@ +package fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit + +import fr.gouv.cacem.monitorenv.config.UseCase +import fr.gouv.cacem.monitorenv.domain.repositories.IControlUnitContactRepository + +@UseCase +class DeleteControlUnitContact(private val controlUnitContactRepository: IControlUnitContactRepository) { + fun execute(controlUnitContactId: Int) { + return controlUnitContactRepository.deleteById(controlUnitContactId) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitResource.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitResource.kt new file mode 100644 index 000000000..02d5ea929 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/controlUnit/DeleteControlUnitResource.kt @@ -0,0 +1,11 @@ +package fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit + +import fr.gouv.cacem.monitorenv.config.UseCase +import fr.gouv.cacem.monitorenv.domain.repositories.IControlUnitResourceRepository + +@UseCase +class DeleteControlUnitResource(private val controlUnitResourceRepository: IControlUnitResourceRepository) { + fun execute(controlUnitResourceId: Int) { + return controlUnitResourceRepository.deleteById(controlUnitResourceId) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/healthcheck/GetHealthcheck.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/healthcheck/GetHealthcheck.kt index c41b2cfb5..bef235ec4 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/healthcheck/GetHealthcheck.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/healthcheck/GetHealthcheck.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.healthcheck // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.healthcheck import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.health.Health diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMission.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMission.kt index c0817e2b0..c9408942f 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMission.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMission.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.missions // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.missions import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.mission.* @@ -14,7 +16,7 @@ class CreateOrUpdateMission( private val missionRepository: IMissionRepository, private val facadeRepository: IFacadeAreasRepository, - ) { +) { @Throws(IllegalArgumentException::class) fun execute(mission: MissionEntity?): MissionEntity { require(mission != null) { @@ -24,10 +26,14 @@ class CreateOrUpdateMission( when (it.actionType) { ActionTypeEnum.CONTROL -> { (it as EnvActionControlEntity).copy( - facade = (it.geom - ?: mission.geom)?.let { geom -> facadeRepository.findFacadeFromGeometry(geom) }, - department = (it.geom - ?: mission.geom)?.let { geom -> departmentRepository.findDepartmentFromGeometry(geom) }, + facade = ( + it.geom + ?: mission.geom + )?.let { geom -> facadeRepository.findFacadeFromGeometry(geom) }, + department = ( + it.geom + ?: mission.geom + )?.let { geom -> departmentRepository.findDepartmentFromGeometry(geom) }, ) } @@ -39,9 +45,15 @@ class CreateOrUpdateMission( Ideally the fallbacks should not be needed, but if coverMissionZone is true and the mission geom is null, or if coverMissionZone is false and the action geom is null, then rather that nothing, better use the geometry that is available, if any. - */ - val geometry = if (surveillance.coverMissionZone == true) (mission.geom - ?: surveillance.geom) else (surveillance.geom ?: mission.geom) + */ + val geometry = if (surveillance.coverMissionZone == true) { + ( + mission.geom + ?: surveillance.geom + ) + } else { + (surveillance.geom ?: mission.geom) + } surveillance.copy( facade = geometry?.let { geom -> facadeRepository.findFacadeFromGeometry(geom) }, department = geometry?.let { geom -> departmentRepository.findDepartmentFromGeometry(geom) }, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/DeleteMission.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/DeleteMission.kt index c6c44ff5c..62d9b8d04 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/DeleteMission.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/DeleteMission.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.missions // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.missions import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.repositories.IMissionRepository diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissionById.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissionById.kt index 688ecc2a3..1dcda3563 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissionById.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissionById.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.missions // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.missions import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissions.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissions.kt index 3423112a6..4e0982c01 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissions.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMissions.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.missions // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.missions import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionEntity @@ -30,7 +32,14 @@ class GetMissions(private val missionRepository: IMissionRepository) { missionTypes = missionTypes, missionStatuses = missionStatuses, seaFronts = seaFronts, - pageable = if (pageNumber != null && pageSize != null) PageRequest.of(pageNumber, pageSize) else Pageable.unpaged(), + pageable = if (pageNumber != null && pageSize != null) { + PageRequest.of( + pageNumber, + pageSize, + ) + } else { + Pageable.unpaged() + }, ) logger.info("Found ${missions.size} mission(s)") diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMonitorEnvMissions.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMonitorEnvMissions.kt index 9cc263b4f..efee067a2 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMonitorEnvMissions.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/GetMonitorEnvMissions.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.missions // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.missions import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionEntity @@ -30,10 +32,14 @@ class GetMonitorEnvMissions(private val missionRepository: IMissionRepository) { missionStatuses = missionStatuses, missionSources = missionSources ?: listOf(MissionSourceEnum.MONITORENV, MissionSourceEnum.MONITORFISH), seaFronts = seaFronts, - pageable = if (pageNumber != null && pageSize != null) PageRequest.of( - pageNumber, - pageSize - ) else Pageable.unpaged(), + pageable = if (pageNumber != null && pageSize != null) { + PageRequest.of( + pageNumber, + pageSize, + ) + } else { + Pageable.unpaged() + }, ) logger.info("Found ${missions.size} mission(s)") diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/natinfs/GetAllNatinfs.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/natinfs/GetAllNatinfs.kt index 3d7de0e97..041c21633 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/natinfs/GetAllNatinfs.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/natinfs/GetAllNatinfs.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.natinfs // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.natinfs import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.natinf.NatinfEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreas.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreas.kt index 25f98fe93..b5e93acae 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreas.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreas.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.regulatoryAreas // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.regulatoryAreas import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetRegulatoryAreaById.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetRegulatoryAreaById.kt index f162f0015..bf377951c 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetRegulatoryAreaById.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetRegulatoryAreaById.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases.regulatoryAreas // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases.regulatoryAreas import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveOutdatedReportings.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveOutdatedReportings.kt index 348d13a4f..c77581229 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveOutdatedReportings.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveOutdatedReportings.kt @@ -8,7 +8,7 @@ import org.springframework.scheduling.annotation.Scheduled @UseCase class ArchiveOutdatedReportings( - private val reportingRepository: IReportingRepository, + private val reportingRepository: IReportingRepository, ) { private val logger: Logger = LoggerFactory.getLogger(ArchiveOutdatedReportings::class.java) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveReportings.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveReportings.kt index 5a5346177..5cb329fdf 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveReportings.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/ArchiveReportings.kt @@ -10,13 +10,13 @@ class ArchiveReportings( private val reportingRepository: IReportingRepository, ) { - private val logger: Logger = LoggerFactory.getLogger(ArchiveReportings::class.java) + private val logger: Logger = LoggerFactory.getLogger(ArchiveReportings::class.java) - @Throws(IllegalArgumentException::class) - fun execute(ids: List) { - logger.info("Archive reportings: $ids") + @Throws(IllegalArgumentException::class) + fun execute(ids: List) { + logger.info("Archive reportings: $ids") - require(ids.isNotEmpty()) { "No reportings to archive" } - return reportingRepository.archiveReportings(ids) - } + require(ids.isNotEmpty()) { "No reportings to archive" } + return reportingRepository.archiveReportings(ids) + } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/DeleteReportings.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/DeleteReportings.kt index 0c3c38931..a6b094baa 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/DeleteReportings.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/reportings/DeleteReportings.kt @@ -10,13 +10,13 @@ class DeleteReportings( private val reportingRepository: IReportingRepository, ) { - private val logger: Logger = LoggerFactory.getLogger(DeleteReportings::class.java) + private val logger: Logger = LoggerFactory.getLogger(DeleteReportings::class.java) - @Throws(IllegalArgumentException::class) - fun execute(ids: List) { - logger.info("Delete reportings: $ids") + @Throws(IllegalArgumentException::class) + fun execute(ids: List) { + logger.info("Delete reportings: $ids") - require(ids.isNotEmpty()) { "No reportings to delete" } - return reportingRepository.deleteReportings(ids) - } + require(ids.isNotEmpty()) { "No reportings to delete" } + return reportingRepository.deleteReportings(ids) + } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/inputs/CreateOrUpdateControlUnitDataInput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/inputs/CreateOrUpdateControlUnitDataInput.kt index 1797289cb..fecdf5e80 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/inputs/CreateOrUpdateControlUnitDataInput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/inputs/CreateOrUpdateControlUnitDataInput.kt @@ -11,8 +11,7 @@ data class CreateOrUpdateControlUnitDataInput( val name: String, val termsNote: String? = null, ) { - fun toControlUnit( - ): ControlUnitEntity { + fun toControlUnit(): ControlUnitEntity { return ControlUnitEntity( id = this.id, areaNote = this.areaNote, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/ControlUnitResourceDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/ControlUnitResourceDataOutput.kt index a620278b0..7a78b7f2e 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/ControlUnitResourceDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/ControlUnitResourceDataOutput.kt @@ -1,7 +1,6 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.publicapi.outputs import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceEntity -import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceType data class ControlUnitResourceDataOutput( val id: Int, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitContactDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitContactDataOutput.kt index 041edac38..14777e0c5 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitContactDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitContactDataOutput.kt @@ -11,7 +11,9 @@ data class FullControlUnitContactDataOutput( val phone: String? = null, ) { companion object { - fun fromFullControlUnitContact(fullControlUnitContact: FullControlUnitContactDTO): FullControlUnitContactDataOutput { + fun fromFullControlUnitContact( + fullControlUnitContact: FullControlUnitContactDTO, + ): FullControlUnitContactDataOutput { val controlUnit = ControlUnitDataOutput.fromControlUnit(fullControlUnitContact.controlUnit) return FullControlUnitContactDataOutput( diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitDataOutput.kt index 156f34ae4..a91c8441c 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitDataOutput.kt @@ -24,7 +24,7 @@ data class FullControlUnitDataOutput( val administration = AdministrationDataOutput.fromAdministration(fullControlUnit.administration) val controlUnitContacts = fullControlUnit.controlUnitContacts.map { ControlUnitContactDataOutput.fromControlUnitContact( - it + it, ) } val controlUnitResources = fullControlUnit.controlUnitResources.map { @@ -32,7 +32,6 @@ data class FullControlUnitDataOutput( } val departmentArea = fullControlUnit.departmentArea?.let { DepartmentAreaDataOutput.fromDepartmentArea(it) } - return FullControlUnitDataOutput( id = requireNotNull(fullControlUnit.controlUnit.id), areaNote = fullControlUnit.controlUnit.areaNote, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitResourceDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitResourceDataOutput.kt index 8f48b5c31..1d67732cd 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitResourceDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/publicapi/outputs/FullControlUnitResourceDataOutput.kt @@ -15,7 +15,9 @@ data class FullControlUnitResourceDataOutput( val type: ControlUnitResourceType, ) { companion object { - fun fromFullControlUnitResource(fullControlUnitResource: FullControlUnitResourceDTO): FullControlUnitResourceDataOutput { + fun fromFullControlUnitResource( + fullControlUnitResource: FullControlUnitResourceDTO, + ): FullControlUnitResourceDataOutput { val base = BaseDataOutput.fromBase(fullControlUnitResource.base) val controlUnit = ControlUnitDataOutput.fromControlUnit(fullControlUnitResource.controlUnit) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsController.kt index ab777bd31..b5589228d 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsController.kt @@ -15,7 +15,6 @@ import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.tags.Tag import jakarta.websocket.server.PathParam -import java.time.ZonedDateTime import org.springframework.format.annotation.DateTimeFormat import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.DeleteMapping @@ -27,79 +26,80 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestController +import java.time.ZonedDateTime @RestController @RequestMapping("/bff/v1/reportings") @Tag(description = "API des Signalements", name = "Reportings") class ReportingsController( - private val createOrUpdateReporting: CreateOrUpdateReporting, - private val getReportingById: GetReportingById, - private val getReportings: GetReportings, - private val deleteReporting: DeleteReporting, - private val deleteReportings: DeleteReportings, - private val archiveReportings: ArchiveReportings, + private val createOrUpdateReporting: CreateOrUpdateReporting, + private val getReportingById: GetReportingById, + private val getReportings: GetReportings, + private val deleteReporting: DeleteReporting, + private val deleteReportings: DeleteReportings, + private val archiveReportings: ArchiveReportings, ) { @GetMapping("") @Operation(summary = "Get reportings") fun getReportingsController( - @Parameter(description = "page number") - @RequestParam(name = "pageNumber") - pageNumber: Int?, - @Parameter(description = "page size") @RequestParam(name = "pageSize") pageSize: Int?, - @Parameter(description = "Reporting created after date") - @RequestParam(name = "startedAfterDateTime", required = false) - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - startedAfterDateTime: ZonedDateTime?, - @Parameter(description = "Reporting created before date") - @RequestParam(name = "startedBeforeDateTime", required = false) - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - startedBeforeDateTime: ZonedDateTime?, - @Parameter(description = "Reporting type") - @RequestParam(name = "reportingType", required = false) - reportingType: List?, - @Parameter(description = "Facades") - @RequestParam(name = "seaFronts", required = false) - seaFronts: List?, - @Parameter(description = "Reporting source types") - @RequestParam(name = "sourcesType", required = false) - sourcesType: List?, - @Parameter(description = "Reporting status") - @RequestParam(name = "status", required = false) - status: List?, + @Parameter(description = "page number") + @RequestParam(name = "pageNumber") + pageNumber: Int?, + @Parameter(description = "page size") @RequestParam(name = "pageSize") pageSize: Int?, + @Parameter(description = "Reporting created after date") + @RequestParam(name = "startedAfterDateTime", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + startedAfterDateTime: ZonedDateTime?, + @Parameter(description = "Reporting created before date") + @RequestParam(name = "startedBeforeDateTime", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + startedBeforeDateTime: ZonedDateTime?, + @Parameter(description = "Reporting type") + @RequestParam(name = "reportingType", required = false) + reportingType: List?, + @Parameter(description = "Facades") + @RequestParam(name = "seaFronts", required = false) + seaFronts: List?, + @Parameter(description = "Reporting source types") + @RequestParam(name = "sourcesType", required = false) + sourcesType: List?, + @Parameter(description = "Reporting status") + @RequestParam(name = "status", required = false) + status: List?, ): List { return getReportings.execute( - pageNumber = pageNumber, - pageSize = pageSize, - reportingType = reportingType, - seaFronts = seaFronts, - sourcesType = sourcesType, - startedAfterDateTime = startedAfterDateTime, - startedBeforeDateTime = startedBeforeDateTime, - status = status, - ) - .map { ReportingDetailedDataOutput.fromReporting(it.first, it.second, it.third) } + pageNumber = pageNumber, + pageSize = pageSize, + reportingType = reportingType, + seaFronts = seaFronts, + sourcesType = sourcesType, + startedAfterDateTime = startedAfterDateTime, + startedBeforeDateTime = startedBeforeDateTime, + status = status, + ) + .map { ReportingDetailedDataOutput.fromReporting(it.first, it.second, it.third) } } @PutMapping("", consumes = ["application/json"]) @Operation(summary = "Create a new reporting") @ResponseStatus(HttpStatus.CREATED) fun createReportingController( - @RequestBody createReporting: CreateOrUpdateReportingDataInput, + @RequestBody createReporting: CreateOrUpdateReportingDataInput, ): ReportingDataOutput { val newReporting = createReporting.toReportingEntity() val createdReporting = createOrUpdateReporting.execute(newReporting) return ReportingDataOutput.fromReporting( - createdReporting.first, - createdReporting.second, - createdReporting.third, + createdReporting.first, + createdReporting.second, + createdReporting.third, ) } @GetMapping("/{id}") @Operation(summary = "Get reporting by id") fun getReportingByIdController( - @PathParam("reporting id") @PathVariable(name = "id") id: Int, + @PathParam("reporting id") @PathVariable(name = "id") id: Int, ): ReportingDataOutput { return getReportingById.execute(id).let { ReportingDataOutput.fromReporting(it.first, it.second, it.third) @@ -109,21 +109,21 @@ class ReportingsController( @PutMapping(value = ["/{id}"], consumes = ["application/json"]) @Operation(summary = "update a reporting") fun updateReportingController( - @PathParam("reporting id") @PathVariable(name = "id") id: Int, - @RequestBody reporting: CreateOrUpdateReportingDataInput, + @PathParam("reporting id") @PathVariable(name = "id") id: Int, + @RequestBody reporting: CreateOrUpdateReportingDataInput, ): ReportingDataOutput { require(id == reporting.id) { "id in path and body must be the same" } return createOrUpdateReporting.execute( - reporting.toReportingEntity(), - ) - .let { ReportingDataOutput.fromReporting(it.first, it.second, it.third) } + reporting.toReportingEntity(), + ) + .let { ReportingDataOutput.fromReporting(it.first, it.second, it.third) } } @DeleteMapping(value = ["/{id}"]) @Operation(summary = "Delete a reporting") @ResponseStatus(HttpStatus.NO_CONTENT) fun deleteController( - @PathParam("Id") @PathVariable(name = "id") id: Int, + @PathParam("Id") @PathVariable(name = "id") id: Int, ) { deleteReporting.execute(id = id) } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/SemaphoresController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/SemaphoresController.kt index d3b49b66e..b8aba5bb4 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/SemaphoresController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/SemaphoresController.kt @@ -14,7 +14,10 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/bff/v1/semaphores") @Tag(description = "API Semaphores", name = "Semaphores") -class SemaphoresController(private val getAllSemaphores: GetAllSemaphores, private val getSemaphoreById: GetSemaphoreById) { +class SemaphoresController( + private val getAllSemaphores: GetAllSemaphores, + private val getSemaphoreById: GetSemaphoreById, +) { @GetMapping("") @Operation(summary = "Get all semaphores") fun getSemaphoresController(): List { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsController.kt index d29024463..ead4d423c 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsController.kt @@ -84,7 +84,7 @@ class ApiAdministrationsController( ): AdministrationDataOutput { requireNotNull(updateAdministrationDataInput.id) { "`id` can't be null." } require(administrationId == updateAdministrationDataInput.id) { - "Body ID ('${updateAdministrationDataInput.id}') doesn't match path ID ('${administrationId}')." + "Body ID ('${updateAdministrationDataInput.id}') doesn't match path ID ('$administrationId')." } val nextAdministration = updateAdministrationDataInput.toAdministration() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesController.kt index 140cff479..375e74a83 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesController.kt @@ -64,7 +64,7 @@ class ApiBasesController( ): BaseDataOutput { requireNotNull(updateBaseDataInput.id) { "`id` can't be null." } require(baseId == updateBaseDataInput.id) { - "Body ID ('${updateBaseDataInput.id}') doesn't match path ID ('${baseId}')." + "Body ID ('${updateBaseDataInput.id}') doesn't match path ID ('$baseId')." } val nextBase = updateBaseDataInput.toBase() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsController.kt index 73b6f8eff..da14d617e 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsController.kt @@ -1,6 +1,7 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.endpoints.publicapi import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.CreateOrUpdateControlUnitContact +import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.DeleteControlUnitContact import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitContactById import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitContacts import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.publicapi.inputs.CreateOrUpdateControlUnitContactDataInput @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.* @Tag(name = "Control Unit Contacts") class ApiControlUnitContactsController( private val createOrUpdateControlUnitContact: CreateOrUpdateControlUnitContact, + private val deleteControlUnitContact: DeleteControlUnitContact, private val getControlUnitContacts: GetControlUnitContacts, private val getControlUnitContactById: GetControlUnitContactById, ) { @@ -32,6 +34,16 @@ class ApiControlUnitContactsController( return ControlUnitContactDataOutput.fromControlUnitContact(createdControlUnitContact) } + @DeleteMapping("/{controlUnitContactId}") + @Operation(summary = "Delete a control unit contact") + fun delete( + @PathParam("Control unit contact ID") + @PathVariable(name = "controlUnitContactId") + controlUnitContactId: Int, + ) { + deleteControlUnitContact.execute(controlUnitContactId) + } + @GetMapping("/{controlUnitContactId}") @Operation(summary = "Get a control unit contact by its ID") fun get( @@ -60,7 +72,7 @@ class ApiControlUnitContactsController( ): ControlUnitContactDataOutput { requireNotNull(updateControlUnitContactDataInput.id) { "`id` can't be null." } require(controlUnitContactId == updateControlUnitContactDataInput.id) { - "Body ID ('${updateControlUnitContactDataInput.id}') doesn't match path ID ('${controlUnitContactId}')." + "Body ID ('${updateControlUnitContactDataInput.id}') doesn't match path ID ('$controlUnitContactId')." } val nextControlUnitContact = updateControlUnitContactDataInput.toControlUnitContact() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesController.kt index 277187fe1..f28df1809 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesController.kt @@ -1,6 +1,7 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.endpoints.publicapi import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.CreateOrUpdateControlUnitResource +import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.DeleteControlUnitResource import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitResourceById import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitResources import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.publicapi.inputs.CreateOrUpdateControlUnitResourceDataInput @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.* @Tag(name = "Control Unit Resources") class ApiControlUnitResourcesController( private val createOrUpdateControlUnitResource: CreateOrUpdateControlUnitResource, + private val deleteControlUnitResource: DeleteControlUnitResource, private val getControlUnitResources: GetControlUnitResources, private val getControlUnitResourceById: GetControlUnitResourceById, ) { @@ -33,6 +35,16 @@ class ApiControlUnitResourcesController( return ControlUnitResourceDataOutput.fromControlUnitResource(createdControlUnitResource) } + @DeleteMapping("/{controlUnitResourceId}") + @Operation(summary = "Delete a control unit resource") + fun delete( + @PathParam("Control unit resource ID") + @PathVariable(name = "controlUnitResourceId") + controlUnitResourceId: Int, + ) { + deleteControlUnitResource.execute(controlUnitResourceId) + } + @GetMapping("/{controlUnitResourceId}") @Operation(summary = "Get a control unit resource by its ID") fun get( @@ -63,7 +75,7 @@ class ApiControlUnitResourcesController( ): ControlUnitResourceDataOutput { requireNotNull(updateControlUnitResourceDataInput.id) { "`id` can't be null." } require(controlUnitResourceId == updateControlUnitResourceDataInput.id) { - "Body ID ('${updateControlUnitResourceDataInput.id}') doesn't match path ID ('${controlUnitResourceId}')." + "Body ID ('${updateControlUnitResourceDataInput.id}') doesn't match path ID ('$controlUnitResourceId')." } val controlUnitResource = updateControlUnitResourceDataInput.toControlUnitResource() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsController.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsController.kt index e70e3fea8..123eee872 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsController.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsController.kt @@ -84,7 +84,7 @@ class ApiControlUnitsController( ): ControlUnitDataOutput { requireNotNull(updateControlUnitDataInput.id) { "`id` can't be null." } require(controlUnitId == updateControlUnitDataInput.id) { - "Body ID ('${updateControlUnitDataInput.id}') doesn't match path ID ('${controlUnitId}')." + "Body ID ('${updateControlUnitDataInput.id}') doesn't match path ID ('$controlUnitId')." } val nextControlUnit = updateControlUnitDataInput.toControlUnit() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/AdministrationModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/AdministrationModel.kt index b61a04f0e..87f0df6c9 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/AdministrationModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/AdministrationModel.kt @@ -41,7 +41,7 @@ data class AdministrationModel( */ fun fromAdministration( administration: AdministrationEntity, - controlUnitModels: List? = mutableListOf() + controlUnitModels: List? = mutableListOf(), ): AdministrationModel { return AdministrationModel( id = administration.id, @@ -67,4 +67,3 @@ data class AdministrationModel( ) } } - diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitContactModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitContactModel.kt index 84a2641ed..5c6ade6c3 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitContactModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitContactModel.kt @@ -41,7 +41,7 @@ data class ControlUnitContactModel( companion object { fun fromControlUnitContact( controlUnitContact: ControlUnitContactEntity, - controlUnitModel: ControlUnitModel + controlUnitModel: ControlUnitModel, ): ControlUnitContactModel { return ControlUnitContactModel( id = controlUnitContact.id, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitModel.kt index 54278e2d1..9c593106b 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitModel.kt @@ -66,7 +66,7 @@ data class ControlUnitModel( administrationModel: AdministrationModel, departmentAreaModel: DepartmentAreaModel? = null, controlUnitContactModels: List? = null, - controlUnitResourceModels: List? = null + controlUnitResourceModels: List? = null, ): ControlUnitModel { return ControlUnitModel( id = controlUnit.id, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitResourceModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitResourceModel.kt index 873f7a0c8..c8a7c4240 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitResourceModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ControlUnitResourceModel.kt @@ -1,12 +1,13 @@ package fr.gouv.cacem.monitorenv.infrastructure.database.model import com.fasterxml.jackson.annotation.JsonBackReference +import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceEntity import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceType import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.dtos.FullControlUnitResourceDTO import jakarta.persistence.* -import org.hibernate.annotations.ColumnTransformer import org.hibernate.annotations.CreationTimestamp +import org.hibernate.annotations.Type import org.hibernate.annotations.UpdateTimestamp import java.time.Instant @@ -37,9 +38,9 @@ data class ControlUnitResourceModel( @Column(name = "photo") var photo: ByteArray? = byteArrayOf(), + @Column(name = "type", nullable = false, columnDefinition = "control_unit_resource_type") @Enumerated(EnumType.STRING) - @Column(name = "type", nullable = false) - @ColumnTransformer(write = "?::control_unit_resource_type") + @Type(PostgreSQLEnumType::class) var type: ControlUnitResourceType, @Column(name = "created_at_utc", nullable = false, updatable = false) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionControlResourceModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionControlResourceModel.kt index 0c81bc103..77ffdb014 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionControlResourceModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionControlResourceModel.kt @@ -24,7 +24,7 @@ data class MissionControlResourceModel( controlUnitResource: ControlUnitResourceEntity, baseModel: BaseModel, missionModel: MissionModel, - controlUnitModel: ControlUnitModel + controlUnitModel: ControlUnitModel, ) = MissionControlResourceModel( ressource = ControlUnitResourceModel( id = requireNotNull(controlUnitResource.id), diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionModel.kt index e4198c12a..f8160bb28 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/MissionModel.kt @@ -129,7 +129,7 @@ data class MissionModel( missionControlUnitModel.unit.toLegacyControlUnit().copy( contact = missionControlUnitModel.contact, - resources = controlUnitResources + resources = controlUnitResources, ) } @@ -159,7 +159,7 @@ data class MissionModel( fun fromMissionEntity( mission: MissionEntity, mapper: ObjectMapper, - baseModelMap: Map + baseModelMap: Map, ): MissionModel { val missionModel = MissionModel( id = mission.id, @@ -198,7 +198,7 @@ data class MissionModel( controlUnitResource, baseModel, missionModel, - controlUnitModel.unit + controlUnitModel.unit, ) } missionModel.controlResources?.addAll(missionControlUnitResourceModels) diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepository.kt index 4dc52b78e..fac967aa4 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepository.kt @@ -12,7 +12,6 @@ import org.springframework.dao.InvalidDataAccessApiUsageException import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional - @Repository class JpaAdministrationRepository( private val dbAdministrationRepository: IDBAdministrationRepository, @@ -21,7 +20,9 @@ class JpaAdministrationRepository( override fun archiveById(administrationId: Int) { val fullAdministration = findById(administrationId) if (fullAdministration.controlUnits.any { !it.isArchived }) { - throw UnarchivedChildException("Cannot archive administration (ID=$administrationId) due to some of its control units not being archived.") + throw UnarchivedChildException( + "Cannot archive administration (ID=$administrationId) due to some of its control units not being archived.", + ) } dbAdministrationRepository.archiveById(administrationId) @@ -30,7 +31,9 @@ class JpaAdministrationRepository( override fun deleteById(administrationId: Int) { val fullAdministration = findById(administrationId) if (fullAdministration.controlUnits.isNotEmpty()) { - throw ForeignKeyConstraintException("Cannot delete administration (ID=$administrationId) due to existing relationships.") + throw ForeignKeyConstraintException( + "Cannot delete administration (ID=$administrationId) due to existing relationships.", + ) } dbAdministrationRepository.deleteById(administrationId) @@ -53,7 +56,7 @@ class JpaAdministrationRepository( } catch (e: InvalidDataAccessApiUsageException) { throw NotFoundException( "Unable to find (and update) control unit administration with `id` = ${administration.id}.", - e + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepository.kt index 490e77dc9..b12798a06 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepository.kt @@ -39,7 +39,7 @@ class JpaBaseRepository( } catch (e: InvalidDataAccessApiUsageException) { throw NotFoundException( "Unable to find (and update) base with `id` = ${base.id}.", - e + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepository.kt index 5307be20b..c881a8b68 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepository.kt @@ -40,7 +40,7 @@ class JpaControlUnitContactRepository( } catch (e: InvalidDataAccessApiUsageException) { throw NotFoundException( "Unable to find (and update) control unit contact with `id` = ${controlUnitContact.id}.", - e + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepository.kt index 86b3f1db7..95bb6cb4b 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepository.kt @@ -55,7 +55,7 @@ class JpaControlUnitRepository( } catch (e: InvalidDataAccessApiUsageException) { throw NotFoundException( "Unable to find (and update) control unit with `id` = ${controlUnit.id}.", - e + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepository.kt index f3f3ffb53..7f804f522 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepository.kt @@ -47,7 +47,7 @@ class JpaControlUnitResourceRepository( } catch (e: InvalidDataAccessApiUsageException) { throw NotFoundException( "Unable to find (and update) control unit resource with `id` = ${controlUnitResource.id}.", - e + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaDepartmentAreaRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaDepartmentAreaRepository.kt index f74e6164e..e60afcadc 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaDepartmentAreaRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaDepartmentAreaRepository.kt @@ -8,7 +8,7 @@ import org.springframework.stereotype.Repository @Repository class JpaDepartmentAreaRepository( - private val dbDepartmentAreasRepository: IDBDepartmentAreaRepository + private val dbDepartmentAreasRepository: IDBDepartmentAreaRepository, ) : IDepartmentAreaRepository { override fun findAll(): List { return dbDepartmentAreasRepository.findAll().map { it.toDepartmentArea() } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepository.kt index d260aa652..70f920f1d 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepository.kt @@ -70,7 +70,7 @@ class JpaMissionRepository( is DataIntegrityViolationException, is InvalidDataAccessApiUsageException -> { throw ControlResourceOrUnitNotFoundException( "Invalid control unit or resource id: not found in referential.", - e + e, ) } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt index d0b011f60..93e8adf05 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt @@ -7,41 +7,41 @@ import fr.gouv.cacem.monitorenv.domain.exceptions.ControlResourceOrUnitNotFoundE import fr.gouv.cacem.monitorenv.domain.repositories.IReportingRepository import fr.gouv.cacem.monitorenv.infrastructure.database.model.ReportingModel import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBReportingRepository -import java.time.Instant import org.springframework.dao.InvalidDataAccessApiUsageException import org.springframework.data.domain.Pageable import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional +import java.time.Instant @Repository class JpaReportingRepository( - private val dbReportingRepository: IDBReportingRepository, + private val dbReportingRepository: IDBReportingRepository, ) : IReportingRepository { override fun findById(reportingId: Int): ReportingEntity { return dbReportingRepository.findById(reportingId).get().toReporting() } override fun findAll( - pageable: Pageable, - reportingType: List?, - seaFronts: List?, - sourcesType: List?, - startedAfter: Instant, - startedBefore: Instant?, - status: List?, + pageable: Pageable, + reportingType: List?, + seaFronts: List?, + sourcesType: List?, + startedAfter: Instant, + startedBefore: Instant?, + status: List?, ): List { val sourcesTypeAsStringArray = sourcesType?.map { it.name } val reportingTypeAsStringArray = reportingType?.map { it.name } return dbReportingRepository.findAll( - pageable, - reportingType = convertToString(reportingTypeAsStringArray), - seaFronts = convertToString(seaFronts), - sourcesType = convertToString(sourcesTypeAsStringArray), - startedAfter = startedAfter, - startedBefore = startedBefore, - status = convertToString(status), - ) - .map { it.toReporting() } + pageable, + reportingType = convertToString(reportingTypeAsStringArray), + seaFronts = convertToString(seaFronts), + sourcesType = convertToString(sourcesTypeAsStringArray), + startedAfter = startedAfter, + startedBefore = startedBefore, + status = convertToString(status), + ) + .map { it.toReporting() } } @Transactional @@ -51,8 +51,8 @@ class JpaReportingRepository( dbReportingRepository.save(reportingModel).toReporting() } catch (e: InvalidDataAccessApiUsageException) { throw ControlResourceOrUnitNotFoundException( - "Invalid control unit or resource id: not found in referential", - e, + "Invalid control unit or resource id: not found in referential", + e, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBFacadeAreasRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBFacadeAreasRepository.kt index e25618a04..959069208 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBFacadeAreasRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBFacadeAreasRepository.kt @@ -38,6 +38,5 @@ interface IDBFacadeAreasRepository : CrudRepository { """, nativeQuery = true, ) - fun findFacadeFromGeometry(geometry: Geometry): String - ? + fun findFacadeFromGeometry(geometry: Geometry): String? } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt index 1a6e74ea8..55f99fcc9 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt @@ -1,19 +1,19 @@ package fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces import fr.gouv.cacem.monitorenv.infrastructure.database.model.ReportingModel -import java.time.Instant import org.hibernate.annotations.DynamicUpdate import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.Modifying import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.CrudRepository +import java.time.Instant @DynamicUpdate interface IDBReportingRepository : CrudRepository { @Query( - value = - """ + value = + """ SELECT * FROM reportings WHERE is_deleted IS FALSE @@ -37,63 +37,63 @@ interface IDBReportingRepository : CrudRepository { ) ORDER BY reporting_id DESC """, - nativeQuery = true, + nativeQuery = true, ) fun findAll( - pageable: Pageable, - reportingType: String?, - seaFronts: String?, - sourcesType: String?, - startedAfter: Instant, - startedBefore: Instant?, - status: String?, + pageable: Pageable, + reportingType: String?, + seaFronts: String?, + sourcesType: String?, + startedAfter: Instant, + startedBefore: Instant?, + status: String?, ): List @Modifying(clearAutomatically = true) @Query( - value = - """ + value = + """ UPDATE reportings SET is_deleted = TRUE WHERE id = :id """, - nativeQuery = true, + nativeQuery = true, ) fun delete(id: Int) @Modifying(clearAutomatically = true) @Query( - value = - """ + value = + """ UPDATE reportings SET is_archived = TRUE WHERE (created_at + make_interval(hours => validity_time)) < NOW() AND is_archived IS FALSE """, - nativeQuery = true, + nativeQuery = true, ) fun archiveOutdatedReportings(): Int @Modifying(clearAutomatically = true) @Query( - value = - """ + value = + """ UPDATE reportings SET is_archived = TRUE WHERE id in (:ids) """, - nativeQuery = true, + nativeQuery = true, ) fun archiveReportings(ids: List) @Modifying(clearAutomatically = true) @Query( - value = - """ + value = + """ UPDATE reportings SET is_deleted = TRUE WHERE id in (:ids) """, - nativeQuery = true, + nativeQuery = true, ) fun deleteReportings(ids: List) } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateAdministrationUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateAdministrationUTests.kt index 893f2133f..c9a475a06 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateAdministrationUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateAdministrationUTests.kt @@ -21,7 +21,7 @@ class CreateOrUpdateAdministrationUTests { fun `execute() should return save() result`() { val newAdministration = AdministrationEntity( isArchived = false, - name = "Administration Name" + name = "Administration Name", ) val expectedAdministration = newAdministration.copy(id = 0) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateControlUnitUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateControlUnitUTests.kt index 3d92621aa..e1d47f3a9 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateControlUnitUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateControlUnitUTests.kt @@ -34,7 +34,7 @@ class CreateOrUpdateControlUnitUTests { val result = CreateOrUpdateControlUnit(controlUnitRepository).execute( - newControlUnit + newControlUnit, ) verify(controlUnitRepository, times(1)).save(newControlUnit) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt index 6616d31ec..cb8acb39f 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt @@ -1,4 +1,6 @@ -package fr.gouv.cacem.monitorenv.domain.use_cases // ktlint-disable package-name +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases import com.nhaarman.mockitokotlin2.anyOrNull import com.nhaarman.mockitokotlin2.given @@ -120,9 +122,9 @@ class CreateOrUpdateMissionUTests { baseRepository, departmentRepository, missionRepository, - facadeAreasRepository + facadeAreasRepository, ).execute( - missionToCreate + missionToCreate, ) // Then diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/DepartmentAreaControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/DepartmentAreaControllerITests.kt index 187579ef3..5817bb831 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/DepartmentAreaControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/DepartmentAreaControllerITests.kt @@ -58,7 +58,7 @@ class DepartmentAreaControllerITests { DepartmentAreaEntity( inseeCode = "2", name = "Department Area Name 2", - ) + ), ) given(getDepartmentAreas.execute()).willReturn(expectedAFulldministrations) @@ -69,5 +69,4 @@ class DepartmentAreaControllerITests { BDDMockito.verify(getDepartmentAreas).execute() } - } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsControllerITests.kt index 2cf048cae..5248c9843 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/ReportingsControllerITests.kt @@ -18,7 +18,6 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.DeleteReportings import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.GetReportingById import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.GetReportings import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.CreateOrUpdateReportingDataInput -import java.time.ZonedDateTime import org.junit.jupiter.api.Test import org.locationtech.jts.geom.Point import org.locationtech.jts.io.WKTReader @@ -35,6 +34,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import java.time.ZonedDateTime @Import(WebSecurityConfig::class, MapperConfiguration::class) @WebMvcTest(value = [(ReportingsController::class)]) @@ -57,235 +57,235 @@ class ReportingsControllerITests { @MockBean private lateinit var deleteReporting: DeleteReporting - @MockBean private lateinit var deleteReportings: DeleteReportings + @MockBean private lateinit var deleteReportings: DeleteReportings - @MockBean private lateinit var archiveReportings: ArchiveReportings + @MockBean private lateinit var archiveReportings: ArchiveReportings - @Test - fun `Should create a new Reporting`() { - // Given - val polygon = - WKTReader() - .read( - "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", - ) - val reporting = - ReportingEntity( - id = 1, - sourceType = SourceTypeEnum.SEMAPHORE, - targetType = TargetTypeEnum.VEHICLE, - vehicleType = VehicleTypeEnum.VESSEL, - geom = polygon, - seaFront = "Facade 1", - description = "description", - reportType = ReportingTypeEnum.INFRACTION_SUSPICION, - theme = "theme", - subThemes = listOf("subTheme1", "subTheme2"), - actionTaken = "actions effectuées blabla", - isControlRequired = true, - isUnitAvailable = true, - createdAt = + @Test + fun `Should create a new Reporting`() { + // Given + val polygon = + WKTReader() + .read( + "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", + ) + val reporting = + ReportingEntity( + id = 1, + sourceType = SourceTypeEnum.SEMAPHORE, + targetType = TargetTypeEnum.VEHICLE, + vehicleType = VehicleTypeEnum.VESSEL, + geom = polygon, + seaFront = "Facade 1", + description = "description", + reportType = ReportingTypeEnum.INFRACTION_SUSPICION, + theme = "theme", + subThemes = listOf("subTheme1", "subTheme2"), + actionTaken = "actions effectuées blabla", + isControlRequired = true, + isUnitAvailable = true, + createdAt = ZonedDateTime.parse( "2022-01-15T04:50:09Z", ), - validityTime = 10, - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) - val semaphore = - SemaphoreEntity( - id = 1, - name = "name", - geom = + validityTime = 10, + isArchived = false, + isDeleted = false, + openBy = "CDA", + ) + val semaphore = + SemaphoreEntity( + id = 1, + name = "name", + geom = WKTReader() .read( "POINT (-61.0 14.0)", ) as Point, - ) + ) - val request = - CreateOrUpdateReportingDataInput( - sourceType = SourceTypeEnum.SEMAPHORE, - semaphoreId = 1, - targetType = TargetTypeEnum.VEHICLE, - vehicleType = VehicleTypeEnum.VESSEL, - geom = polygon, - description = "description", - reportType = ReportingTypeEnum.INFRACTION_SUSPICION, - theme = "theme", - subThemes = listOf("subTheme1", "subTheme2"), - actionTaken = "actions effectuées blabla", - isControlRequired = true, - isUnitAvailable = true, - createdAt = + val request = + CreateOrUpdateReportingDataInput( + sourceType = SourceTypeEnum.SEMAPHORE, + semaphoreId = 1, + targetType = TargetTypeEnum.VEHICLE, + vehicleType = VehicleTypeEnum.VESSEL, + geom = polygon, + description = "description", + reportType = ReportingTypeEnum.INFRACTION_SUSPICION, + theme = "theme", + subThemes = listOf("subTheme1", "subTheme2"), + actionTaken = "actions effectuées blabla", + isControlRequired = true, + isUnitAvailable = true, + createdAt = ZonedDateTime.parse( "2022-01-15T04:50:09Z", ), - validityTime = 10, - isArchived = false, - openBy = "CDA", - ) + validityTime = 10, + isArchived = false, + openBy = "CDA", + ) - given(createOrUpdateReporting.execute(any())).willReturn(Triple(reporting, null, semaphore)) - // When - mockedApi - .perform( - put("/bff/v1/reportings") - .contentType( - MediaType.APPLICATION_JSON, - ) - .content( - objectMapper.writeValueAsString( - request, + given(createOrUpdateReporting.execute(any())).willReturn(Triple(reporting, null, semaphore)) + // When + mockedApi + .perform( + put("/bff/v1/reportings") + .contentType( + MediaType.APPLICATION_JSON, + ) + .content( + objectMapper.writeValueAsString( + request, + ), ), - ), - ) - // Then - .andExpect(status().isCreated) - .andExpect(jsonPath("$.id").value(1)) - .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) - .andExpect(jsonPath("$.targetType").value("VEHICLE")) - .andExpect(jsonPath("$.vehicleType").value("VESSEL")) - .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) - .andExpect(jsonPath("$.seaFront").value("Facade 1")) - .andExpect(jsonPath("$.description").value("description")) - .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) - .andExpect(jsonPath("$.theme").value("theme")) - .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) - .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) - .andExpect( - jsonPath("$.actionTaken").value("actions effectuées blabla"), - ) - .andExpect(jsonPath("$.isControlRequired").value(true)) - .andExpect(jsonPath("$.isUnitAvailable").value(true)) - .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) - .andExpect(jsonPath("$.validityTime").value(10)) - .andExpect(jsonPath("$.isArchived").value(false)) - } - - @Test - fun `Should return the reporting specified in url`() { - // Given - val polygon = - WKTReader() - .read( - "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", ) - val reporting = - ReportingEntity( - id = 1, - sourceType = SourceTypeEnum.SEMAPHORE, - targetType = TargetTypeEnum.VEHICLE, - vehicleType = VehicleTypeEnum.VESSEL, - geom = polygon, - seaFront = "Facade 1", - description = "description", - reportType = ReportingTypeEnum.INFRACTION_SUSPICION, - theme = "theme", - subThemes = listOf("subTheme1", "subTheme2"), - actionTaken = "actions effectuées blabla", - isControlRequired = true, - isUnitAvailable = true, - createdAt = + // Then + .andExpect(status().isCreated) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) + .andExpect(jsonPath("$.targetType").value("VEHICLE")) + .andExpect(jsonPath("$.vehicleType").value("VESSEL")) + .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) + .andExpect(jsonPath("$.seaFront").value("Facade 1")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) + .andExpect(jsonPath("$.theme").value("theme")) + .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) + .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) + .andExpect( + jsonPath("$.actionTaken").value("actions effectuées blabla"), + ) + .andExpect(jsonPath("$.isControlRequired").value(true)) + .andExpect(jsonPath("$.isUnitAvailable").value(true)) + .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) + .andExpect(jsonPath("$.validityTime").value(10)) + .andExpect(jsonPath("$.isArchived").value(false)) + } + + @Test + fun `Should return the reporting specified in url`() { + // Given + val polygon = + WKTReader() + .read( + "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", + ) + val reporting = + ReportingEntity( + id = 1, + sourceType = SourceTypeEnum.SEMAPHORE, + targetType = TargetTypeEnum.VEHICLE, + vehicleType = VehicleTypeEnum.VESSEL, + geom = polygon, + seaFront = "Facade 1", + description = "description", + reportType = ReportingTypeEnum.INFRACTION_SUSPICION, + theme = "theme", + subThemes = listOf("subTheme1", "subTheme2"), + actionTaken = "actions effectuées blabla", + isControlRequired = true, + isUnitAvailable = true, + createdAt = ZonedDateTime.parse( "2022-01-15T04:50:09Z", ), - validityTime = 10, - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) - val semaphore = - SemaphoreEntity( - id = 1, - name = "name", - geom = + validityTime = 10, + isArchived = false, + isDeleted = false, + openBy = "CDA", + ) + val semaphore = + SemaphoreEntity( + id = 1, + name = "name", + geom = WKTReader() .read( "POINT (-61.0 14.0)", ) as Point, - ) + ) - given(getReportingById.execute(any())).willReturn(Triple(reporting, null, semaphore)) + given(getReportingById.execute(any())).willReturn(Triple(reporting, null, semaphore)) - // When - mockedApi - .perform( - get("/bff/v1/reportings/1") - .contentType( - MediaType.APPLICATION_JSON, - ), - ) - // Then - .andExpect(status().isOk) - .andExpect(jsonPath("$.id").value(1)) - .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) - .andExpect(jsonPath("$.targetType").value("VEHICLE")) - .andExpect(jsonPath("$.vehicleType").value("VESSEL")) - .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) - .andExpect(jsonPath("$.seaFront").value("Facade 1")) - .andExpect(jsonPath("$.description").value("description")) - .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) - .andExpect(jsonPath("$.theme").value("theme")) - .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) - .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) - .andExpect( - jsonPath("$.actionTaken").value("actions effectuées blabla"), - ) - .andExpect(jsonPath("$.isControlRequired").value(true)) - .andExpect(jsonPath("$.isUnitAvailable").value(true)) - .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) - .andExpect(jsonPath("$.validityTime").value(10)) - .andExpect(jsonPath("$.isArchived").value(false)) - } - - @Test - fun `Should return all reportings`() { - // Given - val polygon = - WKTReader() - .read( - "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", + // When + mockedApi + .perform( + get("/bff/v1/reportings/1") + .contentType( + MediaType.APPLICATION_JSON, + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) + .andExpect(jsonPath("$.targetType").value("VEHICLE")) + .andExpect(jsonPath("$.vehicleType").value("VESSEL")) + .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) + .andExpect(jsonPath("$.seaFront").value("Facade 1")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) + .andExpect(jsonPath("$.theme").value("theme")) + .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) + .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) + .andExpect( + jsonPath("$.actionTaken").value("actions effectuées blabla"), ) - val reporting = - ReportingEntity( - id = 1, - sourceType = SourceTypeEnum.SEMAPHORE, - targetType = TargetTypeEnum.VEHICLE, - vehicleType = VehicleTypeEnum.VESSEL, - geom = polygon, - seaFront = "Facade 1", - description = "description", - reportType = ReportingTypeEnum.INFRACTION_SUSPICION, - theme = "theme", - subThemes = listOf("subTheme1", "subTheme2"), - actionTaken = "actions effectuées blabla", - isControlRequired = true, - isUnitAvailable = true, - createdAt = + .andExpect(jsonPath("$.isControlRequired").value(true)) + .andExpect(jsonPath("$.isUnitAvailable").value(true)) + .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) + .andExpect(jsonPath("$.validityTime").value(10)) + .andExpect(jsonPath("$.isArchived").value(false)) + } + + @Test + fun `Should return all reportings`() { + // Given + val polygon = + WKTReader() + .read( + "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", + ) + val reporting = + ReportingEntity( + id = 1, + sourceType = SourceTypeEnum.SEMAPHORE, + targetType = TargetTypeEnum.VEHICLE, + vehicleType = VehicleTypeEnum.VESSEL, + geom = polygon, + seaFront = "Facade 1", + description = "description", + reportType = ReportingTypeEnum.INFRACTION_SUSPICION, + theme = "theme", + subThemes = listOf("subTheme1", "subTheme2"), + actionTaken = "actions effectuées blabla", + isControlRequired = true, + isUnitAvailable = true, + createdAt = ZonedDateTime.parse( "2022-01-15T04:50:09Z", ), - validityTime = 10, - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) - val semaphore = - SemaphoreEntity( - id = 1, - name = "semaphore name", - geom = + validityTime = 10, + isArchived = false, + isDeleted = false, + openBy = "CDA", + ) + val semaphore = + SemaphoreEntity( + id = 1, + name = "semaphore name", + geom = WKTReader() .read( "POINT (-61.0 14.0)", ) as Point, - ) - given( + ) + given( getReportings.execute( pageNumber = anyOrNull(), pageSize = anyOrNull(), @@ -297,160 +297,160 @@ class ReportingsControllerITests { status = any(), ), ) - .willReturn(listOf(Triple(reporting, null, semaphore))) + .willReturn(listOf(Triple(reporting, null, semaphore))) - // When - mockedApi - .perform(get("/bff/v1/reportings")) - // Then - .andExpect(status().isOk) - } + // When + mockedApi + .perform(get("/bff/v1/reportings")) + // Then + .andExpect(status().isOk) + } - @Test - fun `Should update a reporting`() { - // Given - val polygon = - WKTReader() - .read( - "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", - ) - val updatedReporting = - ReportingEntity( - id = 1, - sourceType = SourceTypeEnum.SEMAPHORE, - targetType = TargetTypeEnum.VEHICLE, - vehicleType = VehicleTypeEnum.VESSEL, - geom = polygon, - seaFront = "Facade 1", - description = "description", - reportType = ReportingTypeEnum.INFRACTION_SUSPICION, - theme = "theme", - subThemes = listOf("subTheme1", "subTheme2"), - actionTaken = "actions effectuées blabla", - isControlRequired = true, - isUnitAvailable = true, - createdAt = - ZonedDateTime.parse( - "2022-01-15T04:50:09Z", - ), - validityTime = 10, - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) - val semaphore = - SemaphoreEntity( - id = 1, - name = "name", - geom = - WKTReader() - .read( - "POINT (-61.0 14.0)", - ) as - Point, - ) - val updateRequestBody = - objectMapper.writeValueAsString( - CreateOrUpdateReportingDataInput( + @Test + fun `Should update a reporting`() { + // Given + val polygon = + WKTReader() + .read( + "MULTIPOLYGON (((-61.0 14.0, -61.0 15.0, -60.0 15.0, -60.0 14.0, -61.0 14.0)))", + ) + val updatedReporting = + ReportingEntity( id = 1, sourceType = SourceTypeEnum.SEMAPHORE, targetType = TargetTypeEnum.VEHICLE, vehicleType = VehicleTypeEnum.VESSEL, geom = polygon, + seaFront = "Facade 1", description = "description", reportType = ReportingTypeEnum.INFRACTION_SUSPICION, theme = "theme", - subThemes = - listOf( - "subTheme1", - "subTheme2", - ), + subThemes = listOf("subTheme1", "subTheme2"), actionTaken = "actions effectuées blabla", isControlRequired = true, isUnitAvailable = true, createdAt = - ZonedDateTime.parse( - "2022-01-15T04:50:09Z", - ), + ZonedDateTime.parse( + "2022-01-15T04:50:09Z", + ), validityTime = 10, isArchived = false, + isDeleted = false, openBy = "CDA", - ), - ) + ) + val semaphore = + SemaphoreEntity( + id = 1, + name = "name", + geom = + WKTReader() + .read( + "POINT (-61.0 14.0)", + ) as + Point, + ) + val updateRequestBody = + objectMapper.writeValueAsString( + CreateOrUpdateReportingDataInput( + id = 1, + sourceType = SourceTypeEnum.SEMAPHORE, + targetType = TargetTypeEnum.VEHICLE, + vehicleType = VehicleTypeEnum.VESSEL, + geom = polygon, + description = "description", + reportType = ReportingTypeEnum.INFRACTION_SUSPICION, + theme = "theme", + subThemes = + listOf( + "subTheme1", + "subTheme2", + ), + actionTaken = "actions effectuées blabla", + isControlRequired = true, + isUnitAvailable = true, + createdAt = + ZonedDateTime.parse( + "2022-01-15T04:50:09Z", + ), + validityTime = 10, + isArchived = false, + openBy = "CDA", + ), + ) - given(createOrUpdateReporting.execute(any())) - .willReturn(Triple(updatedReporting, null, semaphore)) + given(createOrUpdateReporting.execute(any())) + .willReturn(Triple(updatedReporting, null, semaphore)) - // When - mockedApi - .perform( - put("/bff/v1/reportings/1") - .content(updateRequestBody) - .contentType( - MediaType.APPLICATION_JSON, - ), - ) - // Then - .andExpect(status().isOk) - .andExpect(jsonPath("$.id").value(1)) - .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) - .andExpect(jsonPath("$.targetType").value("VEHICLE")) - .andExpect(jsonPath("$.vehicleType").value("VESSEL")) - .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) - .andExpect(jsonPath("$.seaFront").value("Facade 1")) - .andExpect(jsonPath("$.description").value("description")) - .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) - .andExpect(jsonPath("$.theme").value("theme")) - .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) - .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) - .andExpect( - jsonPath("$.actionTaken").value("actions effectuées blabla"), - ) - .andExpect(jsonPath("$.isControlRequired").value(true)) - .andExpect(jsonPath("$.isUnitAvailable").value(true)) - .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) - .andExpect(jsonPath("$.validityTime").value(10)) - .andExpect(jsonPath("$.isArchived").value(false)) - } + // When + mockedApi + .perform( + put("/bff/v1/reportings/1") + .content(updateRequestBody) + .contentType( + MediaType.APPLICATION_JSON, + ), + ) + // Then + .andExpect(status().isOk) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.sourceType").value("SEMAPHORE")) + .andExpect(jsonPath("$.targetType").value("VEHICLE")) + .andExpect(jsonPath("$.vehicleType").value("VESSEL")) + .andExpect(jsonPath("$.geom.type").value("MultiPolygon")) + .andExpect(jsonPath("$.seaFront").value("Facade 1")) + .andExpect(jsonPath("$.description").value("description")) + .andExpect(jsonPath("$.reportType").value("INFRACTION_SUSPICION")) + .andExpect(jsonPath("$.theme").value("theme")) + .andExpect(jsonPath("$.subThemes[0]").value("subTheme1")) + .andExpect(jsonPath("$.subThemes[1]").value("subTheme2")) + .andExpect( + jsonPath("$.actionTaken").value("actions effectuées blabla"), + ) + .andExpect(jsonPath("$.isControlRequired").value(true)) + .andExpect(jsonPath("$.isUnitAvailable").value(true)) + .andExpect(jsonPath("$.createdAt").value("2022-01-15T04:50:09Z")) + .andExpect(jsonPath("$.validityTime").value(10)) + .andExpect(jsonPath("$.isArchived").value(false)) + } - @Test - fun `Should delete a reporting`() { - // When - mockedApi - .perform(delete("/bff/v1/reportings/123")) - // Then - .andExpect(status().isNoContent()) + @Test + fun `Should delete a reporting`() { + // When + mockedApi + .perform(delete("/bff/v1/reportings/123")) + // Then + .andExpect(status().isNoContent()) - Mockito.verify(deleteReporting).execute(123) - } + Mockito.verify(deleteReporting).execute(123) + } - @Test - fun `Should archive multiple reportings`() { - // When - mockedApi - .perform( - put("/bff/v1/reportings/archive") - .content(objectMapper.writeValueAsString(listOf(1, 2, 3))) - .contentType(MediaType.APPLICATION_JSON), - ) - // Then - .andExpect(status().isNoContent()) + @Test + fun `Should archive multiple reportings`() { + // When + mockedApi + .perform( + put("/bff/v1/reportings/archive") + .content(objectMapper.writeValueAsString(listOf(1, 2, 3))) + .contentType(MediaType.APPLICATION_JSON), + ) + // Then + .andExpect(status().isNoContent()) - Mockito.verify(archiveReportings).execute(listOf(1, 2, 3)) - } + Mockito.verify(archiveReportings).execute(listOf(1, 2, 3)) + } - @Test - fun `Should delete multiple reportings`() { - // When - mockedApi - .perform( - put("/bff/v1/reportings/delete") - .content(objectMapper.writeValueAsString(listOf(1, 2, 3))) - .contentType(MediaType.APPLICATION_JSON), - ) - // Then - .andExpect(status().isNoContent()) + @Test + fun `Should delete multiple reportings`() { + // When + mockedApi + .perform( + put("/bff/v1/reportings/delete") + .content(objectMapper.writeValueAsString(listOf(1, 2, 3))) + .contentType(MediaType.APPLICATION_JSON), + ) + // Then + .andExpect(status().isNoContent()) - Mockito.verify(deleteReportings).execute(listOf(1, 2, 3)) - } + Mockito.verify(deleteReportings).execute(listOf(1, 2, 3)) + } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsControllerITests.kt index 8748d9d90..33fa40220 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiAdministrationsControllerITests.kt @@ -112,7 +112,7 @@ class ApiAdministrationsControllerITests { name = "Administration Name 2", ), controlUnits = listOf(), - ) + ), ) given(getAdministrations.execute()).willReturn(expectedAFulldministrations) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesControllerITests.kt index d36e2b92b..381ece240 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiBasesControllerITests.kt @@ -113,7 +113,7 @@ class ApiBasesControllerITests { name = "Base Name 2", ), controlUnitResources = listOf(), - ) + ), ) given(getBases.execute()).willReturn(expectedFullBases) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsControllerITests.kt index 0b07d0c29..499416dbc 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitContactsControllerITests.kt @@ -7,6 +7,7 @@ import fr.gouv.cacem.monitorenv.config.WebSecurityConfig import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitContactEntity import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitEntity import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.CreateOrUpdateControlUnitContact +import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.DeleteControlUnitContact import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitContactById import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitContacts import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.dtos.FullControlUnitContactDTO @@ -35,6 +36,9 @@ class ApiControlUnitContactsControllerITests { @MockBean private lateinit var createOrUpdateControlUnitContact: CreateOrUpdateControlUnitContact + @MockBean + private lateinit var deleteControlUnitContact: DeleteControlUnitContact + @MockBean private lateinit var getControlUnitContactById: GetControlUnitContactById @@ -63,7 +67,7 @@ class ApiControlUnitContactsControllerITests { val requestBody = objectMapper.writeValueAsString(newControlUnitContactData) given(createOrUpdateControlUnitContact.execute(controlUnitContact = any())).willReturn( - expectedCreatedControlUnitContact + expectedCreatedControlUnitContact, ) mockMvc.perform( @@ -93,7 +97,7 @@ class ApiControlUnitContactsControllerITests { email = null, name = "Contact Name", phone = null, - ) + ), ) val requestedId = 1 @@ -145,7 +149,7 @@ class ApiControlUnitContactsControllerITests { name = "Contact Name 2", phone = null, ), - ) + ), ) given(getControlUnitContacts.execute()).willReturn(expectedFullControlUnitContacts) @@ -177,7 +181,7 @@ class ApiControlUnitContactsControllerITests { val requestBody = objectMapper.writeValueAsString(nextControlUnitContactData) given(createOrUpdateControlUnitContact.execute(controlUnitContact = any())).willReturn( - expectedUpdatedControlUnitContact + expectedUpdatedControlUnitContact, ) mockMvc.perform( diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesControllerITests.kt index d6fc9bbb9..730b4a40b 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitResourcesControllerITests.kt @@ -9,6 +9,7 @@ import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitEntity import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceEntity import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.ControlUnitResourceType import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.CreateOrUpdateControlUnitResource +import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.DeleteControlUnitResource import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitResourceById import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.GetControlUnitResources import fr.gouv.cacem.monitorenv.domain.use_cases.controlUnit.dtos.FullControlUnitResourceDTO @@ -37,6 +38,9 @@ class ApiControlUnitResourcesControllerITests { @MockBean private lateinit var createOrUpdateControlUnitResource: CreateOrUpdateControlUnitResource + @MockBean + private lateinit var deleteControlUnitResource: DeleteControlUnitResource + @MockBean private lateinit var getControlUnitResourceById: GetControlUnitResourceById @@ -69,7 +73,7 @@ class ApiControlUnitResourcesControllerITests { val requestBody = objectMapper.writeValueAsString(newControlUnitData) given(createOrUpdateControlUnitResource.execute(controlUnitResource = any())).willReturn( - expectedCreatedControlUnitResource + expectedCreatedControlUnitResource, ) mockMvc.perform( @@ -175,7 +179,7 @@ class ApiControlUnitResourcesControllerITests { photo = null, type = ControlUnitResourceType.BARGE, ), - ) + ), ) given(getControlUnitResources.execute()).willReturn(expectedFullControlUnitResources) @@ -211,7 +215,7 @@ class ApiControlUnitResourcesControllerITests { val requestBody = objectMapper.writeValueAsString(nextControlUnitData) given(createOrUpdateControlUnitResource.execute(controlUnitResource = any())).willReturn( - expectedUpdatedControlUnitResource + expectedUpdatedControlUnitResource, ) mockMvc.perform( diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsControllerITests.kt index fe707b2b1..a7edb9fda 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiControlUnitsControllerITests.kt @@ -71,7 +71,7 @@ class ApiControlUnitsControllerITests { val requestBody = objectMapper.writeValueAsString(newControlUnitData) given(createOrUpdateControlUnit.execute(controlUnit = any())).willReturn( - expectedCreatedControlUnit + expectedCreatedControlUnit, ) mockMvc.perform( @@ -153,7 +153,7 @@ class ApiControlUnitsControllerITests { ), controlUnitContacts = listOf(), controlUnitResources = listOf(), - ) + ), ) given(getControlUnits.execute()).willReturn(expectedControlUnits) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiLegacyControlUnitsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiLegacyControlUnitsControllerITests.kt index 899dc4ce8..01943df8a 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiLegacyControlUnitsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/publicapi/ApiLegacyControlUnitsControllerITests.kt @@ -41,7 +41,7 @@ class ApiLegacyControlUnitsControllerITests { controlUnitId = 0, name = "Vedette", type = ControlUnitResourceType.BARGE, - ) + ), ), ) given(getLegacyControlUnits.execute()).willReturn(listOf(controlUnit)) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepositoryITests.kt index e02bb0559..d5065d243 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaAdministrationRepositoryITests.kt @@ -64,10 +64,10 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { administration = AdministrationEntity( id = 1, isArchived = false, - name = "Affaires Maritimes" + name = "Affaires Maritimes", ), controlUnits = listOf(), - ) + ), ) assertThat(foundFullAdministrations[34]).isEqualTo( @@ -75,10 +75,10 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { administration = AdministrationEntity( id = 2006, isArchived = false, - name = "FOSIT" + name = "FOSIT", ), controlUnits = listOf(), - ) + ), ) } @@ -92,7 +92,7 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { administration = AdministrationEntity( id = 6, isArchived = false, - name = "Gendarmerie Nationale" + name = "Gendarmerie Nationale", ), controlUnits = listOf( ControlUnitEntity( @@ -114,7 +114,7 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { termsNote = null, ), ), - ) + ), ) } @@ -126,7 +126,7 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { val newAdministration = AdministrationEntity( isArchived = false, - name = "Administration Name" + name = "Administration Name", ) val createdAdministration = jpaAdministrationRepository.save(newAdministration) @@ -139,7 +139,7 @@ class JpaAdministrationRepositoryITests : AbstractDBTests() { val nextAdministration = AdministrationEntity( id = 2007, isArchived = false, - name = "Updated Administration Name" + name = "Updated Administration Name", ) val updatedAdministration = jpaAdministrationRepository.save(nextAdministration) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt index d3348c194..781bae27d 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaBaseRepositoryITests.kt @@ -26,7 +26,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { id = 1, latitude = 43.295765, longitude = 5.375486, - name = "Marseille" + name = "Marseille", ), controlUnitResources = listOf( ControlUnitResourceEntity( @@ -36,7 +36,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Semi-rigide 1", note = null, photo = null, - type = ControlUnitResourceType.BARGE + type = ControlUnitResourceType.BARGE, ), ControlUnitResourceEntity( id = 2, @@ -45,7 +45,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Semi-rigide 2", note = null, photo = null, - type = ControlUnitResourceType.BARGE + type = ControlUnitResourceType.BARGE, ), ), ), @@ -57,7 +57,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { id = 3, latitude = 51.035534, longitude = 2.372845, - name = "Dunkerque" + name = "Dunkerque", ), controlUnitResources = listOf( ControlUnitResourceEntity( @@ -67,7 +67,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Voiture", note = null, photo = null, - type = ControlUnitResourceType.CAR + type = ControlUnitResourceType.CAR, ), ControlUnitResourceEntity( id = 7, @@ -76,7 +76,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Semi-rigide", note = null, photo = null, - type = ControlUnitResourceType.BARGE + type = ControlUnitResourceType.BARGE, ), ControlUnitResourceEntity( id = 8, @@ -85,7 +85,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "PAM Jeanne Barret", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ControlUnitResourceEntity( id = 9, @@ -94,7 +94,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "PAM Themis", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ControlUnitResourceEntity( id = 10, @@ -103,7 +103,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "ALTAIR", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ControlUnitResourceEntity( id = 11, @@ -112,7 +112,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "PHEROUSA", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ControlUnitResourceEntity( id = 12, @@ -121,7 +121,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "ARIOLA", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ), ), @@ -139,7 +139,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { id = 2, latitude = 48.648105, longitude = -2.013144, - name = "Saint-Malo" + name = "Saint-Malo", ), controlUnitResources = listOf( ControlUnitResourceEntity( @@ -149,7 +149,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Semi-rigide 1", note = null, photo = null, - type = ControlUnitResourceType.BARGE + type = ControlUnitResourceType.BARGE, ), ControlUnitResourceEntity( id = 4, @@ -158,7 +158,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "Semi-rigide 2", note = null, photo = null, - type = ControlUnitResourceType.BARGE + type = ControlUnitResourceType.BARGE, ), ControlUnitResourceEntity( id = 6, @@ -167,7 +167,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { name = "AR VECHEN", note = null, photo = null, - type = ControlUnitResourceType.FRIGATE + type = ControlUnitResourceType.FRIGATE, ), ), ), @@ -183,7 +183,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { val newBase = BaseEntity( latitude = 1.2, longitude = 3.4, - name = "Base Name" + name = "Base Name", ) val createdBase = jpaBaseRepository.save(newBase) @@ -197,7 +197,7 @@ class JpaBaseRepositoryITests : AbstractDBTests() { id = 4, latitude = 5.6, longitude = 7.8, - name = "Updated Base Name" + name = "Updated Base Name", ) val updatedBase = jpaBaseRepository.save(nextBase) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepositoryITests.kt index bba1a98b5..9a6cfe12d 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitContactRepositoryITests.kt @@ -29,7 +29,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "Cultures marines – DDTM 40", - termsNote = null + termsNote = null, ), controlUnitContact = ControlUnitContactEntity( id = 1, @@ -37,7 +37,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { name = "Contact 1", phone = null, ), - ) + ), ) assertThat(foundFullControlUnitContacts[2]).isEqualTo( @@ -49,7 +49,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "DPM – DDTM 14", - termsNote = null + termsNote = null, ), controlUnitContact = ControlUnitContactEntity( id = 3, @@ -57,7 +57,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { name = "Contact 3", phone = null, ), - ) + ), ) } @@ -75,7 +75,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "Cultures marines – DDTM 40", - termsNote = null + termsNote = null, ), controlUnitContact = ControlUnitContactEntity( id = 1, @@ -83,7 +83,7 @@ class JpaControlUnitContactRepositoryITests : AbstractDBTests() { name = "Contact 1", phone = null, ), - ) + ), ) } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt index ea80c8eb5..0b50ac7df 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitRepositoryITests.kt @@ -123,7 +123,7 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { note = null, photo = null, type = ControlUnitResourceType.BARGE, - ) + ), ), FullControlUnitResourceDTO( base = BaseEntity( @@ -149,10 +149,10 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { note = null, photo = null, type = ControlUnitResourceType.BARGE, - ) - ) + ), + ), ), - ) + ), ) assertThat(foundFullControlUnits[32]).isEqualTo( @@ -173,7 +173,7 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { ), controlUnitContacts = listOf(), controlUnitResources = listOf(), - ) + ), ) } @@ -187,7 +187,7 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { administration = AdministrationEntity( id = 1005, isArchived = false, - name = "DDTM" + name = "DDTM", ), controlUnit = ControlUnitEntity( id = 10000, @@ -204,15 +204,15 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { controlUnitId = 10000, email = null, name = "Contact 1", - phone = null + phone = null, ), ControlUnitContactEntity( id = 2, controlUnitId = 10000, email = null, name = "Contact 2", - phone = null - ) + phone = null, + ), ), controlUnitResources = listOf( FullControlUnitResourceDTO( @@ -239,7 +239,7 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { note = null, photo = null, type = ControlUnitResourceType.BARGE, - ) + ), ), FullControlUnitResourceDTO( base = BaseEntity( @@ -268,7 +268,7 @@ class JpaControlUnitRepositoryITests : AbstractDBTests() { ), ), ), - ) + ), ) } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepositoryITests.kt index 82c7f85e8..185a2d95c 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaControlUnitResourceRepositoryITests.kt @@ -30,7 +30,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { id = 1, latitude = 43.295765, longitude = 5.375486, - name = "Marseille" + name = "Marseille", ), controlUnit = ControlUnitEntity( id = 10000, @@ -39,7 +39,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "Cultures marines – DDTM 40", - termsNote = null + termsNote = null, ), controlUnitResource = ControlUnitResourceEntity( id = 1, @@ -50,7 +50,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { photo = null, type = ControlUnitResourceType.BARGE, ), - ) + ), ) assertThat(foundFullControlUnitResources[11]).isEqualTo( @@ -59,7 +59,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { id = 3, latitude = 51.035534, longitude = 2.372845, - name = "Dunkerque" + name = "Dunkerque", ), controlUnit = ControlUnitEntity( id = 10018, @@ -68,7 +68,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "DREAL Pays-de-La-Loire", - termsNote = null + termsNote = null, ), controlUnitResource = ControlUnitResourceEntity( id = 12, @@ -79,7 +79,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { photo = null, type = ControlUnitResourceType.FRIGATE, ), - ) + ), ) } @@ -94,7 +94,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { id = 1, latitude = 43.295765, longitude = 5.375486, - name = "Marseille" + name = "Marseille", ), controlUnit = ControlUnitEntity( id = 10000, @@ -103,7 +103,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { departmentAreaInseeCode = null, isArchived = false, name = "Cultures marines – DDTM 40", - termsNote = null + termsNote = null, ), controlUnitResource = ControlUnitResourceEntity( id = 1, @@ -114,7 +114,7 @@ class JpaControlUnitResourceRepositoryITests : AbstractDBTests() { photo = null, type = ControlUnitResourceType.BARGE, ), - ) + ), ) } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt index 12c342ec8..64dc02f59 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaMissionRepositoryITests.kt @@ -88,7 +88,7 @@ class JpaMissionRepositoryITests : AbstractDBTests() { controlUnitId = 10004, name = "PAM Jeanne Barret", type = ControlUnitResourceType.BARGE, - ) + ), ), ), ), @@ -113,7 +113,7 @@ class JpaMissionRepositoryITests : AbstractDBTests() { assertThat(newMissionCreated.envActions?.get(1)?.facade).isEqualTo("Facade 2") assertThat(newMissionCreated.envActions?.get(1)?.department).isEqualTo("Department 2") assertThat((newMissionCreated.envActions?.get(2) as EnvActionNoteEntity).observations).isEqualTo( - noteObservations + noteObservations, ) val missions = jpaMissionRepository.findAll( @@ -153,7 +153,7 @@ class JpaMissionRepositoryITests : AbstractDBTests() { controlUnitId = 10004, name = "PAM Jeanne Barret", type = ControlUnitResourceType.BARGE, - ) + ), ), ), ), @@ -229,7 +229,7 @@ class JpaMissionRepositoryITests : AbstractDBTests() { controlUnitId = 5, name = "PAM Jeanne Barret", type = ControlUnitResourceType.BARGE, - ) + ), ), ), ), @@ -264,8 +264,8 @@ class JpaMissionRepositoryITests : AbstractDBTests() { name = "PAM Jeanne Barret", administration = "", isArchived = false, - resources = listOf() - ) + resources = listOf(), + ), ), isGeometryComputedFromControls = false, ) @@ -473,7 +473,7 @@ class JpaMissionRepositoryITests : AbstractDBTests() { missionSources = listOf( MissionSourceEnum.MONITORFISH, MissionSourceEnum.POSEIDON_CACEM, - MissionSourceEnum.POSEIDON_CNSP + MissionSourceEnum.POSEIDON_CNSP, ), pageable = Pageable.unpaged(), ) diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingITests.kt index 65c353991..24cfb7ebc 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingITests.kt @@ -4,13 +4,13 @@ import fr.gouv.cacem.monitorenv.domain.entities.VehicleTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.reporting.ReportingEntity import fr.gouv.cacem.monitorenv.domain.entities.reporting.SourceTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.reporting.TargetTypeEnum -import java.time.ZonedDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional +import java.time.ZonedDateTime @SpringBootTest(properties = ["monitorenv.scheduling.enable=false"]) class JpaReportingITests : AbstractDBTests() { @@ -25,12 +25,12 @@ class JpaReportingITests : AbstractDBTests() { // When val newReporting = - ReportingEntity( - createdAt = ZonedDateTime.parse("2023-04-01T00:00:00Z"), - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) + ReportingEntity( + createdAt = ZonedDateTime.parse("2023-04-01T00:00:00Z"), + isArchived = false, + isDeleted = false, + openBy = "CDA", + ) val createdReporting = jpaReportingRepository.save(newReporting) // Then @@ -58,15 +58,15 @@ class JpaReportingITests : AbstractDBTests() { @Test fun `findAll should return all reportings`() { val reportings = - jpaReportingRepository.findAll( - Pageable.unpaged(), - startedAfter = ZonedDateTime.parse("2022-01-01T00:01:00Z").toInstant(), - startedBefore = null, - reportingType = null, - seaFronts = null, - sourcesType = null, - status = null, - ) + jpaReportingRepository.findAll( + Pageable.unpaged(), + startedAfter = ZonedDateTime.parse("2022-01-01T00:01:00Z").toInstant(), + startedBefore = null, + reportingType = null, + seaFronts = null, + sourcesType = null, + status = null, + ) assertThat(reportings.size).isEqualTo(5) } @@ -80,14 +80,14 @@ class JpaReportingITests : AbstractDBTests() { // When val existingReporting = jpaReportingRepository.findById(1) val updatedReporting = - existingReporting.copy( - sourceType = SourceTypeEnum.SEMAPHORE, - semaphoreId = 23, - createdAt = ZonedDateTime.parse("2023-04-01T00:00:00Z"), - isArchived = false, - isDeleted = false, - openBy = "CDA", - ) + existingReporting.copy( + sourceType = SourceTypeEnum.SEMAPHORE, + semaphoreId = 23, + createdAt = ZonedDateTime.parse("2023-04-01T00:00:00Z"), + isArchived = false, + isDeleted = false, + openBy = "CDA", + ) val savedReporting = jpaReportingRepository.save(updatedReporting) // Then diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/MapOrElseEmptyUTest.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/MapOrElseEmptyUTest.kt index 4f87eb0fc..22cd07ae3 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/MapOrElseEmptyUTest.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/MapOrElseEmptyUTest.kt @@ -1,8 +1,8 @@ package fr.gouv.cacem.monitorenv.utils -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test @DisplayName("utils/mapOrElseEmpty()") class MapOrElseEmptyUTest { diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireIdsUTest.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireIdsUTest.kt index 03320581f..9389126e5 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireIdsUTest.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireIdsUTest.kt @@ -1,8 +1,8 @@ package fr.gouv.cacem.monitorenv.utils -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test @DisplayName("utils/requireIds()") class RequireIdsUTest { diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireNotNullListUTest.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireNotNullListUTest.kt index f3437417b..5255cc12a 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireNotNullListUTest.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequireNotNullListUTest.kt @@ -1,8 +1,8 @@ package fr.gouv.cacem.monitorenv.utils -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test @DisplayName("utils/requireNotNullList()") class RequireNotNullListUTest { diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequirePresentUTest.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequirePresentUTest.kt index f0338c85d..83fbe6624 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequirePresentUTest.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/utils/RequirePresentUTest.kt @@ -1,8 +1,8 @@ package fr.gouv.cacem.monitorenv.utils -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import java.util.Optional @DisplayName("utils/_root_ide_package_.fr.gouv.cacem.monitorenv.utils.requirePresent()") diff --git a/frontend/.husky/pre-commit b/frontend/.husky/pre-commit index f22ecd59d..582752578 100755 --- a/frontend/.husky/pre-commit +++ b/frontend/.husky/pre-commit @@ -3,4 +3,5 @@ [ -n "$CI" ] && exit 0 +make lint-back npx lint-staged --cwd ./frontend diff --git a/frontend/cypress/e2e/back_office/administration_form.spec.ts b/frontend/cypress/e2e/back_office/administration_form.spec.ts index 8f8bed997..3e695193e 100644 --- a/frontend/cypress/e2e/back_office/administration_form.spec.ts +++ b/frontend/cypress/e2e/back_office/administration_form.spec.ts @@ -59,7 +59,7 @@ context('Back Office > Administration Form', () => { cy.intercept('POST', `/api/v1/administrations/2007/archive`).as('archiveAdministration') cy.getTableRowById(2007).clickButton('Archiver cette administration') - cy.clickButton('Confirmer') + cy.clickButton('Archiver') cy.wait('@archiveAdministration') @@ -73,7 +73,7 @@ context('Back Office > Administration Form', () => { cy.intercept('DELETE', `/api/v1/administrations/2007`).as('deleteAdministration') cy.getTableRowById(2007).clickButton('Supprimer cette administration') - cy.clickButton('Confirmer') + cy.clickButton('Supprimer') cy.wait('@deleteAdministration') diff --git a/frontend/cypress/e2e/back_office/administration_list/filters.spec.ts b/frontend/cypress/e2e/back_office/administration_table/filters.spec.ts similarity index 94% rename from frontend/cypress/e2e/back_office/administration_list/filters.spec.ts rename to frontend/cypress/e2e/back_office/administration_table/filters.spec.ts index cd0da42f6..eba48f244 100644 --- a/frontend/cypress/e2e/back_office/administration_list/filters.spec.ts +++ b/frontend/cypress/e2e/back_office/administration_table/filters.spec.ts @@ -1,4 +1,4 @@ -context('Back Office > Administration List > Filters', () => { +context('Back Office > Administration Table > Filters', () => { beforeEach(() => { cy.intercept('GET', `/api/v1/administrations`).as('getAdministrations') diff --git a/frontend/cypress/e2e/back_office/administration_list/row_actions.spec.ts b/frontend/cypress/e2e/back_office/administration_table/row_actions.spec.ts similarity index 80% rename from frontend/cypress/e2e/back_office/administration_list/row_actions.spec.ts rename to frontend/cypress/e2e/back_office/administration_table/row_actions.spec.ts index 91b5af156..480115e65 100644 --- a/frontend/cypress/e2e/back_office/administration_list/row_actions.spec.ts +++ b/frontend/cypress/e2e/back_office/administration_table/row_actions.spec.ts @@ -1,5 +1,5 @@ // Successful archiving and deleting use cases are tested in `administration_form.spec.ts` for Test Idempotency purpose -context('Back Office > Administration List > Row Actions', () => { +context('Back Office > Administration Table > Row Actions', () => { beforeEach(() => { cy.intercept('GET', `/api/v1/administrations`).as('getAdministrations') @@ -12,21 +12,23 @@ context('Back Office > Administration List > Row Actions', () => { cy.intercept('POST', `/api/v1/administrations/1005/archive`).as('archiveAdministration') cy.getTableRowById(1005).clickButton('Archiver cette administration') - cy.clickButton('Confirmer') + cy.clickButton('Archiver') cy.wait('@archiveAdministration') cy.get('.Component-Dialog').should('be.visible') + cy.contains('Archivage impossible').should('be.visible') }) it('Should show a dialog when trying to delete an administration linked to some control units', () => { cy.intercept('DELETE', `/api/v1/administrations/1005`).as('deleteAdministration') cy.getTableRowById(1005).clickButton('Supprimer cette administration') - cy.clickButton('Confirmer') + cy.clickButton('Supprimer') cy.wait('@deleteAdministration') cy.get('.Component-Dialog').should('be.visible') + cy.contains('Suppression impossible').should('be.visible') }) }) diff --git a/frontend/cypress/e2e/back_office/base_list/filters.spec.ts b/frontend/cypress/e2e/back_office/base_table/filters.spec.ts similarity index 92% rename from frontend/cypress/e2e/back_office/base_list/filters.spec.ts rename to frontend/cypress/e2e/back_office/base_table/filters.spec.ts index 5427e3e21..505cdf92a 100644 --- a/frontend/cypress/e2e/back_office/base_list/filters.spec.ts +++ b/frontend/cypress/e2e/back_office/base_table/filters.spec.ts @@ -1,4 +1,4 @@ -context('Back Office > Base List > Filters', () => { +context('Back Office > Base Table > Filters', () => { beforeEach(() => { cy.intercept('GET', `/api/v1/bases`).as('getBases') diff --git a/frontend/cypress/e2e/back_office/control_unit_form.spec.ts b/frontend/cypress/e2e/back_office/control_unit_form.spec.ts index 22648d3cd..ae06ede2e 100644 --- a/frontend/cypress/e2e/back_office/control_unit_form.spec.ts +++ b/frontend/cypress/e2e/back_office/control_unit_form.spec.ts @@ -77,7 +77,7 @@ context('Back Office > Control Unit Form', () => { cy.intercept('POST', `/api/v2/control_units/10033/archive`).as('archiveControlUnit') cy.getTableRowById(10033).clickButton('Archiver cette unité de contrôle') - cy.clickButton('Confirmer') + cy.clickButton('Archiver') cy.wait('@archiveControlUnit') @@ -91,7 +91,7 @@ context('Back Office > Control Unit Form', () => { cy.intercept('DELETE', `/api/v2/control_units/10033`).as('deleteControlUnit') cy.getTableRowById(10033).clickButton('Supprimer cette unité de contrôle') - cy.clickButton('Confirmer') + cy.clickButton('Supprimer') cy.wait('@deleteControlUnit') diff --git a/frontend/cypress/e2e/back_office/control_unit_list/filters.spec.ts b/frontend/cypress/e2e/back_office/control_unit_table/filters.spec.ts similarity index 96% rename from frontend/cypress/e2e/back_office/control_unit_list/filters.spec.ts rename to frontend/cypress/e2e/back_office/control_unit_table/filters.spec.ts index f3579f144..b0608f40c 100644 --- a/frontend/cypress/e2e/back_office/control_unit_list/filters.spec.ts +++ b/frontend/cypress/e2e/back_office/control_unit_table/filters.spec.ts @@ -1,4 +1,4 @@ -context('Back Office > Control Unit List > Filters', () => { +context('Back Office > Control Unit Table > Filters', () => { beforeEach(() => { cy.intercept('GET', `/api/v2/control_units`).as('getControlUnits') diff --git a/frontend/cypress/e2e/back_office/control_unit_list/row_actions.spec.ts b/frontend/cypress/e2e/back_office/control_unit_table/row_actions.spec.ts similarity index 80% rename from frontend/cypress/e2e/back_office/control_unit_list/row_actions.spec.ts rename to frontend/cypress/e2e/back_office/control_unit_table/row_actions.spec.ts index b34ee7c52..3c4890c31 100644 --- a/frontend/cypress/e2e/back_office/control_unit_list/row_actions.spec.ts +++ b/frontend/cypress/e2e/back_office/control_unit_table/row_actions.spec.ts @@ -1,5 +1,5 @@ // Successful archiving and deleting use cases are tested in `control_unit_form.spec.ts` for Test Idempotency purpose -context('Back Office > Control Unit List > Row Actions', () => { +context('Back Office > Control Unit Table > Row Actions', () => { beforeEach(() => { cy.intercept('GET', `/api/v2/control_units`).as('getControlUnits') @@ -12,10 +12,11 @@ context('Back Office > Control Unit List > Row Actions', () => { cy.intercept('DELETE', `/api/v2/control_units/10000`).as('deleteControlUnit') cy.getTableRowById(10000).clickButton('Supprimer cette unité de contrôle') - cy.clickButton('Confirmer') + cy.clickButton('Supprimer') cy.wait('@deleteControlUnit') cy.get('.Component-Dialog').should('be.visible') + cy.contains('Suppression impossible').should('be.visible') }) }) diff --git a/frontend/cypress/e2e/main_window/control_unit_dialog/contact_list.spec.ts b/frontend/cypress/e2e/main_window/control_unit_dialog/contact_list.spec.ts new file mode 100644 index 000000000..5495e9c86 --- /dev/null +++ b/frontend/cypress/e2e/main_window/control_unit_dialog/contact_list.spec.ts @@ -0,0 +1,98 @@ +import { gotToMainWindowAndOpenControlUnit } from './utils' + +context('Main Window > Control Unit Dialog > Contact List', () => { + beforeEach(() => { + gotToMainWindowAndOpenControlUnit(10000) + }) + + it('Should show all contacts by default', () => { + cy.contains('Contact 1').should('be.visible') + cy.contains('Contact 2').should('be.visible') + }) + + it('Should validate the form', () => { + cy.clickButton('Ajouter un contact') + + cy.clickButton('Ajouter') + + cy.contains('Veuillez choisir un nom.').should('be.visible') + cy.contains('Veuillez entrer un téléphone ou un email.').should('be.visible') + + cy.clickButton('Annuler') + + cy.get('p').contains('Ajouter un contact').should('not.exist') + }) + + it('Should add, edit and delete a contact', () => { + // ------------------------------------------------------------------------- + // Create + + cy.intercept('POST', `/api/v1/control_unit_contacts`).as('createControlUnitContact') + + cy.clickButton('Ajouter un contact') + + cy.fill('Nom du contact', 'Adjoint') + cy.fill('Numéro de téléphone', '0123456789') + cy.fill('Adresse mail', 'foo@example.org') + + cy.clickButton('Ajouter') + + cy.wait('@createControlUnitContact').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + email: 'foo@example.org', + name: 'ADJUNCT', + phone: '0123456789' + }) + }) + + cy.get('p').contains('Ajouter un contact').should('not.exist') + cy.contains('Adjoint').should('be.visible') + + // ------------------------------------------------------------------------- + // Edit + + cy.intercept('PUT', `/api/v1/control_unit_contacts/4`).as('updateControlUnitContact') + + cy.getDataCy('ControlUnitDialog-control-unit-contact').filter('[data-id="4"]').clickButton('Éditer ce contact') + + cy.fill('Nom du contact', 'Passerelle') + cy.fill('Numéro de téléphone', '9876543210') + cy.fill('Adresse mail', 'bar@example.org') + + cy.clickButton('Enregistrer les modifications') + + cy.wait('@updateControlUnitContact').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + email: 'bar@example.org', + id: 4, + name: 'BRIDGE', + phone: '9876543210' + }) + }) + + cy.get('p').contains('Éditer un contact').should('not.exist') + cy.contains('Enregistrer les modifications').should('not.exist') + cy.contains('Passerelle').should('be.visible') + + // ------------------------------------------------------------------------- + // Delete + + cy.intercept('DELETE', `/api/v1/control_unit_contacts/4`).as('deleteControlUnitContact') + + cy.getDataCy('ControlUnitDialog-control-unit-contact').filter('[data-id="4"]').clickButton('Éditer ce contact') + cy.clickButton('Supprimer ce contact') + cy.clickButton('Supprimer') + + cy.wait('@deleteControlUnitContact') + + cy.contains('Passerelle').should('not.exist') + }) +}) diff --git a/frontend/cypress/e2e/main_window/control_unit_dialog/form.spec.ts b/frontend/cypress/e2e/main_window/control_unit_dialog/form.spec.ts new file mode 100644 index 000000000..b49f650ce --- /dev/null +++ b/frontend/cypress/e2e/main_window/control_unit_dialog/form.spec.ts @@ -0,0 +1,57 @@ +import { gotToMainWindowAndOpenControlUnit } from './utils' + +context('Main Window > Control Unit Dialog > Resource List', () => { + beforeEach(() => { + gotToMainWindowAndOpenControlUnit(10000) + }) + + it('Should edit a control unit', () => { + cy.intercept('PUT', `/api/v2/control_units/10000`).as('updateControlUnit') + + // ------------------------------------------------------------------------- + // Terms note + + cy.getDataCy('ControlUnitDialog-termsNote').forceClick() + + cy.fill('Modalités de contact avec l’unité', 'Des modalités de contact avec l’unité.') + + cy.clickButton('Valider') + + cy.wait('@updateControlUnit').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + id: 10000, + termsNote: 'Des modalités de contact avec l’unité.' + }) + }) + + cy.contains('Des modalités de contact avec l’unité.').should('be.visible') + cy.get('.Element-Button').contains('Valider').should('not.exist') + + // ------------------------------------------------------------------------- + // Area note + + cy.getDataCy('ControlUnitDialog-areaNote').forceClick() + + cy.fill('Secteur d’intervention', 'Un secteur d’intervention.') + + cy.clickButton('Valider') + + cy.wait('@updateControlUnit').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + areaNote: 'Un secteur d’intervention.', + id: 10000 + }) + }) + + cy.contains('Un secteur d’intervention.').should('be.visible') + cy.get('.Element-Button').contains('Valider').should('not.exist') + }) +}) diff --git a/frontend/cypress/e2e/main_window/control_unit_dialog/resource_list.spec.ts b/frontend/cypress/e2e/main_window/control_unit_dialog/resource_list.spec.ts new file mode 100644 index 000000000..04f24edd9 --- /dev/null +++ b/frontend/cypress/e2e/main_window/control_unit_dialog/resource_list.spec.ts @@ -0,0 +1,103 @@ +import { gotToMainWindowAndOpenControlUnit } from './utils' + +context('Main Window > Control Unit Dialog > Resource List', () => { + beforeEach(() => { + gotToMainWindowAndOpenControlUnit(10000) + }) + + it('Should show all resources by default', () => { + cy.contains('Barge – Semi-rigide 1').should('be.visible') + cy.contains('Barge – Semi-rigide 2').should('be.visible') + }) + + it('Should validate the form', () => { + cy.clickButton('Ajouter un moyen') + + cy.clickButton('Ajouter') + + cy.contains('Veuillez choisir un type.').should('be.visible') + cy.contains('Veuillez choisir une base.').should('be.visible') + + cy.clickButton('Annuler') + + cy.get('p').contains('Ajouter un moyen').should('not.exist') + }) + + it('Should add, edit and delete a resource', () => { + // ------------------------------------------------------------------------- + // Create + + cy.intercept('POST', `/api/v1/control_unit_resources`).as('createControlUnitResource') + + cy.clickButton('Ajouter un moyen') + + cy.fill('Type de moyen', 'Avion') + // On ne met pas de nom de moyen ici + // pour tester que ce soit bien le type qui soit utilisé comme nom lorsque le nom est vide. + cy.fill('Base du moyen', 'Dunkerque') + cy.fill('Commentaire', 'Un commentaire sur le moyen.') + + cy.clickButton('Ajouter') + + cy.wait('@createControlUnitResource').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + baseId: 3, + name: 'Avion', + note: 'Un commentaire sur le moyen.', + type: 'AIRPLANE' + }) + }) + + cy.get('p').contains('Ajouter un moyen').should('not.exist') + cy.contains('Avion – Avion').should('be.visible') + + // ------------------------------------------------------------------------- + // Edit + + cy.intercept('PUT', `/api/v1/control_unit_resources/13`).as('updateControlUnitResource') + + cy.getDataCy('ControlUnitDialog-control-unit-resource').filter('[data-id="13"]').clickButton('Éditer ce moyen') + + cy.fill('Type de moyen', 'Bâtiment de soutien') + cy.fill('Nom du moyen', 'Super Moyen') + cy.fill('Base du moyen', 'Saint-Malo') + cy.fill('Commentaire', 'Un autre commentaire sur le moyen.') + + cy.clickButton('Enregistrer les modifications') + + cy.wait('@updateControlUnitResource').then(interception => { + if (!interception.response) { + assert.fail('`interception.response` is undefined.') + } + + assert.deepInclude(interception.request.body, { + baseId: 2, + id: 13, + name: 'Super Moyen', + note: 'Un autre commentaire sur le moyen.', + type: 'SUPPORT_SHIP' + }) + }) + + cy.get('p').contains('Éditer un moyen').should('not.exist') + cy.contains('Enregistrer les modifications').should('not.exist') + cy.contains('Bâtiment de soutien – Super Moyen').should('be.visible') + + // ------------------------------------------------------------------------- + // Delete + + cy.intercept('DELETE', `/api/v1/control_unit_resources/13`).as('deleteControlUnitResource') + + cy.getDataCy('ControlUnitDialog-control-unit-resource').filter('[data-id="13"]').clickButton('Éditer ce moyen') + cy.clickButton('Supprimer ce moyen') + cy.clickButton('Supprimer') + + cy.wait('@deleteControlUnitResource') + + cy.contains('Bâtiment de soutien – Super Moyen').should('not.exist') + }) +}) diff --git a/frontend/cypress/e2e/main_window/control_unit_dialog/utils.ts b/frontend/cypress/e2e/main_window/control_unit_dialog/utils.ts new file mode 100644 index 000000000..57ba47355 --- /dev/null +++ b/frontend/cypress/e2e/main_window/control_unit_dialog/utils.ts @@ -0,0 +1,8 @@ +import { goToMainWindow } from '../utils' + +export function gotToMainWindowAndOpenControlUnit(controlUnitId: number) { + goToMainWindow() + + cy.clickButton('Liste des unités de contrôle') + cy.getDataCy('ControlUnitListDialog-control-unit').filter(`[data-id="${controlUnitId}"]`).forceClick().wait(250) +} diff --git a/frontend/cypress/e2e/main_window/control_unit_list_dialog/filters.spec.ts b/frontend/cypress/e2e/main_window/control_unit_list_dialog/filters.spec.ts new file mode 100644 index 000000000..a7b8d2b99 --- /dev/null +++ b/frontend/cypress/e2e/main_window/control_unit_list_dialog/filters.spec.ts @@ -0,0 +1,53 @@ +import { FAKE_MAPBOX_RESPONSE } from '../../constants' + +context('Main Window > Control Unit List Dialog > Filters', () => { + beforeEach(() => { + cy.intercept('GET', 'https://api.mapbox.com/**', FAKE_MAPBOX_RESPONSE) + + cy.visit(`/`).wait(1000) + + cy.clickButton('Liste des unités de contrôle') + }) + + it('Should show all control units by default', () => { + cy.getDataCy('ControlUnitListDialog-control-unit').should('have.length', 33) + + cy.contains('A636 Maïto').should('exist') + cy.contains('SML 50').should('exist') + }) + + it('Should find control units matching the search query', () => { + cy.fill('Rechercher une unité', 'marine') + + cy.getDataCy('ControlUnitListDialog-control-unit').should('have.length', 4) + + cy.contains('Cultures marines – DDTM 30').should('exist') + cy.contains('A636 Maïto').should('exist') + }) + + it('Should find control units matching the selected administration', () => { + cy.fill('Administration', 'Douane') + + cy.getDataCy('ControlUnitListDialog-control-unit').should('have.length', 5) + + cy.contains('BGC Ajaccio').should('exist') + cy.contains('DF 61 Port-de-Bouc').should('exist') + }) + + it('Should find control units matching the selected resource type', () => { + cy.fill('Type de moyen', 'Barge') + + cy.getDataCy('ControlUnitListDialog-control-unit').should('have.length', 3) + + cy.contains('Cultures marines – DDTM 40').should('exist') + cy.contains('DPM – DDTM 14').should('exist') + }) + + it('Should find control units matching the selected base', () => { + cy.fill('Base du moyen', 'Marseille') + + cy.getDataCy('ControlUnitListDialog-control-unit').should('have.length', 1) + + cy.contains('Cultures marines – DDTM 40').should('exist') + }) +}) diff --git a/frontend/cypress/e2e/main_window/utils.ts b/frontend/cypress/e2e/main_window/utils.ts new file mode 100644 index 000000000..78e9173f2 --- /dev/null +++ b/frontend/cypress/e2e/main_window/utils.ts @@ -0,0 +1,7 @@ +import { FAKE_MAPBOX_RESPONSE } from '../constants' + +export function goToMainWindow() { + cy.intercept('GET', 'https://api.mapbox.com/**', FAKE_MAPBOX_RESPONSE) + + cy.visit(`/`).wait(1000) +} diff --git a/frontend/src/api/controlUnitContactsAPI.ts b/frontend/src/api/controlUnitContactsAPI.ts index 23517f66e..00b2888de 100644 --- a/frontend/src/api/controlUnitContactsAPI.ts +++ b/frontend/src/api/controlUnitContactsAPI.ts @@ -17,6 +17,14 @@ export const controlUnitContactsAPI = monitorenvPublicApi.injectEndpoints({ }) }), + deleteControlUnitContact: builder.mutation({ + invalidatesTags: () => [{ type: 'ControlUnits' }], + query: controlUnitContactId => ({ + method: 'DELETE', + url: `/v1/control_unit_contacts/${controlUnitContactId}` + }) + }), + getControlUnitContact: builder.query({ providesTags: () => [{ type: 'ControlUnits' }], query: controlUnitContactId => `/v1/control_unit_contacts/${controlUnitContactId}`, @@ -42,6 +50,7 @@ export const controlUnitContactsAPI = monitorenvPublicApi.injectEndpoints({ export const { useCreateControlUnitContactMutation, + useDeleteControlUnitContactMutation, useGetControlUnitContactQuery, useGetControlUnitContactsQuery, useUpdateControlUnitContactMutation diff --git a/frontend/src/api/controlUnitResourcesAPI.ts b/frontend/src/api/controlUnitResourcesAPI.ts index 1cf6bb76f..767531a42 100644 --- a/frontend/src/api/controlUnitResourcesAPI.ts +++ b/frontend/src/api/controlUnitResourcesAPI.ts @@ -1,8 +1,12 @@ import { monitorenvPublicApi } from './api' +import { ApiErrorCode } from './types' import { FrontendApiError } from '../libs/FrontendApiError' +import { newUserError } from '../libs/UserError' import type { ControlUnit } from '../domain/entities/controlUnit' +const DELETE_CONTROL_UNIT_RESOURCE_ERROR_MESSAGE = + "Ce moyen est rattaché à des missions. Veuillez l'en détacher avant de la supprimer." const GET_CONTROL_UNIT_RESOURCE_ERROR_MESSAGE = "Nous n'avons pas pu récupérer cette resource." const GET_CONTROL_UNIT_RESOURCES_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des resources." @@ -17,6 +21,21 @@ export const controlUnitResourcesAPI = monitorenvPublicApi.injectEndpoints({ }) }), + deleteControlUnitResource: builder.mutation({ + invalidatesTags: () => [{ type: 'Bases' }, { type: 'ControlUnits' }], + query: controlUnitResourceId => ({ + method: 'DELETE', + url: `/v1/control_unit_resources/${controlUnitResourceId}` + }), + transformErrorResponse: response => { + if (response.data.type === ApiErrorCode.FOREIGN_KEY_CONSTRAINT) { + return newUserError(DELETE_CONTROL_UNIT_RESOURCE_ERROR_MESSAGE) + } + + return new FrontendApiError(DELETE_CONTROL_UNIT_RESOURCE_ERROR_MESSAGE, response) + } + }), + getControlUnitResource: builder.query({ providesTags: () => [{ type: 'ControlUnits' }], query: controlUnitResourceId => `/v1/control_unit_resources/${controlUnitResourceId}`, @@ -42,6 +61,7 @@ export const controlUnitResourcesAPI = monitorenvPublicApi.injectEndpoints({ export const { useCreateControlUnitResourceMutation, + useDeleteControlUnitResourceMutation, useGetControlUnitResourceQuery, useGetControlUnitResourcesQuery, useUpdateControlUnitResourceMutation diff --git a/frontend/src/components/ConfirmationModal.tsx b/frontend/src/components/ConfirmationModal.tsx new file mode 100644 index 000000000..d2b89aa3f --- /dev/null +++ b/frontend/src/components/ConfirmationModal.tsx @@ -0,0 +1,87 @@ +import { Accent, Button, Dialog, Icon } from '@mtes-mct/monitor-ui' +import styled from 'styled-components' + +import type { Promisable } from 'type-fest' + +export type ConfirmationModalProps = { + color?: string + confirmationButtonLabel: string + iconName?: keyof typeof Icon + message: string + onCancel: () => Promisable + onConfirm: () => Promisable + title: string +} +export function ConfirmationModal({ + color, + confirmationButtonLabel, + iconName, + message, + onCancel, + onConfirm, + title +}: ConfirmationModalProps) { + const SelectedIcon = iconName ? Icon[iconName] : null + + return ( + + {title} + + {SelectedIcon && ( + + + + )} + {message} + + + {confirmationButtonLabel} + + Annuler + + + + ) +} + +// TODO Allow direct `width` prop control in MUI. +// This is a mess. I wonder if we should add inner classes in MUI. +const StyledDialog = styled(Dialog)` + > div:last-child { + max-width: 440px; + min-width: 440px; + + /* Dialog.Body */ + > div:nth-child(2) { + padding-top: 24px; + } + + /* Dialog.Action */ + > div:last-child { + padding: 24px 0 32px; + + > .Element-Button { + width: 136px; + + :not(:first-child) { + margin-left: 8px; + } + } + } + } +` + +const Picto = styled.div` + display: flex; + justify-content: center; + margin-bottom: 8px; +` + +/* TODO Replace the `> p` forcing the `!important`. */ +const Message = styled.p<{ + $color?: string +}>` + ${p => p.$color && `color: ${p.$color} !important;`} + font-size: 16px; + font-weight: bold; +` diff --git a/frontend/src/components/Dialog.tsx b/frontend/src/components/Dialog.tsx new file mode 100644 index 000000000..c7188e5e1 --- /dev/null +++ b/frontend/src/components/Dialog.tsx @@ -0,0 +1,82 @@ +import { Button, Dialog as MuiDialog, Icon } from '@mtes-mct/monitor-ui' +import styled from 'styled-components' + +import type { Promisable } from 'type-fest' + +export type DialogProps = { + color?: string + iconName?: keyof typeof Icon + message: string + onClose: () => Promisable + title: string + titleBackgroundColor?: string +} +export function Dialog({ color, iconName, message, onClose, title, titleBackgroundColor }: DialogProps) { + const SelectedIcon = iconName ? Icon[iconName] : null + + return ( + + {title} + + {SelectedIcon && ( + + + + )} + {message} + + + Fermer + + + ) +} + +// TODO Allow direct `width` prop control in MUI. +// This is a mess. I wonder if we should add inner classes in MUI. +const StyledDialog = styled(MuiDialog)<{ + $titleBackgroundColor: string | undefined +}>` + > div:last-child { + max-width: 440px; + min-width: 440px; + + /* Dialog.Title */ + > h4 { + ${p => p.$titleBackgroundColor && `background-color: ${p.$titleBackgroundColor};`} + } + + /* Dialog.Body */ + > div:nth-child(2) { + padding-top: 24px; + } + + /* Dialog.Action */ + > div:last-child { + padding: 24px 0 32px; + + > .Element-Button { + width: 136px; + + :not(:first-child) { + margin-left: 8px; + } + } + } + } +` + +const Picto = styled.div` + display: flex; + justify-content: center; + margin-bottom: 8px; +` + +/* TODO Replace the `> p` forcing the `!important`. */ +const Message = styled.p<{ + $color?: string +}>` + ${p => p.$color && `color: ${p.$color} !important;`} + font-size: 16px; + font-weight: bold; +` diff --git a/frontend/src/domain/shared_slices/BackOffice.ts b/frontend/src/domain/shared_slices/BackOffice.ts deleted file mode 100644 index 969c82775..000000000 --- a/frontend/src/domain/shared_slices/BackOffice.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { createSlice, type PayloadAction } from '@reduxjs/toolkit' - -import type { BackOfficeConfirmationModalActionType } from '../use_cases/backOffice/types' - -type BackOfficeState = { - confirmationModalActionId: number - confirmationModalActionType: BackOfficeConfirmationModalActionType | undefined - confirmationModalMessage: string | undefined - - dialogMessage: string | undefined - - isConfirmationModalOpen: boolean - isDialogOpen: boolean -} -const INITIAL_STATE: BackOfficeState = { - confirmationModalActionId: -1, - confirmationModalActionType: undefined, - confirmationModalMessage: undefined, - - dialogMessage: undefined, - - isConfirmationModalOpen: false, - isDialogOpen: false -} - -const backOfficeSlice = createSlice({ - initialState: INITIAL_STATE, - name: 'backOffice', - reducers: { - closeConfirmationModal(state) { - state.confirmationModalActionId = -1 - state.confirmationModalActionType = undefined - state.confirmationModalMessage = undefined - state.isConfirmationModalOpen = false - }, - - closeDialog(state) { - state.dialogMessage = undefined - state.isDialogOpen = false - }, - - openConfirmationModal( - state, - action: PayloadAction<{ - actionId: number - actionType: BackOfficeConfirmationModalActionType - message: string - }> - ) { - state.confirmationModalActionId = action.payload.actionId - state.confirmationModalActionType = action.payload.actionType - state.confirmationModalMessage = action.payload.message - state.isConfirmationModalOpen = true - }, - - openDialog( - state, - action: PayloadAction<{ - message: string - }> - ) { - state.dialogMessage = action.payload.message - state.isDialogOpen = true - } - } -}) - -export const { closeConfirmationModal, openConfirmationModal } = backOfficeSlice.actions - -export const backOfficeActions = backOfficeSlice.actions -export const backOfficeReducer = backOfficeSlice.reducer diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/domain/shared_slices/Global.ts index 3ffc8d31d..ddab01d44 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/domain/shared_slices/Global.ts @@ -1,4 +1,4 @@ -// TODO It may be a good thing to either call this slice 'mainWindowSlice' (or something with "map"?) since it targets the main window. +// TODO This slice should disappear in favor of `features/MainWindow/slice.ts`. import { createSlice, type PayloadAction } from '@reduxjs/toolkit' @@ -95,6 +95,7 @@ const initialState: GlobalStateType = { // state entry for other children components whom visibility is already handled by parent components isLayersSidebarVisible: false, + // TODO Use `MainWindowDialog` or `MainWindowConfirmationModal`. isControlUnitDialogVisible: false, isControlUnitListDialogVisible: false, diff --git a/frontend/src/domain/shared_slices/index.js b/frontend/src/domain/shared_slices/index.ts similarity index 78% rename from frontend/src/domain/shared_slices/index.js rename to frontend/src/domain/shared_slices/index.ts index 5bc4c4b83..d3d960fb1 100644 --- a/frontend/src/domain/shared_slices/index.js +++ b/frontend/src/domain/shared_slices/index.ts @@ -3,7 +3,6 @@ import { combineReducers } from '@reduxjs/toolkit' import { administrativeSlicePersistedReducer } from './Administrative' -import { backOfficeReducer } from './BackOffice' import { drawReducer } from './Draw' import { globalReducer } from './Global' import { interestPointSlicePersistedReducer } from './InterestPoint' @@ -28,12 +27,14 @@ import { missionsAPI } from '../../api/missionsAPI' import { regulatoryLayersAPI } from '../../api/regulatoryLayersAPI' import { reportingsAPI } from '../../api/reportingsAPI' import { semaphoresAPI } from '../../api/semaphoresAPI' -import { backOfficeAdministrationListPersistedReducer } from '../../features/Administrations/BackOfficeAdministrationList/slice' -import { backOfficeBaseListPersistedReducer } from '../../features/Bases/BackOfficeBaseList/slice' -import { backOfficeControlUnitListPersistedReducer } from '../../features/ControlUnits/BackOfficeControlUnitList/slice' -import { mapControlUnitDialogReducer } from '../../features/ControlUnits/MapControlUnitDialog/slice' -import { mapControlUnitListDialogPersistedReducer } from '../../features/ControlUnits/MapControlUnitListDialog/slice' +import { administrationTablePersistedReducer } from '../../features/Administration/components/AdministrationTable/slice' +import { backOfficeReducer } from '../../features/BackOffice/slice' +import { baseTablePersistedReducer } from '../../features/Base/components/BaseTable/slice' +import { controlUnitDialogReducer } from '../../features/ControlUnit/components/ControlUnitDialog/slice' +import { controlUnitListDialogPersistedReducer } from '../../features/ControlUnit/components/ControlUnitListDialog/slice' +import { controlUnitTablePersistedReducer } from '../../features/ControlUnit/components/ControlUnitTable/slice' import { layerSearchSliceReducer } from '../../features/layersSelector/search/LayerSearch.slice' +import { mainWindowReducer } from '../../features/MainWindow/slice' import { sideWindowReducer } from '../../features/SideWindow/slice' // TODO Maybe add a specifc store for the backoffice? @@ -44,16 +45,17 @@ export const homeReducers = combineReducers({ administrative: administrativeSlicePersistedReducer, backOffice: backOfficeReducer, - backOfficeAdministrationList: backOfficeAdministrationListPersistedReducer, - backOfficeBaseList: backOfficeBaseListPersistedReducer, - backOfficeControlUnitList: backOfficeControlUnitListPersistedReducer, + backOfficeAdministrationList: administrationTablePersistedReducer, + backOfficeBaseList: baseTablePersistedReducer, + backOfficeControlUnitList: controlUnitTablePersistedReducer, draw: drawReducer, global: globalReducer, interestPoint: interestPointSlicePersistedReducer, layerSearch: layerSearchSliceReducer, + mainWindow: mainWindowReducer, map: mapSliceReducer, - mapControlUnitDialog: mapControlUnitDialogReducer, - mapControlUnitListDialog: mapControlUnitListDialogPersistedReducer, + mapControlUnitDialog: controlUnitDialogReducer, + mapControlUnitListDialog: controlUnitListDialogPersistedReducer, measurement: measurementSlicePersistedReducer, missionFilters: missionFiltersPersistedReducer, missionState: missionStateSliceReducer, diff --git a/frontend/src/domain/use_cases/backOffice/handleModalConfirmation.ts b/frontend/src/domain/use_cases/backOffice/handleModalConfirmation.ts deleted file mode 100644 index 7736badf6..000000000 --- a/frontend/src/domain/use_cases/backOffice/handleModalConfirmation.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BackOfficeConfirmationModalActionType } from './types' -import { closeConfirmationModal } from '../../shared_slices/BackOffice' -import { archiveAdministration } from '../administration/archiveAdministration' -import { deleteAdministration } from '../administration/deleteAdministration' -import { archiveControlUnit } from '../controlUnit/archiveControlUnit' -import { deleteControlUnit } from '../controlUnit/deleteControlUnit' - -import type { AppThunk } from '../../../store' - -export const handleModalConfirmation = (): AppThunk => async (dispatch, getState) => { - const { confirmationModalActionType } = getState().backOffice - - switch (confirmationModalActionType) { - case BackOfficeConfirmationModalActionType.ARCHIVE_ADMINISTRATION: - await dispatch(archiveAdministration()) - break - - case BackOfficeConfirmationModalActionType.ARCHIVE_CONTROL_UNIT: - await dispatch(archiveControlUnit()) - break - - case BackOfficeConfirmationModalActionType.DELETE_ADMINISTRATION: - await dispatch(deleteAdministration()) - break - - case BackOfficeConfirmationModalActionType.DELETE_CONTROL_UNIT: - await dispatch(deleteControlUnit()) - break - - default: - break - } - - dispatch(closeConfirmationModal()) -} diff --git a/frontend/src/domain/use_cases/backOffice/types.ts b/frontend/src/domain/use_cases/backOffice/types.ts deleted file mode 100644 index 13a2e6eed..000000000 --- a/frontend/src/domain/use_cases/backOffice/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum BackOfficeConfirmationModalActionType { - 'ARCHIVE_ADMINISTRATION' = 'ARCHIVE_ADMINISTRATION', - 'ARCHIVE_CONTROL_UNIT' = 'ARCHIVE_CONTROL_UNIT', - 'DELETE_ADMINISTRATION' = 'DELETE_ADMINISTRATION', - 'DELETE_CONTROL_UNIT' = 'DELETE_CONTROL_UNIT' -} diff --git a/frontend/src/domain/use_cases/controlUnit/deleteControlUnit.ts b/frontend/src/domain/use_cases/controlUnit/deleteControlUnit.ts deleted file mode 100644 index 2e75bad86..000000000 --- a/frontend/src/domain/use_cases/controlUnit/deleteControlUnit.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { logSoftError } from '@mtes-mct/monitor-ui' - -import { controlUnitsAPI } from '../../../api/controlUnitsAPI' -import { isUserError } from '../../../libs/UserError' -import { backOfficeActions } from '../../shared_slices/BackOffice' - -import type { AppThunk } from '../../../store' - -export const deleteControlUnit = (): AppThunk> => async (dispatch, getState) => { - const controlUnitId = getState().backOffice.confirmationModalActionId - - try { - const { error } = await dispatch(controlUnitsAPI.endpoints.deleteControlUnit.initiate(controlUnitId) as any) - if (error) { - throw error - } - } catch (err) { - if (isUserError(err)) { - dispatch(backOfficeActions.openDialog({ message: err.userMessage })) - - return - } - - logSoftError({ - message: `An error happened while deleting a control unit (ID=${controlUnitId}").`, - originalError: err, - userMessage: "Une erreur est survenue pendant la suppression de l'unité de contrôle." - }) - } -} diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationForm/constants.tsx b/frontend/src/features/Administration/components/AdministrationForm/constants.tsx similarity index 87% rename from frontend/src/features/Administrations/BackOfficeAdministrationForm/constants.tsx rename to frontend/src/features/Administration/components/AdministrationForm/constants.tsx index 19c846da0..76100c36d 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationForm/constants.tsx +++ b/frontend/src/features/Administration/components/AdministrationForm/constants.tsx @@ -1,11 +1,11 @@ import { Icon, Size } from '@mtes-mct/monitor-ui' import { object, string } from 'yup' -import { NavIconButton } from '../../../ui/NavIconButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { NavIconButton } from '../../../../ui/NavIconButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' import type { AdministrationFormValues } from './types' -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' import type { ColumnDef } from '@tanstack/react-table' export const ADMINISTRATION_FORM_SCHEMA = object({ diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationForm/index.tsx b/frontend/src/features/Administration/components/AdministrationForm/index.tsx similarity index 92% rename from frontend/src/features/Administrations/BackOfficeAdministrationForm/index.tsx rename to frontend/src/features/Administration/components/AdministrationForm/index.tsx index efc5f4014..32ede500d 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationForm/index.tsx +++ b/frontend/src/features/Administration/components/AdministrationForm/index.tsx @@ -6,15 +6,15 @@ import { useNavigate, useParams } from 'react-router' import styled from 'styled-components' import { ADMINISTRATION_FORM_SCHEMA, CONTROL_UNIT_TABLE_COLUMNS, INITIAL_ADMINISTRATION_FORM_VALUES } from './constants' -import { administrationsAPI, useGetAdministrationQuery } from '../../../api/administrationsAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { FrontendError } from '../../../libs/FrontendError' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { administrationsAPI, useGetAdministrationQuery } from '../../../../api/administrationsAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { FrontendError } from '../../../../libs/FrontendError' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' import type { AdministrationFormValues } from './types' -import type { Administration } from '../../../domain/entities/administration' +import type { Administration } from '../../../../domain/entities/administration' -export function BackOfficeAdministrationForm() { +export function AdministrationForm() { const { administrationId } = useParams() if (!administrationId) { throw new FrontendError('`administrationId` is undefined.') diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationForm/types.ts b/frontend/src/features/Administration/components/AdministrationForm/types.ts similarity index 66% rename from frontend/src/features/Administrations/BackOfficeAdministrationForm/types.ts rename to frontend/src/features/Administration/components/AdministrationForm/types.ts index 82195d762..9c30d25a1 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationForm/types.ts +++ b/frontend/src/features/Administration/components/AdministrationForm/types.ts @@ -1,4 +1,4 @@ -import type { Administration } from '../../../domain/entities/administration' +import type { Administration } from '../../../../domain/entities/administration' import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' export type AdministrationFormValues = UndefineExceptArrays diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/FilterBar.tsx b/frontend/src/features/Administration/components/AdministrationTable/FilterBar.tsx similarity index 72% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/FilterBar.tsx rename to frontend/src/features/Administration/components/AdministrationTable/FilterBar.tsx index 79c2ac714..2fe6e05cd 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/FilterBar.tsx +++ b/frontend/src/features/Administration/components/AdministrationTable/FilterBar.tsx @@ -2,9 +2,9 @@ import { Icon, TextInput } from '@mtes-mct/monitor-ui' import { useCallback } from 'react' import styled from 'styled-components' -import { backOfficeAdministrationListActions } from './slice' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' +import { administrationTableActions } from './slice' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' export function FilterBar() { const dispatch = useAppDispatch() @@ -12,7 +12,7 @@ export function FilterBar() { const updateQuery = useCallback( (nextValue: string | undefined) => { - dispatch(backOfficeAdministrationListActions.setFilter({ key: 'query', value: nextValue })) + dispatch(administrationTableActions.setFilter({ key: 'query', value: nextValue })) }, [dispatch] ) diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/TabMenu.tsx b/frontend/src/features/Administration/components/AdministrationTable/TabMenu.tsx similarity index 53% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/TabMenu.tsx rename to frontend/src/features/Administration/components/AdministrationTable/TabMenu.tsx index 9a8d146e9..2c77a406d 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/TabMenu.tsx +++ b/frontend/src/features/Administration/components/AdministrationTable/TabMenu.tsx @@ -1,30 +1,30 @@ -import { backOfficeAdministrationListActions } from './slice' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { TabBar } from '../../BackOffice/components/TabBar' +import { administrationTableActions } from './slice' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { BackOfficeTabBar } from '../../../BackOffice/components/BackOfficeTabBar' export function TabMenu() { const dispatch = useAppDispatch() const backOfficeAdministrationList = useAppSelector(store => store.backOfficeAdministrationList) const filterArchivedAdministrations = (isArchived: boolean) => { - dispatch(backOfficeAdministrationListActions.setFilter({ key: 'isArchived', value: isArchived })) + dispatch(administrationTableActions.setFilter({ key: 'isArchived', value: isArchived })) } return ( - - + filterArchivedAdministrations(false)} > Administrations actives - - + filterArchivedAdministrations(true)} > Administrations archivées - - + + ) } diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/constants.tsx b/frontend/src/features/Administration/components/AdministrationTable/constants.tsx similarity index 85% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/constants.tsx rename to frontend/src/features/Administration/components/AdministrationTable/constants.tsx index 7dfbd9cd6..53f73520c 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/constants.tsx +++ b/frontend/src/features/Administration/components/AdministrationTable/constants.tsx @@ -1,9 +1,9 @@ import { Icon, Size } from '@mtes-mct/monitor-ui' -import { NavIconButton } from '../../../ui/NavIconButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { NavIconButton } from '../../../../ui/NavIconButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -import type { Administration } from '../../../domain/entities/administration' +import type { Administration } from '../../../../domain/entities/administration' import type { ColumnDef } from '@tanstack/react-table' export const ADMINISTRATION_TABLE_COLUMNS: Array> = [ diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/index.tsx b/frontend/src/features/Administration/components/AdministrationTable/index.tsx similarity index 84% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/index.tsx rename to frontend/src/features/Administration/components/AdministrationTable/index.tsx index 11dd14741..58b951e48 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/index.tsx +++ b/frontend/src/features/Administration/components/AdministrationTable/index.tsx @@ -5,13 +5,13 @@ import styled from 'styled-components' import { FilterBar } from './FilterBar' import { TabMenu } from './TabMenu' import { getAdministrationTableColumns, getFilters } from './utils' -import { useGetAdministrationsQuery } from '../../../api/administrationsAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { NavButton } from '../../../ui/NavButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { useGetAdministrationsQuery } from '../../../../api/administrationsAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { NavButton } from '../../../../ui/NavButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -export function BackOfficeAdministrationList() { +export function AdministrationTable() { const backOfficeAdministrationList = useAppSelector(store => store.backOfficeAdministrationList) const dispatch = useAppDispatch() const { data: administrations } = useGetAdministrationsQuery() diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/slice.ts b/frontend/src/features/Administration/components/AdministrationTable/slice.ts similarity index 58% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/slice.ts rename to frontend/src/features/Administration/components/AdministrationTable/slice.ts index 734045163..532027f9c 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/slice.ts +++ b/frontend/src/features/Administration/components/AdministrationTable/slice.ts @@ -5,11 +5,11 @@ import storage from 'redux-persist/lib/storage' import type { FiltersState } from './types' -export type BackOfficeAdministrationListState = { +interface AdministrationTableState { filtersState: FiltersState } -const INITIAL_STATE: BackOfficeAdministrationListState = { +const INITIAL_STATE: AdministrationTableState = { filtersState: { isArchived: false, query: undefined @@ -17,13 +17,13 @@ const INITIAL_STATE: BackOfficeAdministrationListState = { } const persistConfig = { - key: 'backOfficeAdministrationList', + key: 'administrationList', storage } -const backOfficeAdministrationList = createSlice({ +const AdministrationTableSlice = createSlice({ initialState: INITIAL_STATE, - name: 'backOfficeAdministrationList', + name: 'administrationList', reducers: { setFilter( state, @@ -37,9 +37,6 @@ const backOfficeAdministrationList = createSlice({ } }) -export const backOfficeAdministrationListActions = backOfficeAdministrationList.actions +export const administrationTableActions = AdministrationTableSlice.actions -export const backOfficeAdministrationListPersistedReducer = persistReducer( - persistConfig, - backOfficeAdministrationList.reducer -) +export const administrationTablePersistedReducer = persistReducer(persistConfig, AdministrationTableSlice.reducer) diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/types.ts b/frontend/src/features/Administration/components/AdministrationTable/types.ts similarity index 100% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/types.ts rename to frontend/src/features/Administration/components/AdministrationTable/types.ts diff --git a/frontend/src/features/Administrations/BackOfficeAdministrationList/utils.tsx b/frontend/src/features/Administration/components/AdministrationTable/utils.tsx similarity index 75% rename from frontend/src/features/Administrations/BackOfficeAdministrationList/utils.tsx rename to frontend/src/features/Administration/components/AdministrationTable/utils.tsx index fac67768e..09c1b43d6 100644 --- a/frontend/src/features/Administrations/BackOfficeAdministrationList/utils.tsx +++ b/frontend/src/features/Administration/components/AdministrationTable/utils.tsx @@ -1,22 +1,29 @@ import { CustomSearch, IconButton, type Filter, Icon, Size } from '@mtes-mct/monitor-ui' import { ADMINISTRATION_TABLE_COLUMNS } from './constants' -import { openConfirmationModal } from '../../../domain/shared_slices/BackOffice' -import { BackOfficeConfirmationModalActionType } from '../../../domain/use_cases/backOffice/types' +import { backOfficeActions } from '../../../BackOffice/slice' +import { BackOfficeConfirmationModalActionType } from '../../../BackOffice/types' import type { FiltersState } from './types' -import type { Administration } from '../../../domain/entities/administration' -import type { AppDispatch } from '../../../store' +import type { Administration } from '../../../../domain/entities/administration' +import type { AppDispatch } from '../../../../store' import type { CellContext, ColumnDef } from '@tanstack/react-table' function archiveAdministration(info: CellContext, dispatch: AppDispatch) { const administration = info.getValue() dispatch( - openConfirmationModal({ - actionId: administration.id, + backOfficeActions.openConfirmationModal({ actionType: BackOfficeConfirmationModalActionType.ARCHIVE_ADMINISTRATION, - message: `Confirmez-vous l'archivage de l'administration "${administration.name}" ? Elle n'apparaîtra plus dans MonitorEnv, elle ne sera plus utilisée que pour les statistiques.` + entityId: administration.id, + modalProps: { + confirmationButtonLabel: 'Archiver', + message: [ + `Êtes-vous sûr de vouloir archiver l'administration "${administration.name}" ?`, + `Elle n'apparaîtra plus dans MonitorEnv, elle ne sera plus utilisée que pour les statistiques.` + ].join(' '), + title: `Archivage de l'administration` + } }) ) } @@ -25,10 +32,14 @@ function deleteAdministration(info: CellContext() dispatch( - openConfirmationModal({ - actionId: administration.id, + backOfficeActions.openConfirmationModal({ actionType: BackOfficeConfirmationModalActionType.DELETE_ADMINISTRATION, - message: `Confirmez-vous la suppression de l'administration "${administration.name}" ?` + entityId: administration.id, + modalProps: { + confirmationButtonLabel: 'Supprimer', + message: `Êtes-vous sûr de vouloir supprimer l'administration "${administration.name}" ?`, + title: `Suppression de l'administration` + } }) ) } diff --git a/frontend/src/domain/use_cases/administration/archiveAdministration.ts b/frontend/src/features/Administration/useCases/archiveAdministration.ts similarity index 50% rename from frontend/src/domain/use_cases/administration/archiveAdministration.ts rename to frontend/src/features/Administration/useCases/archiveAdministration.ts index 4ad571a0f..f5ca63047 100644 --- a/frontend/src/domain/use_cases/administration/archiveAdministration.ts +++ b/frontend/src/features/Administration/useCases/archiveAdministration.ts @@ -1,30 +1,43 @@ -import { logSoftError } from '@mtes-mct/monitor-ui' +import { THEME, logSoftError } from '@mtes-mct/monitor-ui' import { administrationsAPI } from '../../../api/administrationsAPI' +import { FrontendError } from '../../../libs/FrontendError' import { isUserError } from '../../../libs/UserError' -import { backOfficeActions } from '../../shared_slices/BackOffice' +import { backOfficeActions } from '../../BackOffice/slice' import type { AppThunk } from '../../../store' export const archiveAdministration = (): AppThunk> => async (dispatch, getState) => { - const administrationId = getState().backOffice.confirmationModalActionId + const { confirmationModal } = getState().backOffice + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } try { const { error } = await dispatch( - administrationsAPI.endpoints.archiveAdministration.initiate(administrationId) as any + administrationsAPI.endpoints.archiveAdministration.initiate(confirmationModal.entityId) as any ) if (error) { throw error } } catch (err) { if (isUserError(err)) { - dispatch(backOfficeActions.openDialog({ message: err.userMessage })) + dispatch( + backOfficeActions.openDialog({ + dialogProps: { + color: THEME.color.maximumRed, + message: err.userMessage, + title: `Archivage impossible`, + titleBackgroundColor: THEME.color.maximumRed + } + }) + ) return } logSoftError({ - message: `An error happened while archiving an administration (ID=${administrationId}").`, + message: `An error happened while archiving an administration (ID=${confirmationModal.entityId}").`, originalError: err, userMessage: "Une erreur est survenue pendant l'archivage de l'administration." }) diff --git a/frontend/src/domain/use_cases/administration/deleteAdministration.ts b/frontend/src/features/Administration/useCases/deleteAdministration.ts similarity index 50% rename from frontend/src/domain/use_cases/administration/deleteAdministration.ts rename to frontend/src/features/Administration/useCases/deleteAdministration.ts index 9198c015b..701f2846c 100644 --- a/frontend/src/domain/use_cases/administration/deleteAdministration.ts +++ b/frontend/src/features/Administration/useCases/deleteAdministration.ts @@ -1,30 +1,43 @@ -import { logSoftError } from '@mtes-mct/monitor-ui' +import { THEME, logSoftError } from '@mtes-mct/monitor-ui' import { administrationsAPI } from '../../../api/administrationsAPI' +import { FrontendError } from '../../../libs/FrontendError' import { isUserError } from '../../../libs/UserError' -import { backOfficeActions } from '../../shared_slices/BackOffice' +import { backOfficeActions } from '../../BackOffice/slice' import type { AppThunk } from '../../../store' export const deleteAdministration = (): AppThunk> => async (dispatch, getState) => { - const administrationId = getState().backOffice.confirmationModalActionId + const { confirmationModal } = getState().backOffice + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } try { const { error } = await dispatch( - administrationsAPI.endpoints.deleteAdministration.initiate(administrationId) as any + administrationsAPI.endpoints.deleteAdministration.initiate(confirmationModal.entityId) as any ) if (error) { throw error } } catch (err) { if (isUserError(err)) { - dispatch(backOfficeActions.openDialog({ message: err.userMessage })) + dispatch( + backOfficeActions.openDialog({ + dialogProps: { + color: THEME.color.maximumRed, + message: err.userMessage, + title: `Suppression impossible`, + titleBackgroundColor: THEME.color.maximumRed + } + }) + ) return } logSoftError({ - message: `An error happened while deleting an administration (ID=${administrationId}").`, + message: `An error happened while deleting an administration (ID=${confirmationModal.entityId}").`, originalError: err, userMessage: "Une erreur est survenue pendant la suppression de l'administration." }) diff --git a/frontend/src/features/BackOffice/components/BackOfficeConfirmationModal.tsx b/frontend/src/features/BackOffice/components/BackOfficeConfirmationModal.tsx new file mode 100644 index 000000000..c96365363 --- /dev/null +++ b/frontend/src/features/BackOffice/components/BackOfficeConfirmationModal.tsx @@ -0,0 +1,27 @@ +import { useCallback } from 'react' + +import { ConfirmationModal } from '../../../components/ConfirmationModal' +import { useAppDispatch } from '../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../hooks/useAppSelector' +import { FrontendError } from '../../../libs/FrontendError' +import { backOfficeActions } from '../slice' +import { handleModalConfirmation } from '../useCases/handleModalConfirmation' + +export function BackOfficeConfirmationModal() { + const dispatch = useAppDispatch() + const { confirmationModal } = useAppSelector(store => store.backOffice) + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + const close = useCallback(() => { + dispatch(backOfficeActions.closeConfirmationModal()) + }, [dispatch]) + + const confirm = useCallback(() => { + dispatch(handleModalConfirmation()) + }, [dispatch]) + + // eslint-disable-next-line react/jsx-props-no-spreading + return +} diff --git a/frontend/src/features/BackOffice/components/BackOfficeDialog.tsx b/frontend/src/features/BackOffice/components/BackOfficeDialog.tsx new file mode 100644 index 000000000..851f03fb0 --- /dev/null +++ b/frontend/src/features/BackOffice/components/BackOfficeDialog.tsx @@ -0,0 +1,22 @@ +import { useCallback } from 'react' + +import { Dialog } from '../../../components/Dialog' +import { useAppDispatch } from '../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../hooks/useAppSelector' +import { FrontendError } from '../../../libs/FrontendError' +import { backOfficeActions } from '../slice' + +export function BackOfficeDialog() { + const dispatch = useAppDispatch() + const { dialog } = useAppSelector(store => store.backOffice) + if (!dialog) { + throw new FrontendError('`dialog` is undefined.') + } + + const close = useCallback(() => { + dispatch(backOfficeActions.closeDialog()) + }, [dispatch]) + + // eslint-disable-next-line react/jsx-props-no-spreading + return +} diff --git a/frontend/src/features/BackOffice/components/TabBar/Tab.tsx b/frontend/src/features/BackOffice/components/BackOfficeTabBar/Tab.tsx similarity index 100% rename from frontend/src/features/BackOffice/components/TabBar/Tab.tsx rename to frontend/src/features/BackOffice/components/BackOfficeTabBar/Tab.tsx diff --git a/frontend/src/features/BackOffice/components/TabBar/index.tsx b/frontend/src/features/BackOffice/components/BackOfficeTabBar/index.tsx similarity index 59% rename from frontend/src/features/BackOffice/components/TabBar/index.tsx rename to frontend/src/features/BackOffice/components/BackOfficeTabBar/index.tsx index 5acbbf14e..0661cc81f 100644 --- a/frontend/src/features/BackOffice/components/TabBar/index.tsx +++ b/frontend/src/features/BackOffice/components/BackOfficeTabBar/index.tsx @@ -2,7 +2,7 @@ import styled from 'styled-components' import { Tab } from './Tab' -const BareTabBar = styled.div` +const BareBackOfficeTabBar = styled.div` display: flex; width: 100%; @@ -11,6 +11,6 @@ const BareTabBar = styled.div` } ` -export const TabBar = Object.assign(BareTabBar, { +export const BackOfficeTabBar = Object.assign(BareBackOfficeTabBar, { Tab }) diff --git a/frontend/src/features/BackOffice/components/ConfirmationModal.tsx b/frontend/src/features/BackOffice/components/ConfirmationModal.tsx deleted file mode 100644 index 6d501def7..000000000 --- a/frontend/src/features/BackOffice/components/ConfirmationModal.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Accent, Button, Dialog } from '@mtes-mct/monitor-ui' -import { useCallback } from 'react' - -import { backOfficeActions } from '../../../domain/shared_slices/BackOffice' -import { handleModalConfirmation } from '../../../domain/use_cases/backOffice/handleModalConfirmation' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' - -export function ConfirmationModal() { - const dispatch = useAppDispatch() - const backOffice = useAppSelector(store => store.backOffice) - - const cancel = useCallback(() => { - dispatch(backOfficeActions.closeConfirmationModal()) - }, [dispatch]) - - const confirm = useCallback(() => { - dispatch(handleModalConfirmation()) - }, [dispatch]) - - return ( - - - {backOffice.confirmationModalMessage} - - - - Annuler - - - Confirmer - - - - ) -} diff --git a/frontend/src/features/BackOffice/components/Dialog.tsx b/frontend/src/features/BackOffice/components/Dialog.tsx deleted file mode 100644 index 6a6eb32f1..000000000 --- a/frontend/src/features/BackOffice/components/Dialog.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Button, Dialog as MonitorUiDialog } from '@mtes-mct/monitor-ui' -import { useCallback } from 'react' - -import { backOfficeActions } from '../../../domain/shared_slices/BackOffice' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' - -export function Dialog() { - const dispatch = useAppDispatch() - const backOffice = useAppSelector(store => store.backOffice) - - const close = useCallback(() => { - dispatch(backOfficeActions.closeDialog()) - }, [dispatch]) - - return ( - - - {backOffice.dialogMessage} - - - Fermer - - - ) -} diff --git a/frontend/src/features/BackOffice/slice.ts b/frontend/src/features/BackOffice/slice.ts new file mode 100644 index 000000000..941f9185a --- /dev/null +++ b/frontend/src/features/BackOffice/slice.ts @@ -0,0 +1,50 @@ +import { createSlice, type PayloadAction } from '@reduxjs/toolkit' + +import type { ConfirmationModalState, DialogState } from './types' + +interface BackOfficeState { + /** Shared Confirmation Modal */ + confirmationModal: ConfirmationModalState | undefined + + /** Shared Dialog */ + dialog: DialogState | undefined + + isConfirmationModalOpen: boolean + isDialogOpen: boolean +} +const INITIAL_STATE: BackOfficeState = { + confirmationModal: undefined, + dialog: undefined, + + isConfirmationModalOpen: false, + isDialogOpen: false +} + +const backOfficeSlice = createSlice({ + initialState: INITIAL_STATE, + name: 'backOffice', + reducers: { + closeConfirmationModal(state) { + state.confirmationModal = undefined + state.isConfirmationModalOpen = false + }, + + closeDialog(state) { + state.dialog = undefined + state.isDialogOpen = false + }, + + openConfirmationModal(state, action: PayloadAction) { + state.confirmationModal = action.payload + state.isConfirmationModalOpen = true + }, + + openDialog(state, action: PayloadAction) { + state.dialog = action.payload + state.isDialogOpen = true + } + } +}) + +export const backOfficeActions = backOfficeSlice.actions +export const backOfficeReducer = backOfficeSlice.reducer diff --git a/frontend/src/features/BackOffice/types.ts b/frontend/src/features/BackOffice/types.ts new file mode 100644 index 000000000..27e62bf87 --- /dev/null +++ b/frontend/src/features/BackOffice/types.ts @@ -0,0 +1,20 @@ +import type { ConfirmationModalProps } from '../../components/ConfirmationModal' +import type { DialogProps } from '../../components/Dialog' + +export type ConfirmationModalState = { + actionType: BackOfficeConfirmationModalActionType + /** ID of the targeted entity. */ + entityId: number + modalProps: Omit +} + +export type DialogState = { + dialogProps: Omit +} + +export enum BackOfficeConfirmationModalActionType { + 'ARCHIVE_ADMINISTRATION' = 'ARCHIVE_ADMINISTRATION', + 'ARCHIVE_CONTROL_UNIT' = 'ARCHIVE_CONTROL_UNIT', + 'DELETE_ADMINISTRATION' = 'DELETE_ADMINISTRATION', + 'DELETE_CONTROL_UNIT' = 'DELETE_CONTROL_UNIT' +} diff --git a/frontend/src/features/BackOffice/useCases/handleModalConfirmation.ts b/frontend/src/features/BackOffice/useCases/handleModalConfirmation.ts new file mode 100644 index 000000000..bc5df13d3 --- /dev/null +++ b/frontend/src/features/BackOffice/useCases/handleModalConfirmation.ts @@ -0,0 +1,39 @@ +import { FrontendError } from '../../../libs/FrontendError' +import { archiveAdministration } from '../../Administration/useCases/archiveAdministration' +import { deleteAdministration } from '../../Administration/useCases/deleteAdministration' +import { archiveControlUnit } from '../../ControlUnit/usesCases/archiveControlUnit' +import { deleteControlUnit } from '../../ControlUnit/usesCases/deleteControlUnit' +import { backOfficeActions } from '../slice' +import { BackOfficeConfirmationModalActionType } from '../types' + +import type { AppThunk } from '../../../store' + +export const handleModalConfirmation = (): AppThunk => async (dispatch, getState) => { + const { confirmationModal } = getState().backOffice + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + switch (confirmationModal.actionType) { + case BackOfficeConfirmationModalActionType.ARCHIVE_ADMINISTRATION: + await dispatch(archiveAdministration()) + break + + case BackOfficeConfirmationModalActionType.ARCHIVE_CONTROL_UNIT: + await dispatch(archiveControlUnit()) + break + + case BackOfficeConfirmationModalActionType.DELETE_ADMINISTRATION: + await dispatch(deleteAdministration()) + break + + case BackOfficeConfirmationModalActionType.DELETE_CONTROL_UNIT: + await dispatch(deleteControlUnit()) + break + + default: + break + } + + dispatch(backOfficeActions.closeConfirmationModal()) +} diff --git a/frontend/src/features/Bases/BackOfficeBaseForm/constants.ts b/frontend/src/features/Base/components/BaseForm/constants.ts similarity index 100% rename from frontend/src/features/Bases/BackOfficeBaseForm/constants.ts rename to frontend/src/features/Base/components/BaseForm/constants.ts diff --git a/frontend/src/features/Bases/BackOfficeBaseForm/index.tsx b/frontend/src/features/Base/components/BaseForm/index.tsx similarity index 88% rename from frontend/src/features/Bases/BackOfficeBaseForm/index.tsx rename to frontend/src/features/Base/components/BaseForm/index.tsx index 7e76e8c59..02f7586dc 100644 --- a/frontend/src/features/Bases/BackOfficeBaseForm/index.tsx +++ b/frontend/src/features/Base/components/BaseForm/index.tsx @@ -7,16 +7,16 @@ import styled from 'styled-components' import { INITIAL_BASE_FORM_VALUES, BASE_FORM_SCHEMA } from './constants' import { isBase } from './utils' -import { basesAPI, useGetBaseQuery } from '../../../api/basesAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { FrontendError } from '../../../libs/FrontendError' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' -import { CONTROL_UNIT_RESOURCE_TABLE_COLUMNS } from '../../ControlUnits/BackOfficeControlUnitForm/constants' +import { basesAPI, useGetBaseQuery } from '../../../../api/basesAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { FrontendError } from '../../../../libs/FrontendError' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' +import { CONTROL_UNIT_RESOURCE_TABLE_COLUMNS } from '../../../ControlUnit/components/ControlUnitForm/constants' import type { BaseFormValues } from './types' -import type { Base } from '../../../domain/entities/base' +import type { Base } from '../../../../domain/entities/base' -export function BackOfficeBaseForm() { +export function BaseForm() { const { baseId } = useParams() if (!baseId) { throw new FrontendError('`baseId` is undefined.') diff --git a/frontend/src/features/Bases/BackOfficeBaseForm/types.ts b/frontend/src/features/Base/components/BaseForm/types.ts similarity index 68% rename from frontend/src/features/Bases/BackOfficeBaseForm/types.ts rename to frontend/src/features/Base/components/BaseForm/types.ts index 75c3086c3..8b8ebb239 100644 --- a/frontend/src/features/Bases/BackOfficeBaseForm/types.ts +++ b/frontend/src/features/Base/components/BaseForm/types.ts @@ -1,4 +1,4 @@ -import type { Base } from '../../../domain/entities/base' +import type { Base } from '../../../../domain/entities/base' import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' export type BaseFormValues = UndefineExceptArrays diff --git a/frontend/src/features/Bases/BackOfficeBaseForm/utils.ts b/frontend/src/features/Base/components/BaseForm/utils.ts similarity index 64% rename from frontend/src/features/Bases/BackOfficeBaseForm/utils.ts rename to frontend/src/features/Base/components/BaseForm/utils.ts index 4367f4fa7..2c9314b2c 100644 --- a/frontend/src/features/Bases/BackOfficeBaseForm/utils.ts +++ b/frontend/src/features/Base/components/BaseForm/utils.ts @@ -1,4 +1,4 @@ -import type { Base } from '../../../domain/entities/base' +import type { Base } from '../../../../domain/entities/base' export function isBase(baseData: Base.BaseData): baseData is Base.Base { return baseData.id !== undefined diff --git a/frontend/src/features/Bases/BackOfficeBaseList/FilterBar.tsx b/frontend/src/features/Base/components/BaseTable/FilterBar.tsx similarity index 73% rename from frontend/src/features/Bases/BackOfficeBaseList/FilterBar.tsx rename to frontend/src/features/Base/components/BaseTable/FilterBar.tsx index e5cbe189e..cc6679cd2 100644 --- a/frontend/src/features/Bases/BackOfficeBaseList/FilterBar.tsx +++ b/frontend/src/features/Base/components/BaseTable/FilterBar.tsx @@ -2,9 +2,9 @@ import { Icon, TextInput } from '@mtes-mct/monitor-ui' import { useCallback } from 'react' import styled from 'styled-components' -import { backOfficeBaseListActions } from './slice' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' +import { baseTableActions } from './slice' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' export function FilterBar() { const dispatch = useAppDispatch() @@ -12,7 +12,7 @@ export function FilterBar() { const updateQuery = useCallback( (nextValue: string | undefined) => { - dispatch(backOfficeBaseListActions.setFilter({ key: 'query', value: nextValue })) + dispatch(baseTableActions.setFilter({ key: 'query', value: nextValue })) }, [dispatch] ) diff --git a/frontend/src/features/Bases/BackOfficeBaseList/constants.tsx b/frontend/src/features/Base/components/BaseTable/constants.tsx similarity index 86% rename from frontend/src/features/Bases/BackOfficeBaseList/constants.tsx rename to frontend/src/features/Base/components/BaseTable/constants.tsx index d58a1b3f5..ae7cc862f 100644 --- a/frontend/src/features/Bases/BackOfficeBaseList/constants.tsx +++ b/frontend/src/features/Base/components/BaseTable/constants.tsx @@ -1,9 +1,9 @@ import { Icon, Size } from '@mtes-mct/monitor-ui' -import { NavIconButton } from '../../../ui/NavIconButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { NavIconButton } from '../../../../ui/NavIconButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -import type { Base } from '../../../domain/entities/base' +import type { Base } from '../../../../domain/entities/base' import type { ColumnDef } from '@tanstack/react-table' export const BASE_TABLE_COLUMNS: Array> = [ diff --git a/frontend/src/features/Bases/BackOfficeBaseList/index.tsx b/frontend/src/features/Base/components/BaseTable/index.tsx similarity index 84% rename from frontend/src/features/Bases/BackOfficeBaseList/index.tsx rename to frontend/src/features/Base/components/BaseTable/index.tsx index 2c23943f0..c8420fed1 100644 --- a/frontend/src/features/Bases/BackOfficeBaseList/index.tsx +++ b/frontend/src/features/Base/components/BaseTable/index.tsx @@ -5,12 +5,12 @@ import styled from 'styled-components' import { BASE_TABLE_COLUMNS } from './constants' import { FilterBar } from './FilterBar' import { getFilters } from './utils' -import { useGetBasesQuery } from '../../../api/basesAPI' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { NavButton } from '../../../ui/NavButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { useGetBasesQuery } from '../../../../api/basesAPI' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { NavButton } from '../../../../ui/NavButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -export function BackOfficeBaseList() { +export function BaseTable() { const backOfficeBaseList = useAppSelector(store => store.backOfficeBaseList) const { data: bases } = useGetBasesQuery() diff --git a/frontend/src/features/Bases/BackOfficeBaseList/slice.ts b/frontend/src/features/Base/components/BaseTable/slice.ts similarity index 62% rename from frontend/src/features/Bases/BackOfficeBaseList/slice.ts rename to frontend/src/features/Base/components/BaseTable/slice.ts index 5bb7d556f..a858b2535 100644 --- a/frontend/src/features/Bases/BackOfficeBaseList/slice.ts +++ b/frontend/src/features/Base/components/BaseTable/slice.ts @@ -5,22 +5,22 @@ import storage from 'redux-persist/lib/storage' import type { FiltersState } from './types' -export type BackOfficeBaseListState = { +interface BaseTableState { filtersState: FiltersState } -const INITIAL_STATE: BackOfficeBaseListState = { +const INITIAL_STATE: BaseTableState = { filtersState: {} } const persistConfig = { - key: 'backOfficeBaseList', + key: 'baseTable', storage } -const backOfficeBaseList = createSlice({ +const baseTableSlice = createSlice({ initialState: INITIAL_STATE, - name: 'backOfficeBaseList', + name: 'baseTable', reducers: { setFilter( state, @@ -34,6 +34,6 @@ const backOfficeBaseList = createSlice({ } }) -export const backOfficeBaseListActions = backOfficeBaseList.actions +export const baseTableActions = baseTableSlice.actions -export const backOfficeBaseListPersistedReducer = persistReducer(persistConfig, backOfficeBaseList.reducer) +export const baseTablePersistedReducer = persistReducer(persistConfig, baseTableSlice.reducer) diff --git a/frontend/src/features/Bases/BackOfficeBaseList/types.ts b/frontend/src/features/Base/components/BaseTable/types.ts similarity index 100% rename from frontend/src/features/Bases/BackOfficeBaseList/types.ts rename to frontend/src/features/Base/components/BaseTable/types.ts diff --git a/frontend/src/features/Bases/BackOfficeBaseList/utils.ts b/frontend/src/features/Base/components/BaseTable/utils.ts similarity index 90% rename from frontend/src/features/Bases/BackOfficeBaseList/utils.ts rename to frontend/src/features/Base/components/BaseTable/utils.ts index 0f2dac9d1..43ff6533d 100644 --- a/frontend/src/features/Bases/BackOfficeBaseList/utils.ts +++ b/frontend/src/features/Base/components/BaseTable/utils.ts @@ -1,7 +1,7 @@ import { CustomSearch, type Filter } from '@mtes-mct/monitor-ui' import type { FiltersState } from './types' -import type { Base } from '../../../domain/entities/base' +import type { Base } from '../../../../domain/entities/base' export function getFilters(data: Base.Base[], filtersState: FiltersState): Filter[] { const customSearch = new CustomSearch(data, ['name'], { diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/AreaNote.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/AreaNote.tsx new file mode 100644 index 000000000..41bdab247 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/AreaNote.tsx @@ -0,0 +1,35 @@ +import styled from 'styled-components' + +import { Section } from './shared/Section' +import { TextareaForm } from './shared/TextareaForm' + +import type { ControlUnit } from '../../../../domain/entities/controlUnit' + +type AreaNoteProps = { + controlUnit: ControlUnit.ControlUnit + onSubmit: (nextControlUnit: ControlUnit.ControlUnit) => any +} +export function AreaNote({ controlUnit, onSubmit }: AreaNoteProps) { + return ( + + Secteur d’intervention + + + + + ) +} + +const StyledSectionBody = styled(Section.Body)` + padding: 24px 32px; + + > div:not(:first-child) { + margin-top: 8px; + } +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Form.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Form.tsx new file mode 100644 index 000000000..a5f7cd087 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Form.tsx @@ -0,0 +1,114 @@ +import { Accent, Button, FormikTextInput, Icon, IconButton, THEME, useKey } from '@mtes-mct/monitor-ui' +import { Formik } from 'formik' +import { useCallback } from 'react' +import styled from 'styled-components' + +import { CONTROL_UNIT_CONTACT_FORM_SCHEMA } from './constants' +import { FormikNameSelect } from './FormikNameSelect' +import { useAppDispatch } from '../../../../../hooks/useAppDispatch' +import { mainWindowActions } from '../../../../MainWindow/slice' +import { MainWindowConfirmationModalActionType } from '../../../../MainWindow/types' + +import type { ControlUnitContactFormValues } from './types' + +export type FormProps = { + initialValues: ControlUnitContactFormValues + isNew: boolean + onCancel: () => void + onSubmit: (controlUnitContactFormValues: ControlUnitContactFormValues) => void +} +export function Form({ initialValues, isNew, onCancel, onSubmit }: FormProps) { + const dispatch = useAppDispatch() + const key = useKey([initialValues]) + + const askForDeletionConfirmation = useCallback(async () => { + if (!initialValues.id) { + return + } + + dispatch( + mainWindowActions.openConfirmationModal({ + actionType: MainWindowConfirmationModalActionType.DELETE_CONTROL_UNIT_CONTACT, + entityId: initialValues.id, + modalProps: { + color: THEME.color.maximumRed, + confirmationButtonLabel: 'Supprimer', + iconName: 'Delete', + message: `Êtes-vous sûr de vouloir supprimer ce contact ?`, + title: `Suppression du contact` + } + }) + ) + }, [initialValues.id, dispatch]) + + return ( + + {({ handleSubmit }) => ( + <> + {isNew ? 'Ajouter un contact' : 'Éditer un contact'} + + + + + + + + {isNew ? 'Ajouter' : 'Enregistrer les modifications'} + + Annuler + + + {!isNew && ( + + )} + + + > + )} + + ) +} + +const Title = styled.p` + background-color: ${p => p.theme.color.gainsboro}; + margin: 16px 0 2px; + padding: 8px 16px; + /* TODO This should be the default height everywhere to have a consistent and exact height of 18px. */ + /* Monitor UI provides that value: https://github.com/MTES-MCT/monitor-ui/blob/main/src/GlobalStyle.ts#L76. */ + line-height: 1.3846; +` + +const StyledForm = styled.form` + background-color: ${p => p.theme.color.gainsboro}; + padding: 16px; + + > div:not(:first-child) { + margin-top: 16px; + } +` + +const ActionBar = styled.div` + display: flex; + justify-content: space-between; + + > div:first-child { + > .Element-Button:last-child { + margin-left: 8px; + } + } +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/FormikNameSelect.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/FormikNameSelect.tsx new file mode 100644 index 000000000..fc710171e --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/FormikNameSelect.tsx @@ -0,0 +1,78 @@ +import { Accent, FormikTextInput, Icon, IconButton, Select } from '@mtes-mct/monitor-ui' +import { useField } from 'formik' +import { useCallback, useEffect, useState } from 'react' +import styled from 'styled-components' + +import { CONTROL_UNIT_CONTACT_NAMES, CONTROL_UNIT_CONTACT_NAMES_AS_OPTIONS } from './constants' +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + +export function FormikNameSelect() { + const [field, meta, helpers] = useField('name') + + const [isCustomName, setIsCustomName] = useState( + !!field.value && !ControlUnit.ControlUnitContactName[field.value] + ) + + const cancelCustomName = useCallback( + () => { + setIsCustomName(false) + helpers.setValue(undefined) + }, + + // We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ) + + const handleChange = useCallback( + (nextName: string | undefined) => { + if (nextName === 'SWITCH_TO_CUSTOM_NAME') { + setIsCustomName(true) + + return + } + + helpers.setValue(nextName) + }, + + // We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ) + + useEffect(() => { + if (!isCustomName && field.value && CONTROL_UNIT_CONTACT_NAMES.includes(field.value)) { + setIsCustomName(false) + } + }, [field.value, isCustomName]) + + return isCustomName ? ( + + + + + ) : ( + + ) +} + +const Wrapper = styled.div` + align-items: flex-start; + display: flex; + margin-bottom: 16px; + + > .Element-Field { + flex-grow: 1; + } + > .Element-IconButton { + margin: 22px 0 0 8px; + } +` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Item.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Item.tsx similarity index 65% rename from frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Item.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Item.tsx index 5097aed43..a37ac5a49 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Item.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/Item.tsx @@ -1,7 +1,9 @@ import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' +import { useCallback } from 'react' import styled from 'styled-components' -import type { ControlUnit } from '../../../../domain/entities/controlUnit' +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + import type { Promisable } from 'type-fest' export type ItemProps = { @@ -9,11 +11,16 @@ export type ItemProps = { onEdit: (controlUnitContactId: number) => Promisable } export function Item({ controlUnitContact, onEdit }: ItemProps) { + const handleEdit = useCallback(() => { + onEdit(controlUnitContact.id) + }, [controlUnitContact.id, onEdit]) + return ( - + - {controlUnitContact.name} {controlUnitContact.phone} + {ControlUnit.ControlUnitContactName[controlUnitContact.name] || controlUnitContact.name} + {controlUnitContact.phone} @@ -22,7 +29,7 @@ export function Item({ controlUnitContact, onEdit }: ItemProps) { - onEdit(controlUnitContact.id)} /> + ) @@ -50,3 +57,7 @@ const Name = styled.span` color: ${p => p.theme.color.gunMetal}; font-weight: bold; ` + +const Phone = styled.span` + margin-left: 16px; +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/constants.ts b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/constants.ts new file mode 100644 index 000000000..d0ab7733c --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/constants.ts @@ -0,0 +1,37 @@ +import { getOptionsFromLabelledEnum } from '@mtes-mct/monitor-ui' +import { object, string } from 'yup' + +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + +import type { ControlUnitContactFormValues } from './types' + +export const CONTROL_UNIT_CONTACT_FORM_SCHEMA = object().shape( + { + email: string().when('phone', { + is: phone => !phone, + then: shema => shema.required('Veuillez entrer un téléphone ou un email.') + }), + name: string().required('Veuillez choisir un nom.'), + phone: string().when('email', { + is: email => !email, + then: shema => shema.required('Veuillez entrer un téléphone ou un email.') + }) + }, + [['email', 'phone']] +) + +export const INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES: ControlUnitContactFormValues = { + controlUnitId: undefined, + email: undefined, + name: undefined, + phone: undefined +} + +export const CONTROL_UNIT_CONTACT_NAMES: string[] = Object.values(ControlUnit.ControlUnitContactName) +export const CONTROL_UNIT_CONTACT_NAMES_AS_OPTIONS = [ + ...getOptionsFromLabelledEnum(ControlUnit.ControlUnitContactName), + { + label: 'Créer un nom personnalisé', + value: 'SWITCH_TO_CUSTOM_NAME' + } +] diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/index.tsx new file mode 100644 index 000000000..6c01a1d65 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/index.tsx @@ -0,0 +1,105 @@ +import { Accent, Button } from '@mtes-mct/monitor-ui' +import { useCallback, useState } from 'react' +import styled from 'styled-components' + +import { INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES } from './constants' +import { Form } from './Form' +import { Item } from './Item' +import { + useCreateControlUnitContactMutation, + useUpdateControlUnitContactMutation +} from '../../../../../api/controlUnitContactsAPI' +import { Section } from '../shared/Section' +import { TextareaForm } from '../shared/TextareaForm' + +import type { ControlUnitContactFormValues } from './types' +import type { ControlUnit } from '../../../../../domain/entities/controlUnit' + +type ControlUnitContactListProps = { + controlUnit: ControlUnit.ControlUnit + onSubmit: (nextControlUnit: ControlUnit.ControlUnit) => any +} +export function ControlUnitContactList({ controlUnit, onSubmit }: ControlUnitContactListProps) { + const [createControlUnitContact] = useCreateControlUnitContactMutation() + const [updateControlUnitContact] = useUpdateControlUnitContactMutation() + + const [editedControlUnitContactId, setEditedControlUnitContactId] = useState(undefined) + const [isNewControlUnitContactFormOpen, setIsNewControlUnitContactFormOpen] = useState(false) + + const { controlUnitContacts } = controlUnit + const editedControlUnitContact = controlUnitContacts.find(({ id }) => id === editedControlUnitContactId) || { + ...INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES, + controlUnitId: controlUnit.id + } + const isFormOpen = isNewControlUnitContactFormOpen || !!editedControlUnitContactId + + const closeForm = useCallback(() => { + setEditedControlUnitContactId(undefined) + setIsNewControlUnitContactFormOpen(false) + }, []) + + const createOrUpdateControlUnitContact = useCallback( + async (controlUnitContactFormValues: ControlUnitContactFormValues) => { + if (isNewControlUnitContactFormOpen) { + await createControlUnitContact(controlUnitContactFormValues as ControlUnit.NewControlUnitContactData) + } else { + await updateControlUnitContact(controlUnitContactFormValues as ControlUnit.ControlUnitContactData) + } + + closeForm() + }, + [closeForm, createControlUnitContact, isNewControlUnitContactFormOpen, updateControlUnitContact] + ) + + const openForm = useCallback(() => { + setIsNewControlUnitContactFormOpen(true) + }, []) + + return ( + + Contacts + + + + {controlUnitContacts.map(controlUnitContact => ( + + ))} + + {isFormOpen ? ( + + ) : ( + + + Ajouter un contact + + + )} + + + ) +} + +const StyledSectionBody = styled(Section.Body)` + padding: 16px 32px 24px; + + > .Field-Textarea { + margin-bottom: 16px; + } + > div:last-child { + margin-top: 16px; + } +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/types.ts b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/types.ts new file mode 100644 index 000000000..849a0ddf3 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitContactList/types.ts @@ -0,0 +1,8 @@ +import type { ControlUnit } from '../../../../../domain/entities/controlUnit' +import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' + +export type ControlUnitContactFormValues = UndefineExceptArrays< + ControlUnit.ControlUnitContactData | ControlUnit.NewControlUnitContactData +> & { + id?: number +} diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Form.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Form.tsx new file mode 100644 index 000000000..6c18889c5 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Form.tsx @@ -0,0 +1,137 @@ +import { + Accent, + Button, + FormikSelect, + FormikTextInput, + FormikTextarea, + Icon, + IconButton, + THEME, + getOptionsFromIdAndName, + useKey +} from '@mtes-mct/monitor-ui' +import { Formik } from 'formik' +import { useCallback } from 'react' +import styled from 'styled-components' + +import { CONTROL_UNIT_RESOURCE_FORM_SCHEMA, CONTROL_UNIT_RESOURCE_TYPES_AS_OPTIONS } from './constants' +import { useGetBasesQuery } from '../../../../../api/basesAPI' +import { useAppDispatch } from '../../../../../hooks/useAppDispatch' +import { mainWindowActions } from '../../../../MainWindow/slice' +import { MainWindowConfirmationModalActionType } from '../../../../MainWindow/types' + +import type { ControlUnitResourceFormValues } from './types' + +export type FormProps = { + initialValues: ControlUnitResourceFormValues + isNew: boolean + onCancel: () => void + onSubmit: (controlUnitResourceFormValues: ControlUnitResourceFormValues) => void +} +export function Form({ initialValues, isNew, onCancel, onSubmit }: FormProps) { + const dispatch = useAppDispatch() + const key = useKey([initialValues]) + + const { data: bases } = useGetBasesQuery() + + const basesAsOptions = getOptionsFromIdAndName(bases)?.filter(baseAsOption => baseAsOption.value !== 0) + + const askForDeletionConfirmation = useCallback(async () => { + if (!initialValues.id) { + return + } + + dispatch( + mainWindowActions.openConfirmationModal({ + actionType: MainWindowConfirmationModalActionType.DELETE_CONTROL_UNIT_RESOURCE, + entityId: initialValues.id, + modalProps: { + color: THEME.color.maximumRed, + confirmationButtonLabel: 'Supprimer', + iconName: 'Delete', + message: `Êtes-vous sûr de vouloir supprimer ce moyen ?`, + title: `Suppression du moyen` + } + }) + ) + }, [initialValues.id, dispatch]) + + if (!basesAsOptions) { + return Chargement en cours... + } + + return ( + + {({ handleSubmit }) => ( + <> + {isNew ? 'Ajouter un moyen' : 'Éditer un moyen'} + + + + + + + + + {isNew ? 'Ajouter' : 'Enregistrer les modifications'} + + Annuler + + + {!isNew && ( + + )} + + + > + )} + + ) +} + +const Title = styled.p` + background-color: ${p => p.theme.color.gainsboro}; + margin: 16px 0 2px; + padding: 8px 16px; + line-height: 1.3846; +` + +const StyledForm = styled.form` + background-color: ${p => p.theme.color.gainsboro}; + padding: 16px; + + > div:not(:first-child) { + margin-top: 16px; + } + > div:last-child { + > .Element-Button:not(:first-child) { + margin-left: 8px; + } + } +` + +const ActionBar = styled.div` + display: flex; + justify-content: space-between; + + > div:first-child { + > .Element-Button:last-child { + margin-left: 8px; + } + } +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Item.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Item.tsx new file mode 100644 index 000000000..ee04203c9 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/Item.tsx @@ -0,0 +1,73 @@ +import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' +import { useCallback } from 'react' +import styled from 'styled-components' + +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + +import type { Promisable } from 'type-fest' + +export type ItemProps = { + controlUnitResource: ControlUnit.ControlUnitResource + onEdit: (controlUnitResourceId: number) => Promisable +} +export function Item({ controlUnitResource, onEdit }: ItemProps) { + const handleEdit = useCallback(() => { + onEdit(controlUnitResource.id) + }, [controlUnitResource.id, onEdit]) + + return ( + + + + + + + {ControlUnit.ControlUnitResourceTypeLabel[controlUnitResource.type]} – {controlUnitResource.name} + + {controlUnitResource.base.name} + + + + + + {controlUnitResource.note} + + + ) +} + +const Wrapper = styled.div` + background-color: ${p => p.theme.color.cultured}; + display: flex; +` + +const InfoBox = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 8px 16px; +` + +const InfoBoxHeader = styled.div` + display: flex; + margin-bottom: 8px; + color: ${p => p.theme.color.gunMetal}; + + > div:first-child { + display: flex; + flex-direction: column; + flex-grow: 1; + } +` + +const Placeholder = styled.div` + background-color: ${p => p.theme.color.lightGray}; + height: 94px; + min-height: 94px; + min-width: 116px; + width: 116px; +` + +const Name = styled.p` + font-weight: bold; +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/constants.ts b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/constants.ts new file mode 100644 index 000000000..76ed1b3c7 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/constants.ts @@ -0,0 +1,25 @@ +import { getOptionsFromLabelledEnum } from '@mtes-mct/monitor-ui' +import { object, string } from 'yup' + +import { ControlUnit } from '../../../../../domain/entities/controlUnit' + +import type { ControlUnitResourceFormValues } from './types' + +export const CONTROL_UNIT_RESOURCE_FORM_SCHEMA = object().shape({ + baseId: string().required('Veuillez choisir une base.'), + type: string().required('Veuillez choisir un type.') +}) + +export const CONTROL_UNIT_RESOURCE_TYPES_AS_OPTIONS = getOptionsFromLabelledEnum( + ControlUnit.ControlUnitResourceTypeLabel, + true +) + +export const INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES: ControlUnitResourceFormValues = { + baseId: undefined, + controlUnitId: undefined, + name: undefined, + note: undefined, + photo: undefined, + type: undefined +} diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/index.tsx new file mode 100644 index 000000000..1f07026ca --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/index.tsx @@ -0,0 +1,111 @@ +// import styled from 'styled-components' + +import { Accent, Button, Icon } from '@mtes-mct/monitor-ui' +import { useCallback, useState } from 'react' +import styled from 'styled-components' + +import { INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES } from './constants' +import { Form } from './Form' +import { Item } from './Item' +import { + useCreateControlUnitResourceMutation, + useUpdateControlUnitResourceMutation +} from '../../../../../api/controlUnitResourcesAPI' +import { ControlUnit } from '../../../../../domain/entities/controlUnit' +import { isEmptyish } from '../../../../../utils/isEmptyish' +import { Section } from '../shared/Section' + +import type { ControlUnitResourceFormValues } from './types' + +type ControlUnitResourceListProps = { + controlUnit: ControlUnit.ControlUnit +} +export function ControlUnitResourceList({ controlUnit }: ControlUnitResourceListProps) { + const [createControlUnitResource] = useCreateControlUnitResourceMutation() + const [updateControlUnitResource] = useUpdateControlUnitResourceMutation() + + const [editedControlUnitResourceId, setEditedControlUnitResourceId] = useState(undefined) + const [isNewControlUnitResourceFormOpen, setIsNewControlUnitResourceFormOpen] = useState(false) + + const { controlUnitResources } = controlUnit + const editedControlUnitResource = controlUnitResources.find(({ id }) => id === editedControlUnitResourceId) || { + ...INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES, + controlUnitId: controlUnit.id + } + const isFormOpen = isNewControlUnitResourceFormOpen || !!editedControlUnitResourceId + + const closeForm = useCallback(() => { + setEditedControlUnitResourceId(undefined) + setIsNewControlUnitResourceFormOpen(false) + }, []) + + const createOrUpdateControlUnitResource = useCallback( + async (controlUnitResourceFormValues: ControlUnitResourceFormValues) => { + const controlledControlUnitResourceFormValues = { + ...controlUnitResourceFormValues, + // We set the resource type as the resource name if no name has been provided by the user + name: isEmptyish(controlUnitResourceFormValues.name) + ? ControlUnit.ControlUnitResourceTypeLabel[controlUnitResourceFormValues.type as string] + : controlUnitResourceFormValues.name + } + + if (isNewControlUnitResourceFormOpen) { + await createControlUnitResource( + controlledControlUnitResourceFormValues as ControlUnit.NewControlUnitResourceData + ) + } else { + await updateControlUnitResource(controlledControlUnitResourceFormValues as ControlUnit.ControlUnitResourceData) + } + + closeForm() + }, + [closeForm, createControlUnitResource, isNewControlUnitResourceFormOpen, updateControlUnitResource] + ) + + const openForm = useCallback(() => { + setIsNewControlUnitResourceFormOpen(true) + }, []) + + return ( + + Moyens + + {controlUnitResources.map(controlUnitResource => ( + + ))} + + {isFormOpen ? ( + + ) : ( + + + Ajouter un moyen + + + )} + + + ) +} + +const StyledSectionBody = styled(Section.Body)<{ + $isEmpty: boolean +}>` + padding: 24px 32px; + + > div:not(:first-child) { + margin-top: 8px; + } + > div:last-child { + margin-top: ${p => (!p.$isEmpty ? 16 : 0)}px; + } +` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/types.ts b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/types.ts new file mode 100644 index 000000000..360f860e8 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/ControlUnitResourceList/types.ts @@ -0,0 +1,6 @@ +import type { ControlUnit } from '../../../../../domain/entities/controlUnit' +import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' + +export type ControlUnitResourceFormValues = UndefineExceptArrays & { + id?: number +} diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx similarity index 66% rename from frontend/src/features/ControlUnits/MapControlUnitDialog/index.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx index 090075b36..7c34070b0 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx @@ -7,13 +7,13 @@ import styled from 'styled-components' import { AreaNote } from './AreaNote' import { ControlUnitContactList } from './ControlUnitContactList' import { ControlUnitResourceList } from './ControlUnitResourceList' -import { useGetControlUnitQuery } from '../../../api/controlUnitsAPI' -import { globalActions } from '../../../domain/shared_slices/Global' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { FrontendError } from '../../../libs/FrontendError' +import { useGetControlUnitQuery, useUpdateControlUnitMutation } from '../../../../api/controlUnitsAPI' +import { globalActions } from '../../../../domain/shared_slices/Global' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { FrontendError } from '../../../../libs/FrontendError' -export function MapControlUnitDialog() { +export function ControlUnitDialog() { const dispatch = useAppDispatch() const mapControlUnitDialog = useAppSelector(store => store.mapControlUnitDialog) if (!mapControlUnitDialog.controlUnitId) { @@ -21,6 +21,7 @@ export function MapControlUnitDialog() { } const { data: controlUnit } = useGetControlUnitQuery(mapControlUnitDialog.controlUnitId) + const [updateControlUnit] = useUpdateControlUnitMutation() const close = useCallback(() => { dispatch( @@ -41,6 +42,17 @@ export function MapControlUnitDialog() { ) } + if (!controlUnit) { + return ( + + + Chargement en cours... + + + + ) + } + return ( @@ -51,9 +63,9 @@ export function MapControlUnitDialog() { - - - + + + diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/shared/Section.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/Section.tsx similarity index 95% rename from frontend/src/features/ControlUnits/MapControlUnitDialog/shared/Section.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/Section.tsx index c21c54bca..d6dda96f5 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/shared/Section.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/Section.tsx @@ -7,7 +7,6 @@ const Wrapper = styled.div` const Body = styled.div` background-color: ${p => p.theme.color.white}; - padding: 24px 32px; ` const Title = styled.div` diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/TextareaForm.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/TextareaForm.tsx new file mode 100644 index 000000000..d77573028 --- /dev/null +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/shared/TextareaForm.tsx @@ -0,0 +1,91 @@ +import { Accent, Button, Label, Textarea, type TextareaProps } from '@mtes-mct/monitor-ui' +import { useCallback, useState, type FormEvent, type ChangeEvent } from 'react' +import styled from 'styled-components' + +import type { ControlUnit } from '../../../../../domain/entities/controlUnit' +import type { Promisable } from 'type-fest' + +type TextareaFormProps = Omit & { + controlUnit: ControlUnit.ControlUnit + name: 'areaNote' | 'termsNote' + onSubmit: (nextControlUnit: ControlUnit.ControlUnit) => Promisable +} +export function TextareaForm({ controlUnit, isLabelHidden, label, name, onSubmit, ...props }: TextareaFormProps) { + const [isEditing, setIsEditing] = useState(false) + const [value, setValue] = useState(controlUnit[name]) + + const moveCursorToEnd = useCallback((event: ChangeEvent) => { + event.target.setSelectionRange(event.target.value.length, event.target.value.length) + }, []) + + const updateControlUnit = (event: FormEvent) => { + event.preventDefault() + + const nextControlUnit = { + ...controlUnit, + [name]: value + } + + onSubmit(nextControlUnit) + + setIsEditing(false) + } + + const toggleIsEditing = useCallback(() => { + setIsEditing(!isEditing) + }, [isEditing]) + + if (isEditing) { + return ( + + + + + Annuler + + Valider + + + ) + } + + return ( + <> + {!isLabelHidden && {label}} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */} + + {controlUnit[name]} + + > + ) +} + +const Form = styled.form` + > div:not(:first-child) { + display: flex; + justify-content: flex-end; + margin-top: 8px; + + > .Element-Button:last-child { + margin-left: 8px; + } + } +` + +const TextBox = styled.div` + background-color: ${p => p.theme.color.gainsboro}; + cursor: text; + height: 72px; + padding: 8px 12px; +` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/slice.ts b/frontend/src/features/ControlUnit/components/ControlUnitDialog/slice.ts similarity index 54% rename from frontend/src/features/ControlUnits/MapControlUnitDialog/slice.ts rename to frontend/src/features/ControlUnit/components/ControlUnitDialog/slice.ts index 3c723a91d..459d80521 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/slice.ts +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/slice.ts @@ -1,16 +1,16 @@ import { type PayloadAction, createSlice } from '@reduxjs/toolkit' -export interface MapControlUnitDialog { +interface ControlUnitDialogState { controlUnitId: number | undefined } -const INITIAL_STATE: MapControlUnitDialog = { +const INITIAL_STATE: ControlUnitDialogState = { controlUnitId: undefined } -const mapControlUnitDialogSlice = createSlice({ +const controlUnitDialogSlice = createSlice({ initialState: INITIAL_STATE, - name: 'mapControlUnitDialog', + name: 'controlUnitDialog', reducers: { /** * Set control unit ID to be loaded in map control unit dialog. @@ -21,5 +21,5 @@ const mapControlUnitDialogSlice = createSlice({ } }) -export const mapControlUnitDialogActions = mapControlUnitDialogSlice.actions -export const mapControlUnitDialogReducer = mapControlUnitDialogSlice.reducer +export const controlUnitDialogActions = controlUnitDialogSlice.actions +export const controlUnitDialogReducer = controlUnitDialogSlice.reducer diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/constants.tsx b/frontend/src/features/ControlUnit/components/ControlUnitForm/constants.tsx similarity index 94% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitForm/constants.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitForm/constants.tsx index 08d1025cf..4741e4616 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/constants.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitForm/constants.tsx @@ -1,7 +1,7 @@ import { number, object, string } from 'yup' import type { ControlUnitFormValues } from './types' -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' import type { ColumnDef } from '@tanstack/react-table' export const CONTROL_UNIT_CONTACT_TABLE_COLUMNS: Array> = [ diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitForm/index.tsx similarity index 91% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitForm/index.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitForm/index.tsx index 45edd96b6..7d0ef5118 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitForm/index.tsx @@ -20,17 +20,17 @@ import { CONTROL_UNIT_CONTACT_TABLE_COLUMNS, CONTROL_UNIT_RESOURCE_TABLE_COLUMNS } from './constants' -import { useGetAdministrationsQuery } from '../../../api/administrationsAPI' -import { controlUnitsAPI, useGetControlUnitQuery } from '../../../api/controlUnitsAPI' -import { useGetDepartmentAreasQuery } from '../../../api/departmentAreasAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { FrontendError } from '../../../libs/FrontendError' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { useGetAdministrationsQuery } from '../../../../api/administrationsAPI' +import { controlUnitsAPI, useGetControlUnitQuery } from '../../../../api/controlUnitsAPI' +import { useGetDepartmentAreasQuery } from '../../../../api/departmentAreasAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { FrontendError } from '../../../../libs/FrontendError' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' import type { ControlUnitFormValues } from './types' -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' -export function BackOfficeControlUnitForm() { +export function ControlUnitForm() { const { controlUnitId } = useParams() if (!controlUnitId) { throw new FrontendError('`controlUnitId` is undefined.') diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/types.ts b/frontend/src/features/ControlUnit/components/ControlUnitForm/types.ts similarity index 67% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitForm/types.ts rename to frontend/src/features/ControlUnit/components/ControlUnitForm/types.ts index 69569aa26..f541c6bc4 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitForm/types.ts +++ b/frontend/src/features/ControlUnit/components/ControlUnitForm/types.ts @@ -1,4 +1,4 @@ -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' export type ControlUnitFormValues = UndefineExceptArrays diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/FilterBar.tsx b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/FilterBar.tsx similarity index 78% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/FilterBar.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/FilterBar.tsx index f3054d3b3..53f4250aa 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitListDialog/FilterBar.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/FilterBar.tsx @@ -9,12 +9,12 @@ import { import { useCallback, useMemo } from 'react' import styled from 'styled-components' -import { mapControlUnitListDialogActions } from './slice' -import { useGetAdministrationsQuery } from '../../../api/administrationsAPI' -import { useGetBasesQuery } from '../../../api/basesAPI' -import { ControlUnit } from '../../../domain/entities/controlUnit' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' +import { controlUnitListDialogActions } from './slice' +import { useGetAdministrationsQuery } from '../../../../api/administrationsAPI' +import { useGetBasesQuery } from '../../../../api/basesAPI' +import { ControlUnit } from '../../../../domain/entities/controlUnit' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' export function FilterBar() { const dispatch = useAppDispatch() @@ -31,28 +31,28 @@ export function FilterBar() { const updateAdministrationId = useCallback( (nextValue: number | undefined) => { - dispatch(mapControlUnitListDialogActions.setFilter({ key: 'administrationId', value: nextValue })) + dispatch(controlUnitListDialogActions.setFilter({ key: 'administrationId', value: nextValue })) }, [dispatch] ) const updateBaseId = useCallback( (nextValue: number | undefined) => { - dispatch(mapControlUnitListDialogActions.setFilter({ key: 'baseId', value: nextValue })) + dispatch(controlUnitListDialogActions.setFilter({ key: 'baseId', value: nextValue })) }, [dispatch] ) const updateQuery = useCallback( (nextValue: string | undefined) => { - dispatch(mapControlUnitListDialogActions.setFilter({ key: 'query', value: nextValue })) + dispatch(controlUnitListDialogActions.setFilter({ key: 'query', value: nextValue })) }, [dispatch] ) const updateType = useCallback( (nextValue: string | undefined) => { - dispatch(mapControlUnitListDialogActions.setFilter({ key: 'type', value: nextValue })) + dispatch(controlUnitListDialogActions.setFilter({ key: 'type', value: nextValue })) }, [dispatch] ) diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/Item.tsx b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx similarity index 77% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/Item.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx index 1390aa1b9..bc00da362 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitListDialog/Item.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx @@ -2,11 +2,11 @@ import { useCallback } from 'react' import styled from 'styled-components' import { displayControlUnitResourcesFromControlUnit, displayBaseNamesFromControlUnit } from './utils' -import { globalActions } from '../../../domain/shared_slices/Global' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { mapControlUnitDialogActions } from '../MapControlUnitDialog/slice' +import { globalActions } from '../../../../domain/shared_slices/Global' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { controlUnitDialogActions } from '../ControlUnitDialog/slice' -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' export type ItemProps = { controlUnit: ControlUnit.ControlUnit @@ -15,7 +15,7 @@ export function Item({ controlUnit }: ItemProps) { const dispatch = useAppDispatch() const edit = useCallback(() => { - dispatch(mapControlUnitDialogActions.setControlUnitId(controlUnit.id)) + dispatch(controlUnitDialogActions.setControlUnitId(controlUnit.id)) dispatch( globalActions.setDisplayedItems({ isControlUnitDialogVisible: true, @@ -26,7 +26,7 @@ export function Item({ controlUnit }: ItemProps) { return ( - {controlUnit.name} + {controlUnit.name} {controlUnit.administration.name} {displayControlUnitResourcesFromControlUnit(controlUnit)} {displayBaseNamesFromControlUnit(controlUnit)} diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/index.tsx similarity index 86% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/index.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/index.tsx index 8f91751fa..899f32c72 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitListDialog/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/index.tsx @@ -5,15 +5,15 @@ import { useMemo } from 'react' import { FilterBar } from './FilterBar' import { Item } from './Item' import { getFilters } from './utils' -import { useGetControlUnitsQuery } from '../../../api/controlUnitsAPI' -import { useAppSelector } from '../../../hooks/useAppSelector' +import { useGetControlUnitsQuery } from '../../../../api/controlUnitsAPI' +import { useAppSelector } from '../../../../hooks/useAppSelector' import type { Promisable } from 'type-fest' -export type MapControlUnitListDialogProps = { +type ControlUnitListDialogProps = { onClose: () => Promisable } -export function MapControlUnitListDialog({ onClose }: MapControlUnitListDialogProps) { +export function ControlUnitListDialog({ onClose }: ControlUnitListDialogProps) { const mapControlUnitListDialog = useAppSelector(store => store.mapControlUnitListDialog) const { data: controlUnits } = useGetControlUnitsQuery() diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/slice.ts b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/slice.ts similarity index 59% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/slice.ts rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/slice.ts index 21012ffed..912bef711 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitListDialog/slice.ts +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/slice.ts @@ -5,22 +5,22 @@ import storage from 'redux-persist/lib/storage' import type { FiltersState } from './types' -export type MapControlUnitListDialogState = { +type ControlUnitListDialogState = { filtersState: FiltersState } -const INITIAL_STATE: MapControlUnitListDialogState = { +const INITIAL_STATE: ControlUnitListDialogState = { filtersState: {} } const persistConfig = { - key: 'mapControlUnitListDialog', + key: 'controlUnitListDialog', storage } -const mapControlUnitListDialog = createSlice({ +const controlUnitListDialogSlice = createSlice({ initialState: INITIAL_STATE, - name: 'mapControlUnitListDialog', + name: 'controlUnitListDialog', reducers: { setFilter( state, @@ -34,6 +34,6 @@ const mapControlUnitListDialog = createSlice({ } }) -export const mapControlUnitListDialogActions = mapControlUnitListDialog.actions +export const controlUnitListDialogActions = controlUnitListDialogSlice.actions -export const mapControlUnitListDialogPersistedReducer = persistReducer(persistConfig, mapControlUnitListDialog.reducer) +export const controlUnitListDialogPersistedReducer = persistReducer(persistConfig, controlUnitListDialogSlice.reducer) diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/types.ts b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/types.ts similarity index 100% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/types.ts rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/types.ts diff --git a/frontend/src/features/ControlUnits/MapControlUnitListDialog/utils.ts b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/utils.ts similarity index 97% rename from frontend/src/features/ControlUnits/MapControlUnitListDialog/utils.ts rename to frontend/src/features/ControlUnit/components/ControlUnitListDialog/utils.ts index 238aa2153..69c4f0783 100644 --- a/frontend/src/features/ControlUnits/MapControlUnitListDialog/utils.ts +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/utils.ts @@ -1,7 +1,7 @@ import { CustomSearch, type Filter, isDefined, pluralize } from '@mtes-mct/monitor-ui' import { isEmpty, uniq } from 'lodash/fp' -import { ControlUnit } from '../../../domain/entities/controlUnit' +import { ControlUnit } from '../../../../domain/entities/controlUnit' import type { FiltersState } from './types' diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/FilterBar.tsx b/frontend/src/features/ControlUnit/components/ControlUnitTable/FilterBar.tsx similarity index 76% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/FilterBar.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitTable/FilterBar.tsx index 7f9eb1add..2b61607cb 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/FilterBar.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/FilterBar.tsx @@ -2,10 +2,10 @@ import { Icon, Select, TextInput, getOptionsFromIdAndName } from '@mtes-mct/moni import { useCallback, useMemo } from 'react' import styled from 'styled-components' -import { backOfficeControlUnitListActions } from './slice' -import { useGetAdministrationsQuery } from '../../../api/administrationsAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' +import { controlUnitTableActions } from './slice' +import { useGetAdministrationsQuery } from '../../../../api/administrationsAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' export function FilterBar() { const dispatch = useAppDispatch() @@ -16,14 +16,14 @@ export function FilterBar() { const updateAdministrationId = useCallback( (nextValue: number | undefined) => { - dispatch(backOfficeControlUnitListActions.setFilter({ key: 'administrationId', value: nextValue })) + dispatch(controlUnitTableActions.setFilter({ key: 'administrationId', value: nextValue })) }, [dispatch] ) const updateQuery = useCallback( (nextValue: string | undefined) => { - dispatch(backOfficeControlUnitListActions.setFilter({ key: 'query', value: nextValue })) + dispatch(controlUnitTableActions.setFilter({ key: 'query', value: nextValue })) }, [dispatch] ) diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/TabMenu.tsx b/frontend/src/features/ControlUnit/components/ControlUnitTable/TabMenu.tsx similarity index 52% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/TabMenu.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitTable/TabMenu.tsx index 1846e43c0..fe28cad23 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/TabMenu.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/TabMenu.tsx @@ -1,30 +1,30 @@ -import { backOfficeControlUnitListActions } from './slice' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { TabBar } from '../../BackOffice/components/TabBar' +import { controlUnitTableActions } from './slice' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { BackOfficeTabBar } from '../../../BackOffice/components/BackOfficeTabBar' export function TabMenu() { const dispatch = useAppDispatch() const backOfficeControlUnitList = useAppSelector(store => store.backOfficeControlUnitList) const filterArchivedControlUnits = (isArchived: boolean) => { - dispatch(backOfficeControlUnitListActions.setFilter({ key: 'isArchived', value: isArchived })) + dispatch(controlUnitTableActions.setFilter({ key: 'isArchived', value: isArchived })) } return ( - - + filterArchivedControlUnits(false)} > Unités actives - - + filterArchivedControlUnits(true)} > Unités archivées - - + + ) } diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/constants.tsx b/frontend/src/features/ControlUnit/components/ControlUnitTable/constants.tsx similarity index 88% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/constants.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitTable/constants.tsx index 117836154..0eb7a38fb 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/constants.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/constants.tsx @@ -1,9 +1,9 @@ import { Icon, Size } from '@mtes-mct/monitor-ui' -import { NavIconButton } from '../../../ui/NavIconButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { NavIconButton } from '../../../../ui/NavIconButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -import type { ControlUnit } from '../../../domain/entities/controlUnit' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' import type { ColumnDef } from '@tanstack/react-table' export const CONTROL_UNIT_TABLE_COLUMNS: Array> = [ diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitTable/index.tsx similarity index 84% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/index.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitTable/index.tsx index 6250ec19f..13128cbde 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/index.tsx @@ -5,13 +5,13 @@ import styled from 'styled-components' import { FilterBar } from './FilterBar' import { TabMenu } from './TabMenu' import { getControlUnitTableColumns, getFilters } from './utils' -import { useGetControlUnitsQuery } from '../../../api/controlUnitsAPI' -import { useAppDispatch } from '../../../hooks/useAppDispatch' -import { useAppSelector } from '../../../hooks/useAppSelector' -import { NavButton } from '../../../ui/NavButton' -import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../BackOfficeMenu/constants' +import { useGetControlUnitsQuery } from '../../../../api/controlUnitsAPI' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { NavButton } from '../../../../ui/NavButton' +import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../../../BackOfficeMenu/constants' -export function BackOfficeControlUnitList() { +export function ControlUnitTable() { const backOfficeControlUnitList = useAppSelector(store => store.backOfficeControlUnitList) const dispatch = useAppDispatch() const { data: controlUnits } = useGetControlUnitsQuery() diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/slice.ts b/frontend/src/features/ControlUnit/components/ControlUnitTable/slice.ts similarity index 61% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/slice.ts rename to frontend/src/features/ControlUnit/components/ControlUnitTable/slice.ts index 8e3451665..356ba647b 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/slice.ts +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/slice.ts @@ -5,11 +5,11 @@ import storage from 'redux-persist/lib/storage' import type { FiltersState } from './types' -export type BackOfficeControlUnitListState = { +interface ControlUnitTableState { filtersState: FiltersState } -const INITIAL_STATE: BackOfficeControlUnitListState = { +const INITIAL_STATE: ControlUnitTableState = { filtersState: { administrationId: undefined, isArchived: false, @@ -18,13 +18,13 @@ const INITIAL_STATE: BackOfficeControlUnitListState = { } const persistConfig = { - key: 'backOfficeControlUnitList', + key: 'controlUnitTable', storage } -const backOfficeControlUnitList = createSlice({ +const controlUnitTableSlice = createSlice({ initialState: INITIAL_STATE, - name: 'backOfficeControlUnitList', + name: 'controlUnitTable', reducers: { setFilter( state, @@ -38,9 +38,6 @@ const backOfficeControlUnitList = createSlice({ } }) -export const backOfficeControlUnitListActions = backOfficeControlUnitList.actions +export const controlUnitTableActions = controlUnitTableSlice.actions -export const backOfficeControlUnitListPersistedReducer = persistReducer( - persistConfig, - backOfficeControlUnitList.reducer -) +export const controlUnitTablePersistedReducer = persistReducer(persistConfig, controlUnitTableSlice.reducer) diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/types.ts b/frontend/src/features/ControlUnit/components/ControlUnitTable/types.ts similarity index 100% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/types.ts rename to frontend/src/features/ControlUnit/components/ControlUnitTable/types.ts diff --git a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/utils.tsx b/frontend/src/features/ControlUnit/components/ControlUnitTable/utils.tsx similarity index 75% rename from frontend/src/features/ControlUnits/BackOfficeControlUnitList/utils.tsx rename to frontend/src/features/ControlUnit/components/ControlUnitTable/utils.tsx index 6925252b7..26f91e505 100644 --- a/frontend/src/features/ControlUnits/BackOfficeControlUnitList/utils.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitTable/utils.tsx @@ -1,22 +1,29 @@ import { CustomSearch, type Filter, Icon, IconButton, Size } from '@mtes-mct/monitor-ui' import { CONTROL_UNIT_TABLE_COLUMNS } from './constants' -import { openConfirmationModal } from '../../../domain/shared_slices/BackOffice' -import { BackOfficeConfirmationModalActionType } from '../../../domain/use_cases/backOffice/types' +import { backOfficeActions } from '../../../BackOffice/slice' +import { BackOfficeConfirmationModalActionType } from '../../../BackOffice/types' import type { FiltersState } from './types' -import type { ControlUnit } from '../../../domain/entities/controlUnit' -import type { AppDispatch } from '../../../store' +import type { ControlUnit } from '../../../../domain/entities/controlUnit' +import type { AppDispatch } from '../../../../store' import type { CellContext, ColumnDef } from '@tanstack/react-table' function archiveControlUnit(info: CellContext, dispatch: AppDispatch) { const controlUnit = info.getValue() dispatch( - openConfirmationModal({ - actionId: controlUnit.id, + backOfficeActions.openConfirmationModal({ actionType: BackOfficeConfirmationModalActionType.ARCHIVE_CONTROL_UNIT, - message: `Confirmez-vous l'archivage de l'unité "${controlUnit.name}" ? Elle n'apparaîtra plus dans MonitorEnv, elle ne sera plus utilisée que pour les statistiques.` + entityId: controlUnit.id, + modalProps: { + confirmationButtonLabel: 'Archiver', + message: [ + `Êtes-vous sûr de vouloir archiver l'unité "${controlUnit.name}" ?`, + `Elle n'apparaîtra plus dans MonitorEnv, elle ne sera plus utilisée que pour les statistiques.` + ].join(' '), + title: `Archivage de l'unité` + } }) ) } @@ -25,10 +32,17 @@ function deleteControlUnit(info: CellContext, const controlUnit = info.getValue() dispatch( - openConfirmationModal({ - actionId: controlUnit.id, + backOfficeActions.openConfirmationModal({ actionType: BackOfficeConfirmationModalActionType.DELETE_CONTROL_UNIT, - message: `Confirmez-vous la suppression de l'unité "${controlUnit.name}" ? Ceci entraînera la suppression de toutes ses informations (moyens, contacts...).` + entityId: controlUnit.id, + modalProps: { + confirmationButtonLabel: 'Supprimer', + message: [ + `Êtes-vous sûr de vouloir supprimer l'unité "${controlUnit.name}" ?`, + `Ceci entraînera la suppression de toutes ses informations (moyens, contacts...).` + ].join(' '), + title: `Suppression de l'unité` + } }) ) } diff --git a/frontend/src/domain/use_cases/controlUnit/archiveControlUnit.ts b/frontend/src/features/ControlUnit/usesCases/archiveControlUnit.ts similarity index 58% rename from frontend/src/domain/use_cases/controlUnit/archiveControlUnit.ts rename to frontend/src/features/ControlUnit/usesCases/archiveControlUnit.ts index 1af31a984..1b288ed88 100644 --- a/frontend/src/domain/use_cases/controlUnit/archiveControlUnit.ts +++ b/frontend/src/features/ControlUnit/usesCases/archiveControlUnit.ts @@ -1,20 +1,26 @@ import { logSoftError } from '@mtes-mct/monitor-ui' import { controlUnitsAPI } from '../../../api/controlUnitsAPI' +import { FrontendError } from '../../../libs/FrontendError' import type { AppThunk } from '../../../store' export const archiveControlUnit = (): AppThunk> => async (dispatch, getState) => { - const controlUnitId = getState().backOffice.confirmationModalActionId + const { confirmationModal } = getState().backOffice + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } try { - const { error } = await dispatch(controlUnitsAPI.endpoints.archiveControlUnit.initiate(controlUnitId) as any) + const { error } = await dispatch( + controlUnitsAPI.endpoints.archiveControlUnit.initiate(confirmationModal.entityId) as any + ) if (error) { throw error } } catch (err) { logSoftError({ - message: `An error happened while archiving a control unit (ID=${controlUnitId}").`, + message: `An error happened while archiving a control unit (ID=${confirmationModal.entityId}").`, originalError: err, userMessage: "Une erreur est survenue pendant l'archivage de l'unité de contrôle." }) diff --git a/frontend/src/features/ControlUnit/usesCases/deleteControlUnit.ts b/frontend/src/features/ControlUnit/usesCases/deleteControlUnit.ts new file mode 100644 index 000000000..3cf47d9e6 --- /dev/null +++ b/frontend/src/features/ControlUnit/usesCases/deleteControlUnit.ts @@ -0,0 +1,45 @@ +import { THEME, logSoftError } from '@mtes-mct/monitor-ui' + +import { controlUnitsAPI } from '../../../api/controlUnitsAPI' +import { FrontendError } from '../../../libs/FrontendError' +import { isUserError } from '../../../libs/UserError' +import { backOfficeActions } from '../../BackOffice/slice' + +import type { AppThunk } from '../../../store' + +export const deleteControlUnit = (): AppThunk> => async (dispatch, getState) => { + const { confirmationModal } = getState().backOffice + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + try { + const { error } = await dispatch( + controlUnitsAPI.endpoints.deleteControlUnit.initiate(confirmationModal.entityId) as any + ) + if (error) { + throw error + } + } catch (err) { + if (isUserError(err)) { + dispatch( + backOfficeActions.openDialog({ + dialogProps: { + color: THEME.color.maximumRed, + message: err.userMessage, + title: `Suppression impossible`, + titleBackgroundColor: THEME.color.maximumRed + } + }) + ) + + return + } + + logSoftError({ + message: `An error happened while deleting a control unit (ID=${confirmationModal.entityId}").`, + originalError: err, + userMessage: "Une erreur est survenue pendant la suppression de l'unité de contrôle." + }) + } +} diff --git a/frontend/src/features/ControlUnit/usesCases/deleteControlUnitContact.ts b/frontend/src/features/ControlUnit/usesCases/deleteControlUnitContact.ts new file mode 100644 index 000000000..2e49c4056 --- /dev/null +++ b/frontend/src/features/ControlUnit/usesCases/deleteControlUnitContact.ts @@ -0,0 +1,36 @@ +import { logSoftError } from '@mtes-mct/monitor-ui' + +import { controlUnitContactsAPI } from '../../../api/controlUnitContactsAPI' +import { FrontendError } from '../../../libs/FrontendError' +import { isUserError } from '../../../libs/UserError' +import { mainWindowActions } from '../../MainWindow/slice' + +import type { AppThunk } from '../../../store' + +export const deleteControlUnitContact = (): AppThunk> => async (dispatch, getState) => { + const { confirmationModal } = getState().mainWindow + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + try { + const { error } = await dispatch( + controlUnitContactsAPI.endpoints.deleteControlUnitContact.initiate(confirmationModal.entityId) as any + ) + if (error) { + throw error + } + } catch (err) { + if (isUserError(err)) { + dispatch(mainWindowActions.openDialog({ message: err.userMessage })) + + return + } + + logSoftError({ + message: `An error happened while deleting a control unit contact (ID=${confirmationModal.entityId}").`, + originalError: err, + userMessage: "Une erreur est survenue pendant la suppression de l'unité de contrôle." + }) + } +} diff --git a/frontend/src/features/ControlUnit/usesCases/deleteControlUnitResource.ts b/frontend/src/features/ControlUnit/usesCases/deleteControlUnitResource.ts new file mode 100644 index 000000000..815f41e6a --- /dev/null +++ b/frontend/src/features/ControlUnit/usesCases/deleteControlUnitResource.ts @@ -0,0 +1,28 @@ +import { logSoftError } from '@mtes-mct/monitor-ui' + +import { controlUnitResourcesAPI } from '../../../api/controlUnitResourcesAPI' +import { FrontendError } from '../../../libs/FrontendError' + +import type { AppThunk } from '../../../store' + +export const deleteControlUnitResource = (): AppThunk> => async (dispatch, getState) => { + const { confirmationModal } = getState().mainWindow + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + try { + const { error } = await dispatch( + controlUnitResourcesAPI.endpoints.deleteControlUnitResource.initiate(confirmationModal.entityId) as any + ) + if (error) { + throw error + } + } catch (err) { + logSoftError({ + message: `An error happened while deleting a control unit contact (ID=${confirmationModal.entityId}").`, + originalError: err, + userMessage: "Une erreur est survenue pendant la suppression de l'unité de contrôle." + }) + } +} diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/AreaNote.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/AreaNote.tsx deleted file mode 100644 index 45a50f6fd..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/AreaNote.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// import styled from 'styled-components' - -import { FormikTextarea } from '@mtes-mct/monitor-ui' - -import { Section } from './shared/Section' - -export function AreaNote() { - return ( - - Secteur d’intervention - - - - - ) -} diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Form.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Form.tsx deleted file mode 100644 index 9d869205e..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/Form.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Accent, Button, FormikTextInput, useKey } from '@mtes-mct/monitor-ui' -import { Formik } from 'formik' -import styled from 'styled-components' - -import { INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES } from './constants' - -import type { ControlUnitContactFormValues } from './types' - -export type FormProps = { - initialValues: ControlUnitContactFormValues | undefined - onCancel: () => void - onSubmit: (controlUnitContactFormValues: ControlUnitContactFormValues) => void -} -export function Form({ initialValues = INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES, onCancel, onSubmit }: FormProps) { - const key = useKey([initialValues]) - - return ( - - {({ handleSubmit }) => ( - - Ajouter un contact - - - - - - - Enregistrer - - Annuler - - - - )} - - ) -} - -const StyledForm = styled.form` - background-color: ${p => p.theme.color.gainsboro}; - margin-top: 16px; - padding: 16px; - - > div:not(:first-child) { - margin-top: 16px; - } - > div:last-child { - > .Element-Button:not(:first-child) { - margin-left: 8px; - } - } -` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/constants.ts b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/constants.ts deleted file mode 100644 index 5a27fe2e6..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { ControlUnitContactFormValues } from './types' - -export const INITIAL_CONTROL_UNIT_CONTACT_FORM_VALUES: ControlUnitContactFormValues = { - controlUnitId: undefined, - email: undefined, - name: undefined, - phone: undefined -} diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/index.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/index.tsx deleted file mode 100644 index 0cc1fb85a..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -// import styled from 'styled-components' - -import { Accent, Button, FormikTextarea } from '@mtes-mct/monitor-ui' -import { useCallback, useState } from 'react' -import styled from 'styled-components' - -import { Form } from './Form' -import { Item } from './Item' -import { Section } from '../shared/Section' - -import type { ControlUnitContactFormValues } from './types' -import type { ControlUnit } from '../../../../domain/entities/controlUnit' - -export type ControlUnitContactListProps = { - controlUnitContacts: ControlUnit.ControlUnitContactData[] -} -export function ControlUnitContactList({ controlUnitContacts }: ControlUnitContactListProps) { - const [editedControlUnitContactId, setEditedControlUnitContactId] = useState(undefined) - const [isNewControlUnitContactFormOpen, setIsNewControlUnitContactFormOpen] = useState(false) - - const editedControlUnitContact = controlUnitContacts.find(({ id }) => id === editedControlUnitContactId) - const isFormOpen = !!editedControlUnitContact || isNewControlUnitContactFormOpen - - const closeForm = useCallback(() => { - setEditedControlUnitContactId(undefined) - setIsNewControlUnitContactFormOpen(false) - }, []) - - const createOrUpdateControlUnitContact = useCallback((controlUnitContactFormValues: ControlUnitContactFormValues) => { - // eslint-disable-next-line no-console - console.log(controlUnitContactFormValues) - - setEditedControlUnitContactId(undefined) - setIsNewControlUnitContactFormOpen(false) - }, []) - - const openForm = useCallback(() => { - setIsNewControlUnitContactFormOpen(true) - }, []) - - return ( - - Contacts - - - - {controlUnitContacts.map(controlUnitContact => ( - - ))} - - {isFormOpen ? ( - - ) : ( - - - Ajouter un contact - - - )} - - - ) -} - -const StyledSectionBody = styled(Section.Body)` - > .Field-Textarea { - margin-bottom: 16px; - } - > div:last-child { - margin-top: 16px; - } -` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/types.ts b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/types.ts deleted file mode 100644 index ec75652bb..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitContactList/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { ControlUnit } from '../../../../domain/entities/controlUnit' -import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' - -export type ControlUnitContactFormValues = UndefineExceptArrays diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Form.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Form.tsx deleted file mode 100644 index 3e85a7875..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Form.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { - Accent, - Button, - FormikSelect, - FormikTextInput, - FormikTextarea, - getOptionsFromIdAndName, - getOptionsFromLabelledEnum, - useKey -} from '@mtes-mct/monitor-ui' -import { Formik } from 'formik' -import styled from 'styled-components' - -import { INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES } from './constants' -import { useGetBasesQuery } from '../../../../api/basesAPI' -import { ControlUnit } from '../../../../domain/entities/controlUnit' - -import type { ControlUnitResourceFormValues } from './types' - -export type FormProps = { - initialValues: ControlUnitResourceFormValues | undefined - onCancel: () => void - onSubmit: (controlUnitResourceFormValues: ControlUnitResourceFormValues) => void -} -export function Form({ initialValues = INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES, onCancel, onSubmit }: FormProps) { - const key = useKey([initialValues]) - - const { data: bases } = useGetBasesQuery() - - const basesAsOptions = getOptionsFromIdAndName(bases) - const controlUnitResourceTypesAsOptions = getOptionsFromLabelledEnum(ControlUnit.ControlUnitResourceType) - - if (!basesAsOptions) { - return Chargement en cours... - } - - return ( - - {({ handleSubmit }) => ( - - Ajouter un moyen - - - - - - - - Enregistrer - - Annuler - - - - )} - - ) -} - -const StyledForm = styled.form` - background-color: ${p => p.theme.color.gainsboro}; - margin-top: 16px; - padding: 16px; - - > div:not(:first-child) { - margin-top: 16px; - } - > div:last-child { - > .Element-Button:not(:first-child) { - margin-left: 8px; - } - } -` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Item.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Item.tsx deleted file mode 100644 index 1c6c180db..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/Item.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Accent, FormikTextarea, Icon, IconButton } from '@mtes-mct/monitor-ui' -import styled from 'styled-components' - -import type { ControlUnit } from '../../../../domain/entities/controlUnit' -import type { Promisable } from 'type-fest' - -export type ItemProps = { - controlUnitResource: ControlUnit.ControlUnitResource - onEdit: (controlUnitResourceId: number) => Promisable -} -export function Item({ controlUnitResource, onEdit }: ItemProps) { - return ( - - - - - - {controlUnitResource.name} - {controlUnitResource.base.name} - - - onEdit(controlUnitResource.id)} /> - - - - - - - - ) -} - -const Wrapper = styled.div` - background-color: ${p => p.theme.color.cultured}; - display: flex; -` - -const Right = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; - padding: 8px 16px; -` - -const RightHeader = styled.div` - display: flex; - margin-bottom: 8px; - color: ${p => p.theme.color.gunMetal}; - - > div:first-child { - display: flex; - flex-direction: column; - flex-grow: 1; - } -` - -const Name = styled.span` - font-weight: bold; -` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/constants.ts b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/constants.ts deleted file mode 100644 index 2c8f41367..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ControlUnitResourceFormValues } from './types' - -export const INITIAL_CONTROL_UNIT_RESOURCE_FORM_VALUES: ControlUnitResourceFormValues = { - baseId: undefined, - controlUnitId: undefined, - name: undefined, - note: undefined, - photo: undefined, - type: undefined -} diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/index.tsx b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/index.tsx deleted file mode 100644 index a49c80cab..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -// import styled from 'styled-components' - -import { Accent, Button, Icon } from '@mtes-mct/monitor-ui' -import { useCallback, useState } from 'react' -import styled from 'styled-components' - -import { Form } from './Form' -import { Item } from './Item' -import { Section } from '../shared/Section' - -import type { ControlUnitResourceFormValues } from './types' -import type { ControlUnit } from '../../../../domain/entities/controlUnit' - -export type ControlUnitResourceListProps = { - controlUnitResources: ControlUnit.ControlUnitResource[] -} -export function ControlUnitResourceList({ controlUnitResources }: ControlUnitResourceListProps) { - const [editedControlUnitResourceId, setEditedControlUnitResourceId] = useState(undefined) - const [isNewControlUnitResourceFormOpen, setIsNewControlUnitResourceFormOpen] = useState(false) - - const editedControlUnitResource = controlUnitResources.find(({ id }) => id === editedControlUnitResourceId) - const isFormOpen = !!editedControlUnitResource || isNewControlUnitResourceFormOpen - - const closeForm = useCallback(() => { - setEditedControlUnitResourceId(undefined) - setIsNewControlUnitResourceFormOpen(false) - }, []) - - const createOrUpdateControlUnitResource = useCallback( - (controlUnitResourceFormValues: ControlUnitResourceFormValues) => { - // eslint-disable-next-line no-console - console.log(controlUnitResourceFormValues) - - setEditedControlUnitResourceId(undefined) - setIsNewControlUnitResourceFormOpen(false) - }, - [] - ) - - const openForm = useCallback(() => { - setIsNewControlUnitResourceFormOpen(true) - }, []) - - return ( - - Moyens - - {controlUnitResources.map(controlUnitResource => ( - - ))} - - {isFormOpen ? ( - - ) : ( - - - Ajouter un moyen - - - )} - - - ) -} - -const StyledSectionBody = styled(Section.Body)` - > div:not(:first-child) { - margin-top: 8px; - } - > div:last-child { - margin-top: 16px; - } -` diff --git a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/types.ts b/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/types.ts deleted file mode 100644 index ae3e45415..000000000 --- a/frontend/src/features/ControlUnits/MapControlUnitDialog/ControlUnitResourceList/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { ControlUnit } from '../../../../domain/entities/controlUnit' -import type { UndefineExceptArrays } from '@mtes-mct/monitor-ui' - -export type ControlUnitResourceFormValues = UndefineExceptArrays diff --git a/frontend/src/features/MainWindow/components/MainWindowConfirmationModal.tsx b/frontend/src/features/MainWindow/components/MainWindowConfirmationModal.tsx new file mode 100644 index 000000000..10dfe0375 --- /dev/null +++ b/frontend/src/features/MainWindow/components/MainWindowConfirmationModal.tsx @@ -0,0 +1,27 @@ +import { useCallback } from 'react' + +import { ConfirmationModal } from '../../../components/ConfirmationModal' +import { useAppDispatch } from '../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../hooks/useAppSelector' +import { FrontendError } from '../../../libs/FrontendError' +import { mainWindowActions } from '../slice' +import { handleModalConfirmation } from '../useCases/handleModalConfirmation' + +export function MainWindowConfirmationModal() { + const dispatch = useAppDispatch() + const { confirmationModal } = useAppSelector(store => store.mainWindow) + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + const close = useCallback(() => { + dispatch(mainWindowActions.closeConfirmationModal()) + }, [dispatch]) + + const confirm = useCallback(() => { + dispatch(handleModalConfirmation()) + }, [dispatch]) + + // eslint-disable-next-line react/jsx-props-no-spreading + return +} diff --git a/frontend/src/features/MainWindow/components/MainWindowDialog.tsx b/frontend/src/features/MainWindow/components/MainWindowDialog.tsx new file mode 100644 index 000000000..262027fab --- /dev/null +++ b/frontend/src/features/MainWindow/components/MainWindowDialog.tsx @@ -0,0 +1,30 @@ +import { Button, Dialog } from '@mtes-mct/monitor-ui' +import { useCallback } from 'react' + +import { useAppDispatch } from '../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../hooks/useAppSelector' +import { FrontendError } from '../../../libs/FrontendError' +import { mainWindowActions } from '../slice' + +export function MainWindowDialog() { + const dispatch = useAppDispatch() + const { dialog } = useAppSelector(store => store.mainWindow) + if (!dialog) { + throw new FrontendError('`dialog` is undefined.') + } + + const close = useCallback(() => { + dispatch(mainWindowActions.closeDialog()) + }, [dispatch]) + + return ( + + + {dialog.message} + + + Fermer + + + ) +} diff --git a/frontend/src/features/MapRightMenu/ControlUnitListButton.tsx b/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx similarity index 60% rename from frontend/src/features/MapRightMenu/ControlUnitListButton.tsx rename to frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx index 714218fb7..dfff856ed 100644 --- a/frontend/src/features/MapRightMenu/ControlUnitListButton.tsx +++ b/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx @@ -2,11 +2,11 @@ import { Icon, Size } from '@mtes-mct/monitor-ui' import { useCallback } from 'react' import styled from 'styled-components' -import { globalActions } from '../../domain/shared_slices/Global' -import { useAppDispatch } from '../../hooks/useAppDispatch' -import { useAppSelector } from '../../hooks/useAppSelector' -import { MenuWithCloseButton } from '../commonStyles/map/MenuWithCloseButton' -import { MapControlUnitListDialog } from '../ControlUnits/MapControlUnitListDialog' +import { globalActions } from '../../../../domain/shared_slices/Global' +import { useAppDispatch } from '../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../hooks/useAppSelector' +import { MenuWithCloseButton } from '../../../commonStyles/map/MenuWithCloseButton' +import { ControlUnitListDialog } from '../../../ControlUnit/components/ControlUnitListDialog' export function ControlUnitListButton() { const dispatch = useAppDispatch() @@ -19,7 +19,9 @@ export function ControlUnitListButton() { return ( - {isControlUnitListDialogVisible && } + {/* TODO The right menu should be a full `MainWindow` feature component by itself. */} + {/* We should positition related dialogs independantly, not include them here. */} + {isControlUnitListDialogVisible && } ) { + state.confirmationModal = action.payload + state.isConfirmationModalOpen = true + }, + + openDialog(state, action: PayloadAction) { + state.dialog = action.payload + state.isDialogOpen = true + } + } +}) + +export const mainWindowActions = mainWindowSlice.actions +export const mainWindowReducer = mainWindowSlice.reducer diff --git a/frontend/src/features/MainWindow/types.ts b/frontend/src/features/MainWindow/types.ts new file mode 100644 index 000000000..b4e38bcad --- /dev/null +++ b/frontend/src/features/MainWindow/types.ts @@ -0,0 +1,17 @@ +import type { ConfirmationModalProps } from '../../components/ConfirmationModal' + +export type ConfirmationModalState = { + actionType: MainWindowConfirmationModalActionType + /** ID of the targeted entity. */ + entityId: number + modalProps: Omit +} + +export type DialogState = { + message: string +} + +export enum MainWindowConfirmationModalActionType { + 'DELETE_CONTROL_UNIT_CONTACT' = 'DELETE_CONTROL_UNIT_CONTACT', + 'DELETE_CONTROL_UNIT_RESOURCE' = 'DELETE_CONTROL_UNIT_RESOURCE' +} diff --git a/frontend/src/features/MainWindow/useCases/handleModalConfirmation.ts b/frontend/src/features/MainWindow/useCases/handleModalConfirmation.ts new file mode 100644 index 000000000..bca222237 --- /dev/null +++ b/frontend/src/features/MainWindow/useCases/handleModalConfirmation.ts @@ -0,0 +1,29 @@ +import { FrontendError } from '../../../libs/FrontendError' +import { deleteControlUnitContact } from '../../ControlUnit/usesCases/deleteControlUnitContact' +import { deleteControlUnitResource } from '../../ControlUnit/usesCases/deleteControlUnitResource' +import { mainWindowActions } from '../slice' +import { MainWindowConfirmationModalActionType } from '../types' + +import type { AppThunk } from '../../../store' + +export const handleModalConfirmation = (): AppThunk => async (dispatch, getState) => { + const { confirmationModal } = getState().mainWindow + if (!confirmationModal) { + throw new FrontendError('`confirmationModal` is undefined.') + } + + switch (confirmationModal?.actionType) { + case MainWindowConfirmationModalActionType.DELETE_CONTROL_UNIT_CONTACT: + await dispatch(deleteControlUnitContact()) + break + + case MainWindowConfirmationModalActionType.DELETE_CONTROL_UNIT_RESOURCE: + await dispatch(deleteControlUnitResource()) + break + + default: + break + } + + dispatch(mainWindowActions.closeConfirmationModal()) +} diff --git a/frontend/src/pages/BackOfficePage.tsx b/frontend/src/pages/BackOfficePage.tsx index f972ea724..7fc4026e7 100644 --- a/frontend/src/pages/BackOfficePage.tsx +++ b/frontend/src/pages/BackOfficePage.tsx @@ -2,16 +2,16 @@ import { Notifier } from '@mtes-mct/monitor-ui' import { Route, Routes } from 'react-router' import styled from 'styled-components' -import { BackOfficeAdministrationForm } from '../features/Administrations/BackOfficeAdministrationForm' -import { BackOfficeAdministrationList } from '../features/Administrations/BackOfficeAdministrationList' -import { ConfirmationModal } from '../features/BackOffice/components/ConfirmationModal' -import { Dialog } from '../features/BackOffice/components/Dialog' +import { AdministrationForm } from '../features/Administration/components/AdministrationForm' +import { AdministrationTable } from '../features/Administration/components/AdministrationTable' +import { BackOfficeConfirmationModal } from '../features/BackOffice/components/BackOfficeConfirmationModal' +import { BackOfficeDialog } from '../features/BackOffice/components/BackOfficeDialog' import { BackOfficeMenu } from '../features/BackOfficeMenu' import { BACK_OFFICE_MENU_PATH, BackOfficeMenuKey } from '../features/BackOfficeMenu/constants' -import { BackOfficeBaseForm } from '../features/Bases/BackOfficeBaseForm' -import { BackOfficeBaseList } from '../features/Bases/BackOfficeBaseList' -import { BackOfficeControlUnitForm } from '../features/ControlUnits/BackOfficeControlUnitForm' -import { BackOfficeControlUnitList } from '../features/ControlUnits/BackOfficeControlUnitList' +import { BaseForm } from '../features/Base/components/BaseForm' +import { BaseTable } from '../features/Base/components/BaseTable' +import { ControlUnitForm } from '../features/ControlUnit/components/ControlUnitForm' +import { ControlUnitTable } from '../features/ControlUnit/components/ControlUnitTable' import { useAppSelector } from '../hooks/useAppSelector' export function BackOfficePage() { @@ -23,36 +23,30 @@ export function BackOfficePage() { - } path="/" /> + } path="/" /> - } path={BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.BASE_LIST]} /> - } - path={`${BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.BASE_LIST]}/:baseId`} - /> + } path={BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.BASE_LIST]} /> + } path={`${BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.BASE_LIST]}/:baseId`} /> } + element={} path={BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.ADMINISTRATION_LIST]} /> } + element={} path={`${BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.ADMINISTRATION_LIST]}/:administrationId`} /> + } path={BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.CONTROL_UNIT_LIST]} /> } - path={BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.CONTROL_UNIT_LIST]} - /> - } + element={} path={`${BACK_OFFICE_MENU_PATH[BackOfficeMenuKey.CONTROL_UNIT_LIST]}/:controlUnitId`} /> - {backOffice.isConfirmationModalOpen && } - {backOffice.isDialogOpen && } + {backOffice.isConfirmationModalOpen && } + {backOffice.isDialogOpen && } diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 02f01fedc..76fb84ac3 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -5,16 +5,18 @@ import styled from 'styled-components' import { APIWorker } from '../api/APIWorker' import { ReportingContext } from '../domain/shared_slices/Global' -import { MapControlUnitDialog } from '../features/ControlUnits/MapControlUnitDialog' +import { ControlUnitDialog } from '../features/ControlUnit/components/ControlUnitDialog' import Healthcheck from '../features/healthcheck/Healthcheck' import { LayersSidebar } from '../features/layersSelector' import { LocateOnMap } from '../features/LocateOnMap' +import { MainWindowConfirmationModal } from '../features/MainWindow/components/MainWindowConfirmationModal' +import { MainWindowDialog } from '../features/MainWindow/components/MainWindowDialog' +import { ControlUnitListButton } from '../features/MainWindow/components/RightMenu/ControlUnitListButton' import { Map } from '../features/map' import { DrawModal } from '../features/map/draw/DrawModal' import { RightMenuOnHoverArea } from '../features/map/shared/RightMenuOnHoverArea' import { InterestPointMapButton } from '../features/map/tools/interestPoint/InterestPointMapButton' import { MeasurementMapButton } from '../features/map/tools/measurements/MeasurementMapButton' -import { ControlUnitListButton } from '../features/MapRightMenu/ControlUnitListButton' import { MissionsMenu } from '../features/missions/MissionsButton' import { Reportings } from '../features/Reportings' import { ReportingsButton } from '../features/Reportings/ReportingsButton' @@ -34,6 +36,7 @@ export function HomePage() { displaySearchSemaphoreButton, isControlUnitDialogVisible } = useAppSelector(state => state.global) + const { isConfirmationModalOpen, isDialogOpen } = useAppSelector(state => state.mainWindow) const { missionState: { isFormDirty, missionState }, multiMissions: { selectedMissions } @@ -69,7 +72,7 @@ export function HomePage() { {displayDrawModal && } {displayLocateOnMap && } - {isControlUnitDialogVisible && } + {isControlUnitDialogVisible && } {displayMissionMenuButton && } {displayReportingsButton && } @@ -83,6 +86,9 @@ export function HomePage() { + {isConfirmationModalOpen && } + {isDialogOpen && } + > diff --git a/frontend/src/store.ts b/frontend/src/store/index.ts similarity index 88% rename from frontend/src/store.ts rename to frontend/src/store/index.ts index 31bea58ec..910ac49ac 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store/index.ts @@ -2,9 +2,9 @@ import { type AnyAction, type ThunkAction, configureStore, isPlain } from '@redu import { setupListeners } from '@reduxjs/toolkit/query' import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from 'redux-persist' -import { monitorenvPrivateApi, monitorenvPublicApi } from './api/api' -import { homeReducers, homeMiddlewares } from './domain/shared_slices' -import { regulatoryActionSanitizer } from './domain/shared_slices/Regulatory' +import { monitorenvPrivateApi, monitorenvPublicApi } from '../api/api' +import { homeReducers, homeMiddlewares } from '../domain/shared_slices' +import { regulatoryActionSanitizer } from '../domain/shared_slices/Regulatory' const homeStore = configureStore({ devTools: { diff --git a/frontend/src/utils/isBrowserSupported.ts b/frontend/src/utils/isBrowserSupported.ts index f614a2326..41ff83859 100644 --- a/frontend/src/utils/isBrowserSupported.ts +++ b/frontend/src/utils/isBrowserSupported.ts @@ -1,12 +1,13 @@ import { browserName, browserVersion } from 'react-device-detect' import { toast } from 'react-toastify' +// TODO Share that in MUI. export function isBrowserSupported(): boolean { const browserVersionAsNumber = Number(browserVersion) switch (browserName) { - case 'Internet Explorer': - return false + case 'Brave': + return browserVersionAsNumber >= 112 case 'Edge': return browserVersionAsNumber >= 79 @@ -20,12 +21,15 @@ export function isBrowserSupported(): boolean { case 'Firefox': return browserVersionAsNumber >= 62 - case 'Safari': - return browserVersionAsNumber >= 12 + case 'Internet Explorer': + return false case 'Opera': return browserVersionAsNumber >= 56 + case 'Safari': + return browserVersionAsNumber >= 12 + default: toast.error(`Navigateur inconnu: "${browserName} v${browserVersion}"`) diff --git a/frontend/src/utils/isEmptyish.ts b/frontend/src/utils/isEmptyish.ts new file mode 100644 index 000000000..2f8f31524 --- /dev/null +++ b/frontend/src/utils/isEmptyish.ts @@ -0,0 +1,9 @@ +import { isEmpty } from 'lodash/fp' + +export function isEmptyish(value: any) { + if (typeof value === 'string') { + return !value.trim().length + } + + return isEmpty(value) +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c01a5f041..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "monitorenv", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}
{backOffice.confirmationModalMessage}
{backOffice.dialogMessage}
- {controlUnitContact.name} {controlUnitContact.phone} + {ControlUnit.ControlUnitContactName[controlUnitContact.name] || controlUnitContact.name} + {controlUnitContact.phone}
@@ -22,7 +29,7 @@ export function Item({ controlUnitContact, onEdit }: ItemProps) {
{controlUnitResource.base.name}
{controlUnitResource.note}
Ajouter un contact
Ajouter un moyen
{dialog.message}