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

[Mission] Correction de la duplication de missions #3052

Merged
merged 3 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
82 changes: 0 additions & 82 deletions frontend/cypress/e2e/side_window/mission_form/action_list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,88 +34,6 @@ context('Side Window > Mission Form > Action List', () => {
cy.get('*[data-cy="action-list-item"]').eq(0).should('contain', 'Une observation.')
})

it('Should send the expected data to the API when duplicating a mission action', () => {
editSideWindowMissionListMissionWithId(4, SeaFrontGroup.MEMN)

cy.intercept('POST', '/api/v1/missions/4', {
body: {
id: 1
},
statusCode: 201
}).as('updateMission4')
cy.intercept('POST', '/bff/v1/mission_actions').as('createMissionAction')
cy.get('*[data-cy="action-list-item"]').click()

cy.wait(250)

cy.clickButton('Dupliquer l’action')

cy.wait(250)

cy.waitForLastRequest(
'@createMissionAction',
{
body: {
actionType: 'SEA_CONTROL',
closedBy: null,
controlQualityComments: null,
controlUnits: [],
emitsAis: null,
emitsVms: 'NOT_APPLICABLE',
externalReferenceNumber: null,
facade: 'MEMN',
faoAreas: ['27.8.a'],
feedbackSheetRequired: false,
flagState: 'FR',
flightGoals: [],
gearInfractions: [],
gearOnboard: [],
hasSomeGearsSeized: false,
hasSomeSpeciesSeized: false,
id: null,
internalReferenceNumber: 'U_W0NTFINDME',
ircs: null,
latitude: 53.35,
licencesAndLogbookObservations: null,
licencesMatchActivity: 'NOT_APPLICABLE',
logbookInfractions: [],
logbookMatchesActivity: 'NOT_APPLICABLE',
longitude: -10.85,
missionId: 4,
numberOfVesselsFlownOver: null,
otherComments: 'Commentaires post contrôle',
otherInfractions: [],
portLocode: null,
segments: [],
seizureAndDiversion: false,
seizureAndDiversionComments: null,
separateStowageOfPreservedSpecies: 'NO',
speciesInfractions: [],
speciesObservations: null,
speciesOnboard: [],
speciesSizeControlled: null,
speciesWeightControlled: null,
unitWithoutOmegaGauge: false,
userTrigram: 'JKL',
vesselId: 2,
vesselName: 'MALOTRU',
vesselTargeted: 'YES'
}
},
5
)
.its('response.statusCode')
.should('eq', 201)

// And we delete this action

editSideWindowMissionListMissionWithId(4, SeaFrontGroup.MEMN)

cy.clickButton('Supprimer l’action', { index: 1 })

cy.wait(250)
})

