Skip to content

Commit

Permalink
Signalements – Revoir le mode d'archivage des alertes "FAR 48h" (#3806)
Browse files Browse the repository at this point in the history
## Linked issues

- Resolve #3733

A exécuter en INTE/PROD :
```
DELETE FROM reportings
WHERE value->>'type' == 'MISSING_FAR_48_HOURS_ALERT';
```

----

- [ ] Tests E2E (Cypress)
  • Loading branch information
louptheron authored Dec 12, 2024
2 parents 8e063e0 + 21aabc5 commit 6253401
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 25 deletions.
17 changes: 17 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"react-select": "5.8.3",
"react-toastify": "9.1.3",
"redux": "5.0.1",
"redux-mock-store": "1.5.5",
"redux-persist": "6.0.0",
"redux-thunk": "3.1.0",
"rsuite": "5.54.0",
Expand Down Expand Up @@ -138,9 +139,9 @@
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"lint-staged": "14.0.1",
"madge": "8.0.0",
"ora": "8.1.1",
"postgres": "3.4.5",
"madge": "8.0.0",
"prettier": "3.4.1",
"puppeteer": "22.15.0",
"strip-json-comments": "5.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import { Seafront } from '@constants/seafront'
import { PendingAlertValueType } from '@features/Alert/types'
import { ReportingCard } from '@features/Reporting/components/ReportingCard'
import { ReportingType } from '@features/Reporting/types'
import { afterAll, describe, expect, it } from '@jest/globals'
import { afterAll, describe, expect, it, jest } from '@jest/globals'
import { THEME, ThemeProvider } from '@mtes-mct/monitor-ui'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { noop } from 'lodash'
import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'

import { VesselIdentifier } from '../../../../../domain/entities/vessel/types'
import { PendingAlertValueType } from '../../../../Alert/types'

import type { PendingAlertReporting } from '@features/Reporting/types'

// @ts-ignore
jest.mock('../../../useCases/archiveReporting', () => () => ({ archiveReporting: jest.fn() }))
// @ts-ignore
jest.mock('../../../useCases/archiveReporting', () => ({ archiveReporting: jest.fn() }))
jest.mock('../../../../../hooks/useMainAppDispatch', () => ({ useMainAppDispatch: () => {} }))
jest.mock('../../../useCases/deleteReporting', () => ({ deleteReporting: jest.fn() }))

