diff --git a/frontend/cypress/e2e/side_window/mission_list/filters.spec.ts b/frontend/cypress/e2e/side_window/mission_list/filters.spec.ts
index 35a781a786..7fb975b731 100644
--- a/frontend/cypress/e2e/side_window/mission_list/filters.spec.ts
+++ b/frontend/cypress/e2e/side_window/mission_list/filters.spec.ts
@@ -54,12 +54,12 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'CACEM')
})
+
+ cy.fill('Administration', undefined)
})
it('Should filter missions by administrations', () => {
- cy.getDataCy('select-administration-filter').click().wait(100)
- cy.get('.rs-picker-search-bar-input').type('DDTM').wait(100)
- cy.get('[data-key="DDTM"]').click().wait(100).clickOutside()
+ cy.fill('Administration', ['DDTM'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -69,12 +69,11 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'DDTM')
})
+ cy.fill('Administration', undefined)
})
it('Should filter missions by units', () => {
- cy.getDataCy('select-units-filter').click().wait(100)
- cy.get('.rs-picker-search-bar-input').type('BSN').wait(100)
- cy.get('[data-key="10015"]').click().wait(100).clickOutside()
+ cy.fill('Unité', ['BSN'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -84,11 +83,12 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'BSN Ste Maxime')
})
+
+ cy.fill('Unité', undefined)
})
it('Should filter missions by types', () => {
- cy.getDataCy('select-types-filter').click().wait(100)
- cy.get('[data-key="SEA"]').click().wait(100).clickOutside()
+ cy.fill('Type de mission', ['Mer'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -98,11 +98,12 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'Mer')
})
+
+ cy.fill('Type de mission', undefined)
})
it('Should filter missions by sea fronts', () => {
- cy.getDataCy('select-seaFronts-filter').click().wait(100)
- cy.get('[data-key="MED"]').click().wait(100).clickOutside()
+ cy.fill('Facade', ['MED'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -112,11 +113,12 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'MED')
})
+
+ cy.fill('Facade', undefined)
})
it('Should filter missions by statuses', () => {
- cy.getDataCy('select-statuses-filter').click().wait(100)
- cy.get('[data-key="PENDING"]').click().wait(100).clickOutside()
+ cy.fill('Statut', ['En cours'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -126,12 +128,12 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'En cours')
})
+
+ cy.fill('Statut', undefined)
})
it('Should filter missions by themes', () => {
- cy.getDataCy('select-theme-filter').click().wait(100)
- cy.get('.rs-picker-search-bar-input').type('Police').wait(100)
- cy.get('[data-key="Police des activités de cultures marines"]').click().wait(100).clickOutside()
+ cy.fill('Thématique', ['Police des activités de cultures marines'])
cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0)
cy.get('.Table-SimpleTable tr').each((row, index) => {
@@ -141,5 +143,7 @@ context('Side Window > Mission List > Filter Bar', () => {
cy.wrap(row).should('contain', 'Police des activités de cultures marines')
})
+
+ cy.fill('Thématique', undefined)
})
})
diff --git a/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts b/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts
index 4b88d4f1f2..ba64bd51d5 100644
--- a/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts
+++ b/frontend/cypress/e2e/side_window/mission_list/missions.spec.ts
@@ -40,16 +40,12 @@ context('Missions', () => {
cy.log('Units should be filtered')
cy.get('*[data-cy="edit-mission-38"]').should('exist')
- cy.get('*[data-cy="select-units-filter"]').click()
- cy.get('div[role="option"]').find('label').contains('PAM Themis').click({ force: true })
+ cy.fill('Unité', ['PAM Themis'])
cy.get('*[data-cy="edit-mission-48"]').should('exist')
cy.get('*[data-cy="edit-mission-38"]').should('not.exist')
cy.log('Units filter should be clear')
- cy.get('*[data-cy="Missions-numberOfDisplayedMissions"]').click('topLeft')
- cy.get('*[data-cy="select-units-filter"]').get('[title="Clear"]').click({
- force: true
- })
+ cy.fill('Unité', undefined)
cy.get('*[data-cy="edit-mission-38"]').should('exist')
})
@@ -57,8 +53,7 @@ context('Missions', () => {
cy.visit(`/side_window`).wait(1000)
cy.log('Should filter by theme')
- cy.get('*[data-cy="select-theme-filter"]').click()
- cy.get('div[role="option"]').find('label').contains('Police des épaves').click({ force: true })
+ cy.fill('Thématique', ['Police des épaves'])
cy.get('*[data-cy="cell-envactions-themes"]')
.eq(0)
.contains(
diff --git a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts
new file mode 100644
index 0000000000..6ade00f5f7
--- /dev/null
+++ b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts
@@ -0,0 +1,58 @@
+import { ReportingSourceLabels } from '../../../../src/domain/entities/reporting'
+import { SeaFrontLabel } from '../../../../src/domain/entities/seaFrontType'
+
+context('Reportings', () => {
+ beforeEach(() => {
+ cy.viewport(1280, 1024)
+ cy.visit(`/side_window`)
+ cy.intercept('GET', '/bff/v1/reportings*').as('getReportings')
+ cy.clickButton('signalements')
+ cy.wait('@getReportings')
+ })
+
+ it('Reportings should be displayed in Reportings Table and filterable', () => {
+ cy.log('A default period filter should be set')
+ cy.fill('Période', '24 dernières heures')
+ cy.get('*[data-cy="totalReportings"]').contains('5')
+
+ cy.log('Source type should be filtered')
+ cy.fill('Type de source', [ReportingSourceLabels.SEMAPHORE])
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Type Sémaphore')
+ // here we test if the clear button worked correctly
+ cy.fill('Type de source', undefined)
+
+ cy.log('Source should be filtered')
+ cy.fill('Source', ['Sémaphore de Fécamp'])
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Source Sémaphore de Fécamp')
+ cy.fill('Source', undefined)
+
+ cy.log('Reporting type should be filtered')
+ cy.fill('Type de signalement', 'Observation')
+ cy.getDataCy('totalReportings').contains('5')
+
+ cy.log('Themes should be filtered')
+ cy.fill('Thématiques', ['Rejets illicites', 'Police des mouillages'])
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Thème Rejets illicites')
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Thème Police des mouillages')
+ cy.fill('Thématiques', undefined)
+
+ cy.log('Sub-themes should be filtered')
+ cy.fill('Sous-thématiques', ['Arrêté municipal'])
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Sous-thème Arrêté municipal')
+ cy.fill('Sous-thématiques', undefined)
+
+ cy.log('Sea fronts should be filtered')
+ cy.fill('Facade', [SeaFrontLabel.MARTINIQUE, SeaFrontLabel.SOUTH_INDIAN_OCEAN])
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Facade Martinique')
+ cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Facade Sud Océan Indien')
+ cy.fill('Facade', undefined)
+
+ cy.wait('@getReportings')
+ cy.getDataCy('reinitialize-filters').click()
+ cy.getDataCy('totalReportings').contains('5')
+
+ cy.getDataCy('status-filter-Archivés').click()
+ cy.fill('Période', '30 derniers jours')
+ cy.getDataCy('totalReportings').contains('8')
+ })
+})
diff --git a/frontend/cypress/e2e/side_window/reporting/reportings.spec.ts b/frontend/cypress/e2e/side_window/reporting/reportings.spec.ts
index cfc6d7734b..9b145754da 100644
--- a/frontend/cypress/e2e/side_window/reporting/reportings.spec.ts
+++ b/frontend/cypress/e2e/side_window/reporting/reportings.spec.ts
@@ -1,4 +1,4 @@
-context('Missions', () => {
+context('Reportings', () => {
beforeEach(() => {
cy.viewport(1280, 1024)
cy.visit(`/side_window`)
@@ -7,31 +7,6 @@ context('Missions', () => {
cy.wait('@getReportings')
})
- it('Reportings should be displayed in Reportings Table and filterable', () => {
- cy.log('A default period filter should be set')
- cy.fill('Période', '24 dernières heures')
- cy.get('*[data-cy="totalReportings"]').contains('5')
-
- cy.log('Source type should be filtered')
- cy.get('*[data-cy="select-source-type-filter"]').click()
- cy.get('div[role="option"]').find('label').contains('Sémaphore').click()
- cy.get('*[data-cy="totalReportings"]').contains('2')
-
- cy.log('Source should be filtered')
- cy.get('*[data-cy="select-source-filter"]').click()
- cy.get('div[role="option"]').find('label').contains('Sémaphore de Fécamp').click()
- cy.get('*[data-cy="totalReportings"]').click('topLeft')
- cy.get('*[data-cy="totalReportings"]').contains('2')
-
- cy.wait('@getReportings')
- cy.get('*[data-cy="reinitialize-filters"]').click()
- cy.get('*[data-cy="totalReportings"]').contains('5')
-
- cy.get('*[data-cy="status-filter-Archivés"]').click()
- cy.fill('Période', '30 derniers jours')
- cy.get('*[data-cy="totalReportings"]').contains('8')
- })
-
it('Reportings should be archived in Reportings Table', () => {
cy.intercept('PUT', '/bff/v1/reportings/5').as('archiveReporting')
cy.get('*[data-cy="status-filter-Archivés"]').click()
diff --git a/frontend/src/api/missionsAPI.ts b/frontend/src/api/missionsAPI.ts
index c09bd220b8..01f562523b 100644
--- a/frontend/src/api/missionsAPI.ts
+++ b/frontend/src/api/missionsAPI.ts
@@ -8,7 +8,7 @@ type MissionsFilter = {
missionSource?: string
missionStatus?: string[]
missionTypes?: string[]
- seaFronts: string[]
+ seaFronts?: string[]
startedAfterDateTime?: string
startedBeforeDateTime?: string
}
diff --git a/frontend/src/api/reportingsAPI.ts b/frontend/src/api/reportingsAPI.ts
index fa38713665..f8895d61dc 100644
--- a/frontend/src/api/reportingsAPI.ts
+++ b/frontend/src/api/reportingsAPI.ts
@@ -7,7 +7,7 @@ import type { Reporting, ReportingDetailed } from '../domain/entities/reporting'
type ReportingsFilter = {
reportingType: string | undefined
- seaFronts: string[]
+ seaFronts?: string[]
sourcesType?: string[]
startedAfterDateTime?: string
startedBeforeDateTime?: string
diff --git a/frontend/src/domain/entities/missions.ts b/frontend/src/domain/entities/missions.ts
index e58d090412..741d0bc519 100644
--- a/frontend/src/domain/entities/missions.ts
+++ b/frontend/src/domain/entities/missions.ts
@@ -45,6 +45,12 @@ export const missionTypeEnum = {
}
}
+export enum MissionTypeLabel {
+ AIR = 'Air',
+ LAND = 'Terre',
+ SEA = 'Mer'
+}
+
export enum InfractionTypeEnum {
WAITING = 'WAITING',
WITHOUT_REPORT = 'WITHOUT_REPORT',
@@ -182,6 +188,14 @@ export enum MissionStatusEnum {
PENDING = 'PENDING',
UPCOMING = 'UPCOMING'
}
+
+export enum MissionStatusLabel {
+ CLOSED = 'Cloturée',
+ ENDED = 'Terminée',
+ PENDING = 'En cours',
+ UPCOMING = 'À venir'
+}
+
export const missionStatusLabels = {
CLOSED: {
borderColor: THEME.color.slateGray,
@@ -224,6 +238,11 @@ export const missionSourceEnum = {
}
}
+export enum MissionSourceLabel {
+ MONITORENV = 'CACEM',
+ MONITORFISH = 'CNSP'
+}
+
export const THEME_REQUIRE_PROTECTED_SPECIES = ['Police des espèces protégées et de leurs habitats (faune et flore)']
export const relevantCourtEnum = {
diff --git a/frontend/src/domain/shared_slices/MissionFilters.ts b/frontend/src/domain/shared_slices/MissionFilters.ts
index 1b2c225aae..024e6938eb 100644
--- a/frontend/src/domain/shared_slices/MissionFilters.ts
+++ b/frontend/src/domain/shared_slices/MissionFilters.ts
@@ -1,5 +1,6 @@
import { customDayjs as dayjs } from '@mtes-mct/monitor-ui'
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'
+import { isEqual, omit } from 'lodash'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
@@ -22,14 +23,14 @@ export enum MissionFiltersEnum {
type MissionFilterValues = {
hasFilters: boolean
- selectedAdministrationNames: string[]
- selectedControlUnitIds: number[]
+ selectedAdministrationNames: string[] | undefined
+ selectedControlUnitIds: number[] | undefined
selectedMissionSource: string | undefined
- selectedMissionTypes: string[]
+ selectedMissionTypes: string[] | undefined
selectedPeriod: string
- selectedSeaFronts: string[]
- selectedStatuses: string[]
- selectedThemes: string[]
+ selectedSeaFronts: string[] | undefined
+ selectedStatuses: string[] | undefined
+ selectedThemes: string[] | undefined
startedAfter?: string
startedBefore?: string
}
@@ -42,14 +43,14 @@ export type MissionFiltersState = {
const INITIAL_STATE: MissionFiltersState = {
hasFilters: false,
- selectedAdministrationNames: [],
- selectedControlUnitIds: [],
+ selectedAdministrationNames: undefined,
+ selectedControlUnitIds: undefined,
selectedMissionSource: undefined,
- selectedMissionTypes: [],
+ selectedMissionTypes: undefined,
selectedPeriod: DATE_RANGE_LABEL.WEEK.value,
- selectedSeaFronts: [],
- selectedStatuses: [],
- selectedThemes: [],
+ selectedSeaFronts: undefined,
+ selectedStatuses: undefined,
+ selectedThemes: undefined,
startedAfter: SEVEN_DAYS_AGO,
startedBefore: undefined
}
@@ -79,15 +80,14 @@ const missionFiltersSlice = createSlice({
return {
...state,
[action.payload.key]: action.payload.value,
- hasFilters:
- (action.payload.value && action.payload.value.length > 0) ||
- state.selectedPeriod !== DATE_RANGE_LABEL.WEEK.value ||
- state.selectedAdministrationNames.length > 0 ||
- state.selectedControlUnitIds.length > 0 ||
- state.selectedMissionTypes.length > 0 ||
- state.selectedSeaFronts.length > 0 ||
- state.selectedStatuses.length > 0 ||
- state.selectedThemes.length > 0
+ hasFilters: !isEqual(
+ omit(INITIAL_STATE, ['hasFilters', 'startedAfter', 'startedBefore']),
+ omit({ ...state, [action.payload.key]: action.payload.value }, [
+ 'hasFilters',
+ 'startedAfter',
+ 'startedBefore'
+ ])
+ )
}
}
}
diff --git a/frontend/src/domain/shared_slices/ReportingsFilters.ts b/frontend/src/domain/shared_slices/ReportingsFilters.ts
index 9418aa84ae..c016ca0eb2 100644
--- a/frontend/src/domain/shared_slices/ReportingsFilters.ts
+++ b/frontend/src/domain/shared_slices/ReportingsFilters.ts
@@ -31,14 +31,14 @@ type ReportingsFiltersSliceType = {
actionsFilter?: string[]
hasFilters: boolean
periodFilter: string
- seaFrontFilter: string[]
- sourceFilter: SourceFilterProps[]
- sourceTypeFilter: string[]
+ seaFrontFilter: string[] | undefined
+ sourceFilter: SourceFilterProps[] | undefined
+ sourceTypeFilter: string[] | undefined
startedAfter: string
startedBefore?: string
statusFilter: string[]
- subThemesFilter: string[]
- themeFilter: string[]
+ subThemesFilter: string[] | undefined
+ themeFilter: string[] | undefined
typeFilter?: string | undefined
}
@@ -46,14 +46,14 @@ const initialState: ReportingsFiltersSliceType = {
actionsFilter: [],
hasFilters: false,
periodFilter: ReportingDateRangeEnum.DAY,
- seaFrontFilter: [],
- sourceFilter: [],
- sourceTypeFilter: [],
+ seaFrontFilter: undefined,
+ sourceFilter: undefined,
+ sourceTypeFilter: undefined,
startedAfter: LAST_24_HOURS,
startedBefore: undefined,
statusFilter: [StatusFilterEnum.IN_PROGRESS],
- subThemesFilter: [],
- themeFilter: [],
+ subThemesFilter: undefined,
+ themeFilter: undefined,
typeFilter: undefined
}
diff --git a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedAdministrationNames.ts b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedAdministrationNames.ts
index a419eaa4ca..28e5dfdbbe 100644
--- a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedAdministrationNames.ts
+++ b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedAdministrationNames.ts
@@ -2,8 +2,12 @@ import type { Mission } from '../../../entities/missions'
export function isMissionPartOfSelectedAdministrationNames(
mission: Mission,
- selectedAdministrationNames: string[]
+ selectedAdministrationNames: string[] | undefined
): boolean {
+ if (!selectedAdministrationNames || selectedAdministrationNames.length === 0) {
+ return true
+ }
+
return selectedAdministrationNames.length
? !!mission.controlUnits.find(controlUnit => selectedAdministrationNames.includes(controlUnit.administration))
: true
diff --git a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedControlUnitIds.ts b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedControlUnitIds.ts
index 3f4f751637..feaa0c34a0 100644
--- a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedControlUnitIds.ts
+++ b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedControlUnitIds.ts
@@ -1,6 +1,13 @@
import type { Mission } from '../../../entities/missions'
-export function isMissionPartOfSelectedControlUnitIds(mission: Mission, selectedControlUnitIds: number[]): boolean {
+export function isMissionPartOfSelectedControlUnitIds(
+ mission: Mission,
+ selectedControlUnitIds: number[] | undefined
+): boolean {
+ if (!selectedControlUnitIds || selectedControlUnitIds.length === 0) {
+ return true
+ }
+
return selectedControlUnitIds.length
? !!mission.controlUnits.find(controlUnit => selectedControlUnitIds.includes(controlUnit.id))
: true
diff --git a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedThemes.ts b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedThemes.ts
index 1431e5b59e..0ae7ba33de 100644
--- a/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedThemes.ts
+++ b/frontend/src/domain/use_cases/missions/filters/isMissionPartOfSelectedThemes.ts
@@ -1,7 +1,7 @@
import type { Mission } from '../../../entities/missions'
-export function isMissionPartOfSelectedThemes(mission: Mission, selectedThemes: string[]) {
- if (selectedThemes.length === 0) {
+export function isMissionPartOfSelectedThemes(mission: Mission, selectedThemes: string[] | undefined) {
+ if (!selectedThemes || selectedThemes.length === 0) {
return true
}
if (mission.envActions.length === 0) {
diff --git a/frontend/src/domain/use_cases/reporting/filters/sourceFilterFunction.ts b/frontend/src/domain/use_cases/reporting/filters/sourceFilterFunction.ts
index 0151f4dc66..6842b05ec4 100644
--- a/frontend/src/domain/use_cases/reporting/filters/sourceFilterFunction.ts
+++ b/frontend/src/domain/use_cases/reporting/filters/sourceFilterFunction.ts
@@ -1,8 +1,8 @@
import type { ReportingDetailed } from '../../../entities/reporting'
import type { SourceFilterProps } from '../../../shared_slices/ReportingsFilters'
-export function sourceFilterFunction(reporting: ReportingDetailed, sourceFilter: SourceFilterProps[]) {
- if (sourceFilter.length === 0) {
+export function sourceFilterFunction(reporting: ReportingDetailed, sourceFilter: SourceFilterProps[] | undefined) {
+ if (!sourceFilter || sourceFilter.length === 0) {
return true
}
diff --git a/frontend/src/domain/use_cases/reporting/filters/subThemesFilterFunction.ts b/frontend/src/domain/use_cases/reporting/filters/subThemesFilterFunction.ts
index 58aa90d8ba..21e8386168 100644
--- a/frontend/src/domain/use_cases/reporting/filters/subThemesFilterFunction.ts
+++ b/frontend/src/domain/use_cases/reporting/filters/subThemesFilterFunction.ts
@@ -1,7 +1,7 @@
import type { ReportingDetailed } from '../../../entities/reporting'
-export function subThemesFilterFunction(reporting: ReportingDetailed, subThemesFilter: string[]) {
- if (subThemesFilter.length === 0) {
+export function subThemesFilterFunction(reporting: ReportingDetailed, subThemesFilter: string[] | undefined) {
+ if (!subThemesFilter || subThemesFilter.length === 0) {
return true
}
diff --git a/frontend/src/domain/use_cases/reporting/filters/themeFilterFunction.ts b/frontend/src/domain/use_cases/reporting/filters/themeFilterFunction.ts
index f9eb6550cb..09e0cb7905 100644
--- a/frontend/src/domain/use_cases/reporting/filters/themeFilterFunction.ts
+++ b/frontend/src/domain/use_cases/reporting/filters/themeFilterFunction.ts
@@ -1,7 +1,7 @@
import type { ReportingDetailed } from '../../../entities/reporting'
-export function themeFilterFunction(reporting: ReportingDetailed, themeFilter: string[]) {
- if (themeFilter.length === 0) {
+export function themeFilterFunction(reporting: ReportingDetailed, themeFilter: string[] | undefined) {
+ if (!themeFilter || themeFilter.length === 0) {
return true
}
diff --git a/frontend/src/features/Reportings/Filters/Map/index.tsx b/frontend/src/features/Reportings/Filters/Map/index.tsx
index 634a8721ec..d88d400b2a 100644
--- a/frontend/src/features/Reportings/Filters/Map/index.tsx
+++ b/frontend/src/features/Reportings/Filters/Map/index.tsx
@@ -1,4 +1,4 @@
-import { DateRangePicker, Checkbox, SingleTag, Accent } from '@mtes-mct/monitor-ui'
+import { CheckPicker, DateRangePicker, Checkbox, SingleTag, Accent } from '@mtes-mct/monitor-ui'
import { forwardRef, useRef } from 'react'
import styled from 'styled-components'
@@ -6,7 +6,7 @@ import { ReportingSourceLabels } from '../../../../domain/entities/reporting'
import { ReportingsFiltersEnum, reportingsFiltersActions } from '../../../../domain/shared_slices/ReportingsFilters'
import { useAppDispatch } from '../../../../hooks/useAppDispatch'
import { useAppSelector } from '../../../../hooks/useAppSelector'
-import { OptionValue, StyledCheckPicker, StyledSelect, StyledStatusFilter } from '../style'
+import { OptionValue, StyledSelect, StyledStatusFilter } from '../style'
export function MapReportingsFiltersWithRef(
{
@@ -98,20 +98,20 @@ export function MapReportingsFiltersWithRef(
- updateSourceTypeFilter(value)}
+ options={sourceTypeOptions}
placeholder="Type de source"
renderValue={() => sourceTypeFilter && {`Type (${sourceTypeFilter.length})`}}
searchable={false}
- size="sm"
value={sourceTypeFilter}
- valueKey="value"
/>
- {sourceTypeFilter.length > 0 && (
+ {sourceTypeFilter && sourceTypeFilter.length > 0 && (
{sourceTypeFilter.map(sourceType => (
- updateSimpleFilter(value, ReportingsFiltersEnum.THEME_FILTER)}
+ options={themesListAsOptions}
placeholder="Thématiques"
renderValue={() => themeFilter && {`Thème (${themeFilter.length})`}}
- size="sm"
+ searchable
value={themeFilter}
- valueKey="value"
/>
- {themeFilter.length > 0 && (
+ {themeFilter && themeFilter.length > 0 && (
{themeFilter.map(theme => (
)}
- updateSimpleFilter(value, ReportingsFiltersEnum.SUB_THEMES_FILTER)}
+ options={subThemesListAsOptions}
placeholder="Sous-thématiques"
renderValue={() => subThemesFilter && {`Sous-thème (${subThemesFilter.length})`}}
- size="sm"
+ searchable
value={subThemesFilter}
- valueKey="value"
/>
- {subThemesFilter.length > 0 && (
+ {subThemesFilter && subThemesFilter.length > 0 && (
{subThemesFilter.map(subTheme => (
state.reportingFilters
)
const onDeleteTag = (valueToDelete: string | any, filterKey: ReportingsFiltersEnum, reportingFilter) => {
const updatedFilter = reportingFilter.filter(unit => unit !== valueToDelete)
- dispatch(reportingsFiltersActions.updateFilters({ key: filterKey, value: updatedFilter }))
+ dispatch(
+ reportingsFiltersActions.updateFilters({
+ key: filterKey,
+ value: updatedFilter.length === 0 ? undefined : updatedFilter
+ })
+ )
}
- const hasNoFilterTags =
- sourceTypeFilter.length === 0 &&
- sourceFilter.length === 0 &&
- themeFilter.length === 0 &&
- subThemesFilter.length === 0 &&
- seaFrontFilter.length === 0
- if (hasNoFilterTags) {
+ if (!hasFilters) {
return null
}
return (
-
- {sourceTypeFilter.length > 0 &&
+
+ {sourceTypeFilter &&
+ sourceTypeFilter.length > 0 &&
sourceTypeFilter.map(sourceType => (
))}
- {sourceFilter.length > 0 &&
+ {sourceFilter &&
+ sourceFilter.length > 0 &&
sourceFilter.map(source => (
))}
- {themeFilter.length > 0 &&
+ {themeFilter &&
+ themeFilter.length > 0 &&
themeFilter.map(theme => (
))}
- {subThemesFilter.length > 0 &&
+ {subThemesFilter &&
+ subThemesFilter?.length > 0 &&
subThemesFilter.map(subTheme => (
))}
- {seaFrontFilter.length > 0 &&
+ {seaFrontFilter &&
+ seaFrontFilter.length > 0 &&
seaFrontFilter.map(seaFront => (
state.reportingFilters)
const {
dateRangeOptions,
@@ -54,6 +61,32 @@ export function TableReportingsFiltersWithRef(
typeOptions
} = optionsList
+ const sourceCustomSearch = useMemo(
+ () =>
+ new CustomSearch(sourceOptions as Option[], ['label'], {
+ cacheKey: 'REPORTINGS_LIST',
+ withCacheInvalidation: true
+ }),
+ [sourceOptions]
+ )
+ const themeCustomSearch = useMemo(
+ () =>
+ new CustomSearch(themesListAsOptions as Option[], ['label'], {
+ cacheKey: 'REPORTINGS_LIST',
+ withCacheInvalidation: true
+ }),
+ [themesListAsOptions]
+ )
+
+ const subThemeCustomSearch = useMemo(
+ () =>
+ new CustomSearch(subThemesListAsOptions as Option[], ['label'], {
+ cacheKey: 'REPORTINGS_LIST',
+ withCacheInvalidation: true
+ }),
+ [subThemesListAsOptions]
+ )
+
return (
<>
@@ -88,33 +121,34 @@ export function TableReportingsFiltersWithRef(
value={periodFilter}
/>
- updateSourceTypeFilter(value)}
+ options={sourceTypeOptions}
placeholder="Type de source"
renderValue={() => sourceTypeFilter && {`Type (${sourceTypeFilter.length})`}}
- searchable={false}
- size="sm"
style={tagPickerStyle}
value={sourceTypeFilter}
- valueKey="value"
/>
- updateSimpleFilter(value, ReportingsFiltersEnum.SOURCE_FILTER)}
+ options={sourceOptions}
+ optionValueKey={'label' as any}
placeholder="Source"
renderValue={() => sourceFilter && {`Source (${sourceFilter.length})`}}
- size="sm"
style={tagPickerStyle}
- value={sourceFilter}
- valueKey="value"
+ value={sourceFilter as any}
/>
- updateSimpleFilter(value, ReportingsFiltersEnum.THEME_FILTER)}
+ options={themesListAsOptions}
placeholder="Thématiques"
renderValue={() => themeFilter && {`Thème (${themeFilter.length})`}}
- size="sm"
style={{ width: 311 }}
value={themeFilter}
- valueKey="value"
/>
- updateSimpleFilter(value, ReportingsFiltersEnum.SUB_THEMES_FILTER)}
+ options={subThemesListAsOptions}
placeholder="Sous-thématiques"
renderValue={() => subThemesFilter && {`Sous-thème (${subThemesFilter.length})`}}
- size="sm"
+ searchable
style={{ width: 311 }}
value={subThemesFilter}
- valueKey="value"
/>
- updateSimpleFilter(value, ReportingsFiltersEnum.SEA_FRONT_FILTER)}
+ options={seaFrontsOptions}
placeholder="Facade"
renderValue={() => seaFrontFilter && {`Facade (${seaFrontFilter.length})`}}
- searchable={false}
size="sm"
style={tagPickerStyle}
value={seaFrontFilter}
- valueKey="value"
/>
diff --git a/frontend/src/features/Reportings/Filters/index.tsx b/frontend/src/features/Reportings/Filters/index.tsx
index 69f60429c1..f216bcaf66 100644
--- a/frontend/src/features/Reportings/Filters/index.tsx
+++ b/frontend/src/features/Reportings/Filters/index.tsx
@@ -80,10 +80,10 @@ export function ReportingsFilters({ context = ReportingFilterContext.TABLE }: {
)
const sourceOptions = useMemo(() => {
- if (sourceTypeFilter.length === 1 && sourceTypeFilter[0] === ReportingSourceEnum.SEMAPHORE) {
+ if (sourceTypeFilter && sourceTypeFilter.length === 1 && sourceTypeFilter[0] === ReportingSourceEnum.SEMAPHORE) {
return semaphoresAsOptions
}
- if (sourceTypeFilter.length === 1 && sourceTypeFilter[0] === ReportingSourceEnum.CONTROL_UNIT) {
+ if (sourceTypeFilter && sourceTypeFilter.length === 1 && sourceTypeFilter[0] === ReportingSourceEnum.CONTROL_UNIT) {
return unitListAsOptions
}
@@ -213,7 +213,7 @@ export function ReportingsFilters({ context = ReportingFilterContext.TABLE }: {
const updateSourceTypeFilter = types => {
dispatch(reportingsFiltersActions.updateFilters({ key: ReportingsFiltersEnum.SOURCE_TYPE_FILTER, value: types }))
- dispatch(reportingsFiltersActions.updateFilters({ key: ReportingsFiltersEnum.SOURCE_FILTER, value: [] }))
+ dispatch(reportingsFiltersActions.updateFilters({ key: ReportingsFiltersEnum.SOURCE_FILTER, value: undefined }))
}
const resetFilters = () => {
diff --git a/frontend/src/features/Reportings/Filters/style.ts b/frontend/src/features/Reportings/Filters/style.ts
index 50d3b7db12..c2675b33e1 100644
--- a/frontend/src/features/Reportings/Filters/style.ts
+++ b/frontend/src/features/Reportings/Filters/style.ts
@@ -1,5 +1,4 @@
import { Select } from '@mtes-mct/monitor-ui'
-import { CheckPicker } from 'rsuite'
import styled from 'styled-components'
export const StyledStatusFilter = styled.div`
@@ -16,11 +15,7 @@ export const StyledSelect = styled(Select)`
top: 5px !important;
}
`
-export const StyledCheckPicker = styled(CheckPicker)`
- .rs-picker-toggle-placeholder {
- font-size: 13px !important;
- }
-`
+
export const StyledTagsContainer = styled.div<{ $withMargin: boolean }>`
margin-top: ${p => (p.$withMargin ? '16px' : '0px')};
display: flex;
diff --git a/frontend/src/features/layersSelector/search/LayerFilters.tsx b/frontend/src/features/layersSelector/search/LayerFilters.tsx
index 21be88c523..1b51654a73 100644
--- a/frontend/src/features/layersSelector/search/LayerFilters.tsx
+++ b/frontend/src/features/layersSelector/search/LayerFilters.tsx
@@ -1,5 +1,5 @@
-import { Accent, SingleTag } from '@mtes-mct/monitor-ui'
-import { Button, CheckPicker } from 'rsuite'
+import { Accent, CheckPicker, SingleTag } from '@mtes-mct/monitor-ui'
+import { Button } from 'rsuite'
import styled from 'styled-components'
export function LayerFilters({
@@ -28,10 +28,12 @@ export function LayerFilters({
return (
-
filteredRegulatoryThemes && (
@@ -43,8 +45,8 @@ export function LayerFilters({
valueKey="value"
/>
- {filteredRegulatoryThemes.length > 0 &&
- filteredRegulatoryThemes.map(theme => (
+ {filteredRegulatoryThemes?.length > 0 &&
+ filteredRegulatoryThemes?.map(theme => (
- filteredAmpTypes && {`Type d'AMP (${filteredAmpTypes.length})`}}
size="sm"
@@ -67,15 +70,15 @@ export function LayerFilters({
valueKey="value"
/>
- {filteredAmpTypes.length > 0 &&
- filteredAmpTypes.map(type => (
+ {filteredAmpTypes?.length > 0 &&
+ filteredAmpTypes?.map(type => (
{type}
))}
- {(filteredRegulatoryThemes?.length > 0 || filteredAmpTypes.length > 0) && (
+ {(filteredRegulatoryThemes?.length > 0 || filteredAmpTypes?.length > 0) && (
Réinitialiser les filtres
@@ -101,16 +104,6 @@ const ResetFilters = styled(Button)`
padding: 0px;
`
-const StyledCheckPicker = styled(CheckPicker)`
- width: 100%;
- .rs-picker-toggle {
- background-color: ${p => p.theme.color.white} !important;
- }
- .rs-picker-toggle-placeholder {
- font-size: 13px !important;
- }
-`
-
const OptionValue = styled.span`
display: flex;
overflow: hidden;
diff --git a/frontend/src/features/layersSelector/search/index.tsx b/frontend/src/features/layersSelector/search/index.tsx
index 6ba7163692..1289ba036a 100644
--- a/frontend/src/features/layersSelector/search/index.tsx
+++ b/frontend/src/features/layersSelector/search/index.tsx
@@ -86,14 +86,14 @@ export function LayerSearch({ isVisible }) {
setTimeout(() => {
isSearchThrottled.current = false
- if (searchedText.length > 2 || ampTypes.length > 0 || geofilter) {
+ if (searchedText.length > 2 || ampTypes?.length > 0 || geofilter) {
let searchedAMPS
let itemSchema
- if (searchedText.length > 2 || ampTypes.length > 0) {
+ if (searchedText.length > 2 || ampTypes?.length > 0) {
const filterWithTextExpression =
searchedText.length > 0 ? { $path: ['name'], $val: searchedText } : undefined
const filterWithType =
- ampTypes.length > 0 ? { $or: ampTypes.map(theme => ({ $path: 'type', $val: theme })) } : undefined
+ ampTypes?.length > 0 ? { $or: ampTypes.map(theme => ({ $path: 'type', $val: theme })) } : undefined
const filterExpression = [filterWithTextExpression, filterWithType].filter(f => !!f) as Fuse.Expression[]
diff --git a/frontend/src/features/missions/MissionsList/Filters/FilterTags.tsx b/frontend/src/features/missions/MissionsList/Filters/FilterTags.tsx
index 82437c2eb6..80208df08a 100644
--- a/frontend/src/features/missions/MissionsList/Filters/FilterTags.tsx
+++ b/frontend/src/features/missions/MissionsList/Filters/FilterTags.tsx
@@ -38,12 +38,13 @@ export function FilterTags() {
const nextSelectedValues = selectedValues.filter(selectedValue => selectedValue !== valueToDelete) as
| string[]
| number[]
- dispatch(updateFilters({ key: filterKey, value: nextSelectedValues }))
+ dispatch(updateFilters({ key: filterKey, value: nextSelectedValues.length === 0 ? undefined : nextSelectedValues }))
}
return (
- {selectedAdministrationNames.length > 0 &&
+ {selectedAdministrationNames &&
+ selectedAdministrationNames?.length > 0 &&
selectedAdministrationNames.map(admin => (
))}
- {selectedControlUnitIds.length > 0 &&
+ {selectedControlUnitIds &&
+ selectedControlUnitIds?.length > 0 &&
selectedControlUnitIds.map(unit => (
controlUnit.id === unit)?.name || unit}`)}
))}
- {selectedMissionTypes.length > 0 &&
+ {selectedMissionTypes &&
+ selectedMissionTypes?.length > 0 &&
selectedMissionTypes.map(type => (
))}
- {selectedSeaFronts.length > 0 &&
+ {selectedSeaFronts &&
+ selectedSeaFronts?.length > 0 &&
selectedSeaFronts.map(seaFront => (
))}
- {selectedStatuses.length > 0 &&
+ {selectedStatuses &&
+ selectedStatuses?.length > 0 &&
selectedStatuses.map(status => (
))}
- {selectedThemes.length > 0 &&
+ {selectedThemes &&
+ selectedThemes?.length > 0 &&
selectedThemes.map(theme => (
onDeleteTag(theme, MissionFiltersEnum.THEME_FILTER, selectedThemes)}>
{String(`Thème ${theme}`)}
diff --git a/frontend/src/features/missions/MissionsList/Filters/index.tsx b/frontend/src/features/missions/MissionsList/Filters/index.tsx
index 180d9e6642..49d46840f1 100644
--- a/frontend/src/features/missions/MissionsList/Filters/index.tsx
+++ b/frontend/src/features/missions/MissionsList/Filters/index.tsx
@@ -4,10 +4,12 @@ import {
DateRangePicker,
type DateAsStringRange,
useNewWindow,
- getOptionsFromIdAndName
+ getOptionsFromIdAndName,
+ CheckPicker,
+ getOptionsFromLabelledEnum,
+ CustomSearch
} from '@mtes-mct/monitor-ui'
import { type MutableRefObject, useMemo, useRef, useState } from 'react'
-import { CheckPicker } from 'rsuite'
import styled from 'styled-components'
import { FilterTags } from './FilterTags'
@@ -16,7 +18,7 @@ import { RTK_DEFAULT_QUERY_OPTIONS } from '../../../../api/constants'
import { useGetControlThemesQuery } from '../../../../api/controlThemesAPI'
import { useGetLegacyControlUnitsQuery } from '../../../../api/legacyControlUnitsAPI'
import { DateRangeEnum, DATE_RANGE_LABEL } from '../../../../domain/entities/dateRange'
-import { missionSourceEnum, missionStatusLabels, missionTypeEnum } from '../../../../domain/entities/missions'
+import { MissionSourceLabel, MissionTypeLabel, MissionStatusLabel } from '../../../../domain/entities/missions'
import { seaFrontLabels } from '../../../../domain/entities/seaFrontType'
import { MissionFiltersEnum, resetMissionFilters, updateFilters } from '../../../../domain/shared_slices/MissionFilters'
import { useAppDispatch } from '../../../../hooks/useAppDispatch'
@@ -46,26 +48,42 @@ export function MissionsTableFilters() {
const unitPickerRef = useRef() as MutableRefObject
const { data: administrations } = useGetAdministrationsQuery(undefined, RTK_DEFAULT_QUERY_OPTIONS)
- const { data: legacyControlUnits } = useGetLegacyControlUnitsQuery(undefined, RTK_DEFAULT_QUERY_OPTIONS)
+ const { data: legacyControlUnits, isLoading } = useGetLegacyControlUnitsQuery(undefined, RTK_DEFAULT_QUERY_OPTIONS)
const { data: controlThemes } = useGetControlThemesQuery()
- const activeAdministrations = useMemo(() => (administrations || []).filter(isNotArchived), [administrations])
+ const activeAdministrations = useMemo(
+ () =>
+ (administrations || []).filter(isNotArchived).map(admin => ({
+ label: admin.name,
+ value: admin.name
+ })),
+ [administrations]
+ )
const themesAsOptions = useMemo(() => getThemesAsListOptions(controlThemes), [controlThemes])
+ const themeCustomSearch = useMemo(() => new CustomSearch(themesAsOptions, ['label']), [themesAsOptions])
+
const controlUnitsAsOptions = useMemo(() => {
const activeControlUnits = (legacyControlUnits || []).filter(isNotArchived)
- const selectableControlUnits = activeControlUnits.filter(activeControlUnit =>
- selectedAdministrationNames.length ? selectedAdministrationNames.includes(activeControlUnit.administration) : true
+ const selectableControlUnits = activeControlUnits?.filter(activeControlUnit =>
+ selectedAdministrationNames?.length
+ ? selectedAdministrationNames.includes(activeControlUnit.administration)
+ : true
)
return getOptionsFromIdAndName(selectableControlUnits) || []
}, [legacyControlUnits, selectedAdministrationNames])
+ const controlUnitCustomSearch = useMemo(
+ () => new CustomSearch(controlUnitsAsOptions, ['label']),
+ [controlUnitsAsOptions]
+ )
+
const dateRangesAsOptions = Object.values(DATE_RANGE_LABEL)
- const missionStatusesAsOptions = Object.values(missionStatusLabels)
- const missionTypesAsOptions = Object.values(missionTypeEnum)
- const missionSourcesAsOptions = Object.values(missionSourceEnum)
+ const missionStatusesAsOptions = getOptionsFromLabelledEnum(MissionStatusLabel)
+ const missionTypesAsOptions = getOptionsFromLabelledEnum(MissionTypeLabel)
+ const missionSourcesAsOptions = getOptionsFromLabelledEnum(MissionSourceLabel)
const seaFrontsAsOptions = Object.values(seaFrontLabels)
const onUpdatePeriodFilter = (nextDateRange: DateRangeEnum | undefined) => {
@@ -143,6 +161,9 @@ export function MissionsTableFilters() {
setIsCustomPeriodVisible(false)
dispatch(resetMissionFilters())
}
+ if (isLoading) {
+ return Chargement
+ }
return (
<>
@@ -173,12 +194,15 @@ export function MissionsTableFilters() {
style={tagPickerStyle}
value={selectedMissionSource}
/>
-
selectedAdministrationNames && (
@@ -186,83 +210,83 @@ export function MissionsTableFilters() {
)
}
searchable
- size="sm"
style={tagPickerStyle}
value={selectedAdministrationNames}
- valueKey="name"
/>
- onUpdateSimpleFilter(value, MissionFiltersEnum.UNIT_FILTER)}
+ options={controlUnitsAsOptions as any}
placeholder="Unité"
renderValue={() =>
selectedControlUnitIds && {`Unité (${selectedControlUnitIds.length})`}
}
- searchable
size="sm"
style={tagPickerStyle}
value={selectedControlUnitIds}
- valueKey="value"
/>
- onUpdateSimpleFilter(value, MissionFiltersEnum.TYPE_FILTER)}
+ options={missionTypesAsOptions}
placeholder="Type de mission"
renderValue={() =>
selectedMissionTypes && {`Type (${selectedMissionTypes.length})`}
}
- searchable={false}
size="sm"
style={tagPickerStyle}
value={selectedMissionTypes}
- valueKey="code"
/>
- onUpdateSimpleFilter(value, MissionFiltersEnum.SEA_FRONT_FILTER)}
+ options={seaFrontsAsOptions}
placeholder="Facade"
renderValue={() => selectedSeaFronts && {`Facade (${selectedSeaFronts.length})`}}
- searchable={false}
size="sm"
style={tagPickerStyle}
value={selectedSeaFronts}
- valueKey="value"
/>
- onUpdateSimpleFilter(value, MissionFiltersEnum.STATUS_FILTER)}
+ options={missionStatusesAsOptions}
placeholder="Statut"
renderValue={() => selectedStatuses && {`Statut (${selectedStatuses.length})`}}
- searchable={false}
size="sm"
style={tagPickerStyle}
value={selectedStatuses}
- valueKey="code"
/>
- onUpdateSimpleFilter(value, MissionFiltersEnum.THEME_FILTER)}
+ options={themesAsOptions}
placeholder="Thématique"
renderValue={() => selectedThemes && {`Theme (${selectedThemes.length})`}}
size="sm"
style={tagPickerStyle}
value={selectedThemes}
- valueKey="value"
/>
@@ -320,11 +344,7 @@ const StyledSelect = styled(Select)`
top: 5px !important;
}
`
-const StyledCheckPicker = styled(CheckPicker)`
- .rs-picker-toggle-placeholder {
- font-size: 13px !important;
- }
-`
+
const StyledTagsContainer = styled.div`
align-items: baseline;
display: flex;
diff --git a/frontend/src/hooks/useGetFilteredMissionsQuery.ts b/frontend/src/hooks/useGetFilteredMissionsQuery.ts
index 55c524ed1d..e5f62ba32f 100644
--- a/frontend/src/hooks/useGetFilteredMissionsQuery.ts
+++ b/frontend/src/hooks/useGetFilteredMissionsQuery.ts
@@ -43,9 +43,9 @@ export const useGetFilteredMissionsQuery = () => {
}
if (
- selectedAdministrationNames.length === 0 &&
- selectedControlUnitIds.length === 0 &&
- selectedThemes.length === 0
+ selectedAdministrationNames?.length === 0 &&
+ selectedControlUnitIds?.length === 0 &&
+ selectedThemes?.length === 0
) {
return missions
}