Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tech] Mise à jour des propriétés front envoyées au back - Missions, Signalements, Backoffice #1473

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.gouv.cacem.monitorenv.config

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.SerializationFeature
Expand All @@ -22,7 +23,7 @@ class MapperConfiguration {
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
mapper.propertyNamingStrategy = PropertyNamingStrategies.LOWER_CAMEL_CASE
// TODO(15/06/2024): Activate this property when all front end API object are iso backend
// mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: Il faut laisser que les autres équipes vérifient avec cette conf avant de merger.


return mapper
}
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/api/missionsAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,18 @@ export const missionsAPI = monitorenvPrivateApi.injectEndpoints({
.join('&')
}),
updateMission: builder.mutation<Mission, MissionData>({
invalidatesTags: (_, __, { attachedReportingIds = [], detachedReportingIds = [], id }) => [
{ id, type: 'Missions' },
invalidatesTags: missionResponse => [
{ id: missionResponse?.id, type: 'Missions' },
{ id: 'LIST', type: 'Missions' },
{ id: 'LIST', type: 'Reportings' },
...attachedReportingIds.map(reportingId => ({ id: reportingId, type: 'Reportings' as const })),
...detachedReportingIds.map(reportingId => ({ id: reportingId, type: 'Reportings' as const }))
...(missionResponse?.attachedReportingIds ?? []).map(reportingId => ({
id: reportingId,
type: 'Reportings' as const
})),
...(missionResponse?.detachedReportingIds ?? []).map(reportingId => ({
id: reportingId,
type: 'Reportings' as const
}))
],
query: ({ id, ...patch }) => ({
body: { id, ...patch },
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/api/reportingsAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type EntityState, createEntityAdapter } from '@reduxjs/toolkit'
import { monitorenvPrivateApi } from './api'
import { getQueryString } from '../utils/getQueryStringFormatted'

import type { Reporting, ReportingDetailed } from '../domain/entities/reporting'
import type { Reporting } from '../domain/entities/reporting'

type ReportingsFilter = {
isAttachedToMission?: boolean | undefined
Expand All @@ -17,7 +17,7 @@ type ReportingsFilter = {
targetTypes?: string[]
}

const ReportingAdapter = createEntityAdapter<ReportingDetailed>()
const ReportingAdapter = createEntityAdapter<Reporting>()
const initialState = ReportingAdapter.getInitialState()

export const reportingsAPI = monitorenvPrivateApi.injectEndpoints({
Expand Down Expand Up @@ -67,13 +67,13 @@ export const reportingsAPI = monitorenvPrivateApi.injectEndpoints({
providesTags: (_, __, id) => [{ id, type: 'Reportings' }],
query: id => `/v1/reportings/${id}`
}),
getReportings: build.query<EntityState<ReportingDetailed>, ReportingsFilter | void>({
getReportings: build.query<EntityState<Reporting>, ReportingsFilter | void>({
providesTags: result =>
result?.ids
? [{ id: 'LIST', type: 'Reportings' }, ...result.ids.map(id => ({ id, type: 'Reportings' as const }))]
: [{ id: 'LIST', type: 'Reportings' }],
query: filters => getQueryString('/v1/reportings', filters),
transformResponse: (response: ReportingDetailed[]) => ReportingAdapter.setAll(initialState, response)
transformResponse: (response: Reporting[]) => ReportingAdapter.setAll(initialState, response)
}),
updateReporting: build.mutation<Reporting, Partial<Reporting>>({
invalidatesTags: (_, __, { id, missionId }) => [
Expand Down
24 changes: 21 additions & 3 deletions frontend/src/domain/entities/missions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { THEME, customDayjs } from '@mtes-mct/monitor-ui'

import type { ControlPlansData } from './controlPlan'
import type { LegacyControlUnit } from './legacyControlUnit'
import type { DetachedReportingForTimeline, ReportingDetailed, ReportingForTimeline } from './reporting'
import type { DetachedReportingForTimeline, Reporting, ReportingForTimeline } from './reporting'
import type { SeaFrontEnum } from './seaFrontType'
import type { VesselTypeEnum } from './vesselType'
import type { FishMissionAction } from '@features/missions/fishActions.types'
Expand Down Expand Up @@ -215,7 +215,7 @@ export enum ActionSource {
// Mission from API
export type Mission<EnvAction = EnvActionControl | EnvActionSurveillance | EnvActionNote> = {
attachedReportingIds: number[]
attachedReportings: ReportingDetailed[]
attachedReportings: Reporting[]
completedBy: string
controlUnits: LegacyControlUnit[]
createdAtUtc?: string | undefined
Expand Down Expand Up @@ -245,7 +245,25 @@ export type NewMission = Omit<Mission<NewEnvAction>, 'controlUnits' | 'facade' |
id: string
}
// Mission for API
export type MissionData = Omit<Partial<Mission<EnvAction>>, 'attachedReportings'>
export type MissionData = {
attachedReportingIds?: number[] | undefined
completedBy?: string | undefined
controlUnits: LegacyControlUnit[] | Array<Omit<LegacyControlUnit, 'administrationId' | 'id'>>
endDateTimeUtc?: string | undefined
envActions: EnvAction[] | NewEnvAction[]
facade: SeaFrontEnum
geom?: GeoJSON.MultiPolygon
hasMissionOrder?: boolean
id: number | undefined
isGeometryComputedFromControls: boolean
isUnderJdp?: boolean | undefined
missionSource: MissionSourceEnum
missionTypes: MissionTypeEnum[]
observationsCacem?: string
observationsCnsp?: string
openBy: string
startDateTimeUtc: string
}

export type EnvAction = EnvActionControl | EnvActionSurveillance | EnvActionNote
export type NewEnvAction = NewEnvActionControl | EnvActionSurveillance | EnvActionNote
Expand Down
45 changes: 37 additions & 8 deletions frontend/src/domain/entities/reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,58 @@ import { customDayjs as dayjs } from '@mtes-mct/monitor-ui'

import type { ActionSource, ActionTypeEnum, Mission } from './missions'
import type { ReportingTargetTypeEnum } from './targetType'
import type { VehicleTypeEnum } from './vehicleType'
import type { VesselTypeEnum } from './vesselType'

export type Reporting = {
actionTaken: string | undefined
attachedEnvActionId: string
attachedEnvActionId?: string
attachedMission: Mission | undefined
attachedToMissionAtUtc: string | undefined
controlStatus: ControlStatusEnum
controlStatus: ControlStatusEnum | undefined
controlUnitId: number | undefined
createdAt: string | undefined
description: string | undefined
detachedFromMissionAtUtc: string | undefined
displayedSource?: string
geom: Record<string, any>[] | undefined
hasNoUnitAvailable: boolean | undefined
id: number | string
isArchived: boolean
isControlRequired: boolean | undefined
isInfractionProven: boolean
missionId: number | undefined
openBy: string | undefined
reportType?: ReportingTypeEnum
reportingId: number | undefined
semaphoreId: number | undefined
sourceName: string | undefined
sourceType: ReportingSourceEnum
subThemeIds: number[] | undefined
targetDetails?: TargetDetails[]
targetType: ReportingTargetTypeEnum | undefined
themeId: number | undefined
updatedAtUtc: string | undefined
validityTime: number
vehicleType: VehicleTypeEnum | undefined
withVHFAnswer: boolean | undefined
}

export type ReportingData = {
actionTaken: string | undefined
attachedEnvActionId: string | undefined
attachedToMissionAtUtc: string | undefined
controlUnitId: number | undefined
createdAt: string | undefined
description: string | undefined
detachedFromMissionAtUtc: string | undefined
geom: Record<string, any>[] | undefined
hasNoUnitAvailable: boolean | undefined
id: number | undefined
isArchived: boolean
isControlRequired: boolean | undefined
isInfractionProven: boolean
missionId: number | undefined
openBy: string
reportType: ReportingTypeEnum
reportingId: number | undefined
Expand All @@ -35,14 +68,10 @@ export type Reporting = {
themeId: number
updatedAtUtc: string | undefined
validityTime: number
vehicleType: string | undefined
vehicleType: VehicleTypeEnum | undefined
withVHFAnswer: boolean | undefined
}

export type ReportingDetailed = Reporting & {
displayedSource: string
}

export type DetachedReporting = {
attachedToMissionAtUtc?: string
detachedFromMissionAtUtc?: string
Expand Down Expand Up @@ -90,7 +119,7 @@ export type TargetDetails = {
vesselType?: VesselTypeEnum
}

export type ReportingForTimeline = Partial<ReportingDetailed> & {
export type ReportingForTimeline = Partial<Reporting> & {
actionSource: ActionSource
actionType: ActionTypeEnum.REPORTING
timelineDate: string
Expand Down
9 changes: 4 additions & 5 deletions frontend/src/domain/shared_slices/reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit'

import { ReportingContext } from './Global'

import type { AtLeast } from '../../types'
import type { Reporting, ReportingDetailed } from '../entities/reporting'
import type { Reporting } from '../entities/reporting'

export type ReportingType = {
context: ReportingContext
isFormDirty: boolean
reporting: AtLeast<ReportingDetailed, 'id'>
reporting: Reporting
}

export type SelectedReportingType = {
Expand Down Expand Up @@ -118,7 +117,7 @@ const reportingSlice = createSlice({
}
}
},
setReportingState(state, action: PayloadAction<AtLeast<Reporting, 'id'>>) {
setReportingState(state, action: PayloadAction<Reporting>) {
const { id } = action.payload
if (!id) {
return
Expand All @@ -135,7 +134,7 @@ const reportingSlice = createSlice({
setSelectedReportingIdOnMap(state, action: PayloadAction<number | undefined>) {
state.selectedReportingIdOnMap = action.payload
},
updateUnactiveReporting(state, action: PayloadAction<AtLeast<Partial<Reporting>, 'id'>>) {
updateUnactiveReporting(state, action: PayloadAction<Partial<Reporting>>) {
const { id } = action.payload

// If the reporting is active, hence the form is open, the form will be updated from Formik (see FormikSyncMissionFields.ts)
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/domain/use_cases/missions/addMission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { isNewMission } from '../../../utils/isNewMission'
import { sideWindowPaths } from '../../entities/sideWindow'

import type { HomeAppThunk } from '../../../store'
import type { ReportingDetailed } from '../../entities/reporting'
import type { Reporting } from '../../entities/reporting'

export const addMission =
({
attachedReporting,
initialControlUnit
}: Partial<{
attachedReporting: ReportingDetailed
attachedReporting: Reporting
initialControlUnit: ControlUnit.ControlUnit
}> = {}): HomeAppThunk =>
async (dispatch, getState) => {
Expand Down
35 changes: 25 additions & 10 deletions frontend/src/domain/use_cases/missions/saveMission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,42 @@ import { setToast } from '../../shared_slices/Global'
import { reportingActions } from '../../shared_slices/reporting'
import { MapInteractionListenerEnum, updateMapInteractionListeners } from '../map/updateMapInteractionListeners'

import type { MissionData, Mission, NewMission } from 'domain/entities/missions'

const MISSION_VALUES_TO_EXCLUDE = [
'attachedReportings',
'createdAtUtc',
'detachedReportings',
'detachedReportingIds',
'fishActions',
'hasRapportNavActions',
'updatedAtUtc'
]

export const saveMission =
(values, reopen = false, quitAfterSave = false) =>
(values: Mission | NewMission, reopen = false, quitAfterSave = false) =>
async (dispatch, getState) => {
const {
reporting: { reportings },
sideWindow: { currentPath }
} = getState()
const selectedMissions = getState().missionForms.missions

await dispatch(missionFormsActions.setIsListeningToEvents(false))

const envActions = [...values.envActions]
const sortedActions = envActions.sort((a, b) => a.id - b.id)
const valuesToSave = omit({ ...values, envActions: sortedActions }, [
'attachedReportings',
'detachedReportings',
'fishActions'
])
const sortedActions = envActions
.sort((a, b) => a.id.localeCompare(b.id))
.map(action => omit(action, 'durationMatchesMission'))

const valuesToSave = omit({ ...values, envActions: sortedActions }, ...MISSION_VALUES_TO_EXCLUDE)
const routeParams = getMissionPageRoute(currentPath)
const missionIsNewMission = isNewMission(routeParams?.params?.id)
await dispatch(missionFormsActions.setIsListeningToEvents(false))
const newOrNextMissionData = {
...valuesToSave,
id: missionIsNewMission ? undefined : Number(values.id)
} as MissionData

const newOrNextMissionData = missionIsNewMission ? { ...valuesToSave, id: undefined } : valuesToSave
const upsertMission = missionIsNewMission
? missionsAPI.endpoints.createMission
: missionsAPI.endpoints.updateMission
Expand All @@ -51,7 +66,7 @@ export const saveMission =
isFormDirty: false,
missionForm: missionUpdated
},
previousId: values.id
previousId: String(values.id)
})
)

Expand Down
14 changes: 10 additions & 4 deletions frontend/src/domain/use_cases/reporting/archiveReporting.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { REPORTING_VALUES_TO_EXCLUDE_FOR_API } from '@features/Reportings/constants'
import { omit } from 'lodash'

import { reportingsAPI } from '../../../api/reportingsAPI'
import { setToast } from '../../shared_slices/Global'
import { reportingActions } from '../../shared_slices/reporting'

import type { Reporting } from '../../entities/reporting'
import type { ReportingData } from '../../entities/reporting'

export const archiveReportingFromTable = (id: number) => async (dispatch, getState) => {
const {
Expand All @@ -17,9 +20,12 @@ export const archiveReportingFromTable = (id: number) => async (dispatch, getSta
reportingToArchive = reporting
}

const response = await dispatch(
reportingsAPI.endpoints.updateReporting.initiate({ ...(reportingToArchive as Reporting), isArchived: true })
)
const valuesToUpdate = {
...omit(reportingToArchive, ...REPORTING_VALUES_TO_EXCLUDE_FOR_API),
isArchived: true
} as ReportingData

const response = await dispatch(reportingsAPI.endpoints.updateReporting.initiate(valuesToUpdate))
if ('error' in response) {
throw Error("Erreur à l'archivage du signalement")
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { REPORTING_VALUES_TO_EXCLUDE_FOR_API } from '@features/Reportings/constants'
import omit from 'lodash/omit'

import { reportingsAPI } from '../../../api/reportingsAPI'
Expand All @@ -15,8 +16,8 @@ export const createMissionFromReporting = (values: Reporting | Partial<Reporting
reportingFormVisibility: { context: reportingContext }
} = getState().global

const valuesToSave = omit(values, ['attachedMission'])
const newOrNextReportingData = isNewReporting(valuesToSave.id) ? { ...valuesToSave, id: undefined } : valuesToSave
const valuesToSave = omit(values, ...REPORTING_VALUES_TO_EXCLUDE_FOR_API)
const newOrNextReportingData = isNewReporting(values.id) ? { ...valuesToSave, id: undefined } : valuesToSave
const endpoint = reportingsAPI.endpoints.createReporting

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ReportingDetailed } from '../../../entities/reporting'
import type { Reporting } from '../../../entities/reporting'
import type { SourceFilterProps } from '../../../shared_slices/ReportingsFilters'

export function sourceFilterFunction(reporting: ReportingDetailed, sourceFilter: SourceFilterProps[] | undefined) {
export function sourceFilterFunction(reporting: Reporting, sourceFilter: SourceFilterProps[] | undefined) {
if (!sourceFilter || sourceFilter.length === 0) {
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReportingDetailed } from '../../../entities/reporting'
import type { Reporting } from '../../../entities/reporting'

export function subThemesFilterFunction(reporting: ReportingDetailed, subThemesFilter: number[] | undefined) {
export function subThemesFilterFunction(reporting: Reporting, subThemesFilter: number[] | undefined) {
if (!subThemesFilter || subThemesFilter.length === 0) {
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReportingDetailed } from '../../../entities/reporting'
import type { Reporting } from '../../../entities/reporting'

export function themeFilterFunction(reporting: ReportingDetailed, themeFilter: number[] | undefined) {
export function themeFilterFunction(reporting: Reporting, themeFilter: number[] | undefined) {
if (!themeFilter || themeFilter.length === 0) {
return true
}
Expand Down
Loading
Loading