describe('ReportingCard()', () => {
afterAll(() => {
// Reset module registry to clear the mock
// @ts-ignore
jest.resetModules()
})

it('should display all other dates', async () => {
// Given
const mockStore = configureStore()
const store = mockStore({})

const reporting: PendingAlertReporting = {
creationDate: '2023-10-30T09:10:00Z',
externalReferenceNumber: '',
Expand Down Expand Up @@ -57,17 +60,19 @@ describe('ReportingCard()', () => {
}

render(
<ThemeProvider theme={THEME}>
<ReportingCard
isArchived={false}
onEdit={noop}
otherOccurrencesOfSameAlert={[
{ ...reporting, validationDate: '2024-10-30T15:08:05.845121Z' },
{ ...reporting, validationDate: '2025-10-30T15:08:05.845121Z' }
]}
reporting={reporting}
/>
</ThemeProvider>
<Provider store={store}>
<ThemeProvider theme={THEME}>
<ReportingCard
isArchived={false}
onEdit={noop}
otherOccurrencesOfSameAlert={[
{ ...reporting, validationDate: '2024-10-30T15:08:05.845121Z' },
{ ...reporting, validationDate: '2025-10-30T15:08:05.845121Z' }
]}
reporting={reporting}
/>
</ThemeProvider>
</Provider>
)

const linkElement = screen.getByText(/Voir les dates des autres alertes/i)
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/features/Reporting/components/ReportingCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ConfirmationModal } from '@components/ConfirmationModal'
import { getAlertNameFromType } from '@features/Alert/components/SideWindowAlerts/AlertListAndReportingList/utils'
import { PendingAlertValueType } from '@features/Alert/types'
import { deleteReporting } from '@features/Reporting/useCases/deleteReporting'
import { reportingIsAnInfractionSuspicion } from '@features/Reporting/utils'
import { useMainAppDispatch } from '@hooks/useMainAppDispatch'
Expand Down Expand Up @@ -35,6 +36,9 @@ export function ReportingCard({
const reportingName = Object.values(ReportingTypeCharacteristics).find(
reportingType => reportingType.code === reporting.type
)?.name
const canBeArchived = !(
reporting.type === ReportingType.ALERT && reporting.value.type === PendingAlertValueType.MISSING_FAR_48_HOURS_ALERT
)
const alertDateTime = getDateTime(
reporting.type === ReportingType.ALERT ? reporting.validationDate : reporting.creationDate,
true
Expand All @@ -54,7 +58,7 @@ export function ReportingCard({
}, [reporting, reportingName])

const archive = () => {
dispatch(archiveReporting(reporting.id, reporting.type))
dispatch(archiveReporting(reporting))
}

const askForDeletionConfirmation = () => {
Expand Down Expand Up @@ -166,7 +170,11 @@ export function ReportingCard({
Icon={Icon.Archive}
iconSize={20}
onClick={archive}
title="Archiver ce signalement"
title={
canBeArchived
? 'Archiver ce signalement'
: `Ce signalement sera archivé sous la forme de 2 alertes "Absence de message FAR en 24h"`
}
/>
<StyledIconButton
accent={Accent.TERTIARY}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Seafront } from '@constants/seafront'
import { PendingAlertValueType } from '@features/Alert/types'
import { ReportingType } from '@features/Reporting/types'

import { VesselIdentifier } from '../../../../../domain/entities/vessel/types'

import type { PendingAlertReporting } from '@features/Reporting/types'

export const fortyHeightHourAlertReporting: PendingAlertReporting = {
creationDate: '2023-10-30T09:10:00Z',
externalReferenceNumber: '',
flagState: 'ES',
id: 12345,
infraction: {
infraction:
'Pêche maritime non autorisée dans les eaux territoriales francaise par capitaine de navire communautaire',
infractionCategory: 'FISHING',
natinfCode: 2610,
regulation: 'ART.L.945-2 §I AL.1, ART.L.945-5 1°,2°,3°,4° C.RUR'
},
internalReferenceNumber: 'FR04504564',
ircs: '',
isArchived: false,
isDeleted: false,
type: ReportingType.ALERT,
underCharter: null,
validationDate: '2023-10-30T15:08:05.845121Z',
value: {
dml: null,
natinfCode: 2610,
seaFront: Seafront.NAMO,
type: PendingAlertValueType.MISSING_FAR_48_HOURS_ALERT
},
vesselId: 1234568,
vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER,
vesselName: 'A VESSEL'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { PendingAlertValueType } from '@features/Alert/types'
import { fortyHeightHourAlertReporting } from '@features/Reporting/useCases/__tests__/__mocks__/dummyReporting'
import { archiveReporting } from '@features/Reporting/useCases/archiveReporting'
import { describe, it, expect, afterAll } from '@jest/globals'
import { mockedDispatch } from '@store/__tests__/utils'

import { VesselIdentifier } from '../../../../domain/entities/vessel/types'
import { deleteReporting } from '../deleteReporting'

/**
* Warning: We could not add `jest` import as it makes the test to fail.
* We need to have
* @see: https://github.com/swc-project/jest/issues/14#issuecomment-2525330413
*/

// @ts-ignore
jest.mock('../../reportingApi', () => jest.fn())
// @ts-ignore
jest.mock('../deleteReporting', () => ({
// eslint-disable-next-line @typescript-eslint/naming-convention
__esModule: true,
// @ts-ignore
deleteReporting: jest.fn()
}))

describe('archiveReporting()', () => {
const INITIAL_STATE = {
vessel: {
selectedVesselIdentity: {
externalReferenceNumber: '',
flagState: '',
internalReferenceNumber: '',
vesselId: 1234568,
vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER,
vesselName: ''
}
}
}

afterAll(() => {
// Reset module registry to clear the mock
// @ts-ignore
jest.resetModules()
})

it('Should delete reporting When alert is MISSING_FAR_48_HOURS_ALERT', async () => {
// When
mockedDispatch(archiveReporting(fortyHeightHourAlertReporting), INITIAL_STATE)

// Then
expect(deleteReporting).toHaveBeenCalled()
})

it('Should not delete reporting When the alert is not an MISSING_FAR_48_HOURS_ALERT', async () => {
// Given
const otherAlertReporting = {
...fortyHeightHourAlertReporting,
value: {
...fortyHeightHourAlertReporting.value,
type: PendingAlertValueType.MISSING_FAR_ALERT
}
}

// When
mockedDispatch(archiveReporting(otherAlertReporting), INITIAL_STATE)

// Then
expect(deleteReporting).toHaveBeenCalledTimes(0)
})
})
22 changes: 17 additions & 5 deletions frontend/src/features/Reporting/useCases/archiveReporting.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { PendingAlertValueType } from '@features/Alert/types'
import { ReportingType } from '@features/Reporting/types'
import { deleteReporting } from '@features/Reporting/useCases/deleteReporting'
import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures'
import { DisplayedErrorKey } from '@libs/DisplayedError/constants'

Expand All @@ -6,20 +9,29 @@ import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogE
import { removeVesselReporting } from '../../Vessel/slice'
import { reportingApi } from '../reportingApi'

import type { ReportingType } from '@features/Reporting/types'
import type { Reporting } from '@features/Reporting/types'
import type { MainAppThunk } from '@store'

export const archiveReporting =
(id: number, type: ReportingType): MainAppThunk<Promise<void>> =>
(reporting: Reporting.Reporting): MainAppThunk<Promise<void>> =>
async (dispatch, getState) => {
const { selectedVesselIdentity } = getState().vessel

try {
await dispatch(reportingApi.endpoints.archiveReporting.initiate(id)).unwrap()
if (
reporting.type === ReportingType.ALERT &&
reporting.value.type === PendingAlertValueType.MISSING_FAR_48_HOURS_ALERT
) {
await dispatch(deleteReporting(reporting.id, reporting.type))

return
}

await dispatch(reportingApi.endpoints.archiveReporting.initiate(reporting.id)).unwrap()

dispatch(
removeVesselReporting({
reportingType: type,
reportingType: reporting.type,
vesselFeatureId: Vessel.getVesselFeatureId(selectedVesselIdentity)
})
)
Expand All @@ -29,7 +41,7 @@ export const archiveReporting =
dispatch(
displayOrLogError(
error as Error,
() => archiveReporting(id, type),
() => archiveReporting(reporting),
true,
DisplayedErrorKey.VESSEL_SIDEBAR_ERROR
)
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/store/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { jest } from '@jest/globals'

/**
* To be used to capture all dispatched actions.
*
* we could have more middleware functions being called within the
* use-case middleware, and we should be able to capture all of these events.
*/
export const mockedDispatch = (action, initialState) => {
const store = {
dispatch: jest.fn(fn => {
if (typeof fn === 'function') {
fn(store.dispatch, store.getState)
}
}),
getState: jest.fn(() => initialState)
}

action(store.dispatch, store.getState)

return store
}

0 comments on commit 6253401

Please sign in to comment.