it('Should send the expected data to the API when deleting a mission action', () => {
editSideWindowMissionListMissionWithId(34, SeaFrontGroup.MEMN)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import type { Promisable } from 'type-fest'

type FishActionCardProps = Readonly<{
missionAction: MissionActionFormValues
onDuplicate: () => Promisable<void>
onRemove: () => Promisable<void>
}>
export function FishActionCard({ missionAction, onDuplicate, onRemove }: FishActionCardProps) {
export function FishActionCard({ missionAction, onRemove }: FishActionCardProps) {
const natinfsAsOptions = useGetNatinfsAsOptions()

const isControlAction =
Expand Down Expand Up @@ -119,15 +118,6 @@ export function FishActionCard({ missionAction, onDuplicate, onRemove }: FishAct
<p>{actionLabel}</p>
</ActionLabel>

<RightAlignedIconButton
accent={Accent.TERTIARY}
color={THEME.color.slateGray}
Icon={Icon.Duplicate}
iconSize={20}
onClick={onDuplicate}
title="Dupliquer l’action"
withUnpropagatedClick
/>
<RightAlignedIconButton
accent={Accent.TERTIARY}
color={THEME.color.maximumRed}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type ActionListProps = Readonly<{
missionId: number | undefined
missionTypes: Mission.MissionType[] | undefined
onAdd: (actionType: MissionAction.MissionActionType) => Promisable<void>
onDuplicate: (actionIndex: number) => Promisable<void>
onRemove: (actionIndex: number) => Promisable<void>
onSelect: (actionIndex: number) => Promisable<void>
}>
Expand All @@ -33,7 +32,6 @@ export function ActionList({
missionId,
missionTypes = [],
onAdd,
onDuplicate,
onRemove,
onSelect
}: ActionListProps) {
Expand Down Expand Up @@ -128,7 +126,6 @@ export function ActionList({
// eslint-disable-next-line react/no-array-index-key
key={index}
missionAction={action as MissionActionFormValues}
onDuplicate={() => onDuplicate(action.index!!)}
onRemove={() => onRemove(action.index!!)}
/>
</ActionCard>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { expect } from '@jest/globals'

import { MissionAction } from '../../../missionAction.types'
import { getMissionActionsDataFromMissionActionsFormValues } from '../utils'
import { getMissionActionsToCreateUpdateOrDelete } from '../utils'

import MissionActionType = MissionAction.MissionActionType

describe('features/Mission/components/MissionForm/utils.getMissionActionsDataFromMissionActionsFormValues()', () => {
it('Should add the id to an updated action', () => {
it('Should delete a previous action delete from the list and create a new action', () => {
// Given
const missionId = 123
const actionsFormValues = [
Expand Down Expand Up @@ -171,22 +171,104 @@ describe('features/Mission/components/MissionForm/utils.getMissionActionsDataFro
]

// When
const { deletedMissionActionIds, updatedMissionActionDatas } = getMissionActionsDataFromMissionActionsFormValues(
const { createdOrUpdatedMissionActions, deletedMissionActionIds } = getMissionActionsToCreateUpdateOrDelete(
missionId,
actionsFormValues,
originalMissionActions
)

// Then
expect(deletedMissionActionIds).toHaveLength(0)
expect(updatedMissionActionDatas).toHaveLength(1)
expect(updatedMissionActionDatas[0]?.id).toEqual(20)
expect(deletedMissionActionIds).toHaveLength(1)
expect(deletedMissionActionIds[0]).toEqual(20)
expect(createdOrUpdatedMissionActions).toHaveLength(1)
expect(createdOrUpdatedMissionActions[0]?.id).toEqual(undefined)
})

it('Should get deleted actions When the action is no more in the action form list', () => {
// Given
const missionId = 123
const actionsFormValues = []
const actionsFormValues = [
{
actionDatetimeUtc: '2023-12-08T08:27:00Z',
actionType: MissionActionType.SEA_CONTROL,
closedBy: undefined,
controlQualityComments: undefined,
controlUnits: [],
districtCode: 'AY',
emitsAis: undefined,
emitsVms: undefined,
externalReferenceNumber: 'DONTSINK',
facade: 'NAMO',
faoAreas: ['27.8.b', '27.8.c'],
feedbackSheetRequired: false,
flagState: 'FR',
flightGoals: [],
gearInfractions: [],
gearOnboard: [
{
comments: undefined,
controlledMesh: undefined,
declaredMesh: 70.0,
gearCode: 'OTB',
gearName: 'Chaluts de fond à panneaux',
gearWasControlled: undefined,
hasUncontrolledMesh: false
}
],
hasSomeGearsSeized: false,
hasSomeSpeciesSeized: false,
id: 20,
internalReferenceNumber: 'FAK000999999',
ircs: 'CALLME',
isAdministrativeControl: undefined,
isComplianceWithWaterRegulationsControl: undefined,
isFromPoseidon: false,
isSafetyEquipmentAndStandardsComplianceControl: undefined,
isSeafarersControl: undefined,
isValid: true,
latitude: 47.648401281163814,
licencesAndLogbookObservations: undefined,
licencesMatchActivity: undefined,
logbookInfractions: [],
logbookMatchesActivity: undefined,
longitude: -4.281934312813745,
missionId: 43,
numberOfVesselsFlownOver: undefined,
otherComments: undefined,
otherInfractions: [],
portLocode: undefined,
portName: undefined,
segments: [{ segment: 'SWW01/02/03', segmentName: 'Bottom trawls' }],
seizureAndDiversion: false,
seizureAndDiversionComments: undefined,
separateStowageOfPreservedSpecies: undefined,
speciesInfractions: [],
speciesObservations: undefined,
speciesOnboard: [
{
controlledWeight: undefined,
declaredWeight: 471.2,
nbFish: undefined,
speciesCode: 'HKE',
underSized: false
},
{
controlledWeight: undefined,
declaredWeight: 13.46,
nbFish: undefined,
speciesCode: 'BLI',
underSized: false
}
],
speciesSizeControlled: undefined,
speciesWeightControlled: undefined,
unitWithoutOmegaGauge: false,
userTrigram: 'LT',
vesselId: 1,
vesselName: 'PHENOMENE',
vesselTargeted: undefined
}
]
const originalMissionActions = [
{
actionDatetimeUtc: '2023-12-08T08:27:00Z',
Expand Down Expand Up @@ -270,15 +352,15 @@ describe('features/Mission/components/MissionForm/utils.getMissionActionsDataFro
]

// When
const { deletedMissionActionIds, updatedMissionActionDatas } = getMissionActionsDataFromMissionActionsFormValues(
const { createdOrUpdatedMissionActions, deletedMissionActionIds } = getMissionActionsToCreateUpdateOrDelete(
missionId,
actionsFormValues,
originalMissionActions
)

// Then
expect(deletedMissionActionIds).toHaveLength(1)
expect(deletedMissionActionIds).toEqual([20])
expect(updatedMissionActionDatas).toHaveLength(0)
expect(deletedMissionActionIds).toHaveLength(0)
expect(createdOrUpdatedMissionActions).toHaveLength(1)
expect(createdOrUpdatedMissionActions[0]?.id).toEqual(20)
})
})
35 changes: 0 additions & 35 deletions frontend/src/features/Mission/components/MissionForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
import { assertNotNullish } from '@utils/assertNotNullish'
import { getMissionStatus } from 'domain/entities/mission/utils'
import { SideWindowMenuKey } from 'domain/entities/sideWindow/constants'
import { omit } from 'lodash/fp'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components'
import { FrontendErrorBoundary } from 'ui/FrontendErrorBoundary'
Comment on lines 26 to 31
Copy link
Contributor

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [1-1]

Consider refactoring the MissionForm component to improve maintainability and modularity. The component is large and handles multiple responsibilities, which could make future modifications more challenging. Breaking it down into smaller, more focused sub-components and separating business logic from UI rendering where possible would enhance readability and maintainability.

Expand Down Expand Up @@ -334,39 +333,6 @@ export function MissionForm() {
]
)

const duplicateAction = useCallback(
async (actionIndex: number) => {
/**
* If a debounce function is not yet executed, stop there to avoid race condition.
* /!\ This can leads to save the debounced action update to the wrong action index
*/
if (updateEditedActionFormValues.isPending()) {
setTimeout(() => duplicateAction(actionIndex), DEBOUNCE_DELAY)

return
}

const actionCopy: MissionActionFormValues = omit(['id'], actionsFormValues[actionIndex])
setEditedActionIndex(0)

const createdId = await dispatch(
autoSaveMissionAction(actionCopy, missionIdRef.current, mainFormValues.isClosed, isAutoSaveEnabled)
)

const nextActionsWithIdFormValues = [{ ...actionCopy, id: createdId }, ...actionsFormValues]
setActionsFormValues(nextActionsWithIdFormValues)
updateReduxSliceDraft()
},
[
dispatch,
updateEditedActionFormValues,
updateReduxSliceDraft,
actionsFormValues,
mainFormValues.isClosed,
isAutoSaveEnabled
]
)

const updateEditedActionIndex = useCallback(
(nextActionIndex: number | undefined) => {
/**
Expand Down Expand Up @@ -488,7 +454,6 @@ export function MissionForm() {
missionId={missionIdRef.current}
missionTypes={mainFormValues.missionTypes}
onAdd={addAction}
onDuplicate={duplicateAction}
onRemove={removeAction}
onSelect={updateEditedActionIndex}
/>
Expand Down
22 changes: 8 additions & 14 deletions frontend/src/features/Mission/components/MissionForm/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,33 @@ import MissionActionType = MissionAction.MissionActionType
* @param actionsFormValues
* @param originalMissionActions Mission actions as they were previous to the mission edition
*/
export function getMissionActionsDataFromMissionActionsFormValues(
export function getMissionActionsToCreateUpdateOrDelete(
missionId: MissionAction.MissionAction['missionId'],
actionsFormValues: MissionActionFormValues[],
originalMissionActions: MissionAction.MissionAction[] = []
): {
createdOrUpdatedMissionActions: MissionAction.MissionActionData[]
deletedMissionActionIds: number[]
updatedMissionActionDatas: MissionAction.MissionActionData[]
} {
const updatedMissionActionDatas = actionsFormValues.map((missionActionFormValues, index) =>
getMissionActionDataFromFormValues(missionActionFormValues, missionId, originalMissionActions, index)
const createdOrUpdatedMissionActions = actionsFormValues.map(missionActionFormValues =>
getMissionActionDataFromFormValues(missionActionFormValues, missionId)
)

const originalMissionActionIds = originalMissionActions.map(({ id }) => id)
const updatedMissionActionIds = updatedMissionActionDatas
const updatedMissionActionIds = createdOrUpdatedMissionActions
.filter(({ id }) => typeof id === 'number')
.map(({ id }) => id as number)
const deletedMissionActionIds = difference(originalMissionActionIds, updatedMissionActionIds)

return {
deletedMissionActionIds,
updatedMissionActionDatas
createdOrUpdatedMissionActions,
deletedMissionActionIds
}
}

export function getMissionActionDataFromFormValues(
missionActionFormValues: MissionActionFormValues,
missionId: MissionAction.MissionAction['missionId'],
originalMissionActions: MissionAction.MissionAction[] = [],
index?: number
missionId: MissionAction.MissionAction['missionId']
) {
const missionActionFormValuesWithAllProps = {
...MISSION_ACTION_FORM_VALUES_SKELETON,
Expand All @@ -56,12 +54,8 @@ export function getMissionActionDataFromFormValues(
const maybeValidMissionActionData = omit(missionActionFormValuesWithAllProps, ['isValid', 'isVesselUnknown'])
const validMissionActionData = getValidMissionActionData(maybeValidMissionActionData as MissionActionFormValues)

// We get the action `id` to know if the action is an update
const id = index !== undefined ? originalMissionActions[index]?.id : missionActionFormValues.id

return {
...validMissionActionData,
id,
missionId
}
}
Expand Down
Loading
Loading