diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 39aa13031e..0000000000 --- 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 1cfd243d91..36e3e7cdc9 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 1ef86532e8..e2c3f149d4 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 ef27f29b4c..8492bea504 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 5ce323622d..4f72c37f7e 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 5ef02588af..608f150c8e 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 f5c71db5b4..cb43f2c285 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 0fcaae7de0..a91bad3c82 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 30c73d5ba4..bcccd9148d 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 a4b24bc325..03d8f3e59f 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 1eab9ce4ac..285f7b9529 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 0000000000..29f39ab499 --- /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 0000000000..02d5ea929d --- /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 c41b2cfb57..bef235ec45 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 c0817e2b03..c9408942fa 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 c6c44ff5c5..62d9b8d048 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 688ecc2a30..1dcda35636 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 3423112a63..4e0982c019 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 9cc263b4f8..efee067a25 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 3d7de0e97e..041c21633b 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 25f98fe933..b5e93acaee 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 f162f00156..bf377951cf 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 348d13a4f9..c775812294 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 5a53461776..5cb329fdf2 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 0c3c389310..a6b094baa3 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 1797289cbc..fecdf5e808 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 a620278b05..7a78b7f2e1 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 041edac386..14777e0c50 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 156f34ae4b..a91c8441c2 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 8f48b5c310..1d67732cd6 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 ab777bd317..b5589228df 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 d3b49b66e2..b8aba5bb45 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 d29024463b..ead4d423c5 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 140cff479e..375e74a838 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 73b6f8eff2..da14d617e1 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 277187fe1b..f28df18093 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 e70e3fea88..123eee872a 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 b61a04f0e8..87f0df6c9f 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 84a2641ed5..5c6ade6c3d 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 54278e2d1f..9c593106be 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 873f7a0c83..c8a7c42406 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 0c81bc1037..77ffdb0148 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 e4198c12a2..f8160bb287 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 4dc52b78ea..fac967aa43 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 490e77dc9b..b12798a064 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 5307be20b0..c881a8b680 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 86b3f1db71..95bb6cb4ba 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 f3f3ffb533..7f804f5223 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 f74e6164eb..e60afcadc0 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 d260aa652a..70f920f1d8 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 d0b011f60e..93e8adf05c 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 e25618a04b..9590692088 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 1a6e74ea8f..55f99fcc9c 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 893f2133f0..c9a475a06f 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 3d92621aa5..e1d47f3a91 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 6616d31ece..cb8acb39fd 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 187579ef3f..5817bb8319 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 2cf048caea..5248c98435 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 8748d9d906..33fa40220d 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 d36e2b92be..381ece2406 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 0b07d0c29a..499416dbcc 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 d6fc9bbb96..730b4a40bf 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 fe707b2b15..a7edb9fda0 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 899dc4ce85..01943df8a2 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 e02bb05592..d5065d2438 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 d3348c1944..781bae27db 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 bba1a98b52..9a6cfe12d7 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 ea80c8eb52..0b50ac7df1 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 82c7f85e8c..185a2d95cb 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 12c342ec8c..64dc02f593 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 65c3539918..24cfb7ebc2 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 4f87eb0fc3..22cd07ae3b 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 03320581f8..9389126e50 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 f3437417b4..5255cc12a4 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 f0338c85dd..83fbe6624b 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 f22ecd59da..582752578f 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 8f8bed997f..3e695193e3 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 cd0da42f65..eba48f2446 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 91b5af1568..480115e65e 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 5427e3e21a..505cdf92a4 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 22648d3cde..ae06ede2e9 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 f3579f1442..b0608f40c4 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 b34ee7c52b..3c4890c316 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 0000000000..5495e9c86d --- /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 0000000000..b49f650ce5 --- /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 0000000000..04f24edd99 --- /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 0000000000..57ba47355d --- /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 0000000000..a7b8d2b997 --- /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 0000000000..78e9173f27 --- /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 23517f66ed..00b2888def 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 1cf6bb76fa..767531a42a 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 0000000000..d2b89aa3f8 --- /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 0000000000..c7188e5e11 --- /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 969c82775a..0000000000 --- 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 3ffc8d31d0..ddab01d44d 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 5bc4c4b836..d3d960fb12 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 7736badf6e..0000000000 --- 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 13a2e6eedd..0000000000 --- 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 2e75bad86e..0000000000 --- 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 19c846da02..76100c36d4 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 efc5f40149..32ede500db 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 82195d762b..9c30d25a18 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 79c2ac7143..2fe6e05cdb 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 9a8d146e9f..2c77a406d3 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 7dfbd9cd68..53f73520ce 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 11dd147418..58b951e48d 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 734045163d..532027f9ca 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 fac67768e0..09c1b43d64 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 4ad571a0fa..f5ca630470 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 9198c015bc..701f2846cf 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 0000000000..c963653639 --- /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 0000000000..851f03fb05 --- /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 5acbbf14e0..0661cc81fd 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 6d501def75..0000000000 --- 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 6a6eb32f1a..0000000000 --- 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 0000000000..941f9185af --- /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 0000000000..27e62bf87e --- /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 0000000000..bc5df13d3d --- /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 7e76e8c599..02f7586dce 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 75c3086c37..8b8ebb239e 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 4367f4fa78..2c9314b2c1 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 e5cbe189e8..cc6679cd20 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 d58a1b3f5b..ae7cc862f0 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 2c23943f0b..c8420fed19 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 5bb7d556f4..a858b25356 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 0f2dac9d15..43ff6533de 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 0000000000..41bdab2475 --- /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 0000000000..a5f7cd0874 --- /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 0000000000..fc710171ea --- /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 5097aed43f..a37ac5a490 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 0000000000..d0ab7733c5 --- /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 0000000000..6c01a1d65f --- /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 0000000000..849a0ddf30 --- /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 0000000000..6c18889c5c --- /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 0000000000..ee04203c92 --- /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 0000000000..76ed1b3c76 --- /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 0000000000..1f07026ca6 --- /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 0000000000..360f860e8b --- /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 090075b360..7c34070b07 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 c21c54bca9..d6dda96f5a 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 0000000000..d775730289 --- /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 3c723a91d6..459d80521c 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 08d1025cfb..4741e46169 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 45edd96b66..7d0ef5118e 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 69569aa26d..f541c6bc4f 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 f3054d3b39..53f4250aa4 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 1390aa1b9f..bc00da3628 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 8f91751fad..899f32c725 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 21012ffed3..912bef7115 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 238aa21535..69c4f07834 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 7f9eb1addc..2b61607cbf 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 1846e43c06..fe28cad23c 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 1178361541..0eb7a38fbf 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 6250ec19f6..13128cbde1 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 8e3451665d..356ba647b4 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 6925252b7f..26f91e5053 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 1af31a984a..1b288ed88f 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 0000000000..3cf47d9e69 --- /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 0000000000..2e49c40565 --- /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 0000000000..815f41e6ac --- /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 45a50f6fd5..0000000000 --- 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 9d869205ee..0000000000 --- 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 5a27fe2e62..0000000000 --- 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 0cc1fb85a3..0000000000 --- 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 ec75652bb7..0000000000 --- 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 3e85a7875a..0000000000 --- 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 1c6c180db4..0000000000 --- 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 2c8f41367d..0000000000 --- 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 a49c80cab4..0000000000 --- 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 ae3e454158..0000000000 --- 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 0000000000..10dfe03756 --- /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 0000000000..262027fab8 --- /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 714218fb78..dfff856ed9 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 0000000000..b4e38bcadd --- /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 0000000000..bca2222374 --- /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 f972ea7243..7fc4026e7d 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 02f01fedc2..76fb84ac30 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 31bea58ecf..910ac49acf 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 f614a23260..41ff838594 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 0000000000..2f8f315244 --- /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 c01a5f0417..0000000000 --- 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}