diff --git a/frontend/src/domain/entities/vessel/types.ts b/frontend/src/domain/entities/vessel/types.ts index 412b766019..ffd89f0a1b 100644 --- a/frontend/src/domain/entities/vessel/types.ts +++ b/frontend/src/domain/entities/vessel/types.ts @@ -229,10 +229,13 @@ export type TrackTypeRecordItem = { export type VesselEnhancedLastPositionWebGLObject = { coordinates: number[] + course: number filterPreview: number // 0 is False, 1 is True - for WebGL hasBeaconMalfunction: boolean + isAtPort: boolean isFiltered: number // 0 is False, 1 is True - for WebGL lastPositionSentAt: number + speed: number vesselFeatureId: VesselFeatureId vesselProperties: Vessel.VesselEnhancedObject } diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/domain/shared_slices/Global.ts index 727b616303..40c18de283 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/domain/shared_slices/Global.ts @@ -75,10 +75,6 @@ export const globalSlice = createSlice({ window.localStorage.setItem(lastSearchedVesselsLocalStorageKey, JSON.stringify(state.lastSearchedVessels)) }, - closeVesselListModal(state) { - state.vesselListModalIsOpen = false - }, - contractRightMenu(state) { state.rightMenuIsOpen = false }, @@ -178,10 +174,8 @@ export const globalSliceReducer = globalSlice.reducer export const { addSearchedVessel, - closeVesselListModal, contractRightMenu, expandRightMenu, - openVesselListModal, removeError, resetIsUpdatingVessels, setBlockVesselsUpdate, diff --git a/frontend/src/domain/use_cases/vessel/addVesselListFilterZone.ts b/frontend/src/domain/use_cases/vessel/addVesselListFilterZone.ts index a641139cf5..bb8db0ef2e 100644 --- a/frontend/src/domain/use_cases/vessel/addVesselListFilterZone.ts +++ b/frontend/src/domain/use_cases/vessel/addVesselListFilterZone.ts @@ -16,18 +16,3 @@ export const addVesselListFilterZone = (interactionType: InteractionType) => dis }) ) } - -export const closeDrawLayerModal = dispatch => { - dispatch( - setDisplayedComponents({ - isDrawLayerModalDisplayed: false, - isInterestPointMapButtonDisplayed: true, - isMeasurementMapButtonDisplayed: true, - isVesselFiltersMapButtonDisplayed: true, - isVesselLabelsMapButtonDisplayed: true, - isVesselListDisplayed: true, - isVesselSearchDisplayed: true, - isVesselVisibilityMapButtonDisplayed: true - }) - ) -} diff --git a/frontend/src/domain/use_cases/vessel/showVessel.ts b/frontend/src/domain/use_cases/vessel/showVessel.ts index f5c4b47e7f..66fcd98e51 100644 --- a/frontend/src/domain/use_cases/vessel/showVessel.ts +++ b/frontend/src/domain/use_cases/vessel/showVessel.ts @@ -1,6 +1,6 @@ import { getVesselFromAPI } from '@api/vessel' import { logbookActions } from '@features/Logbook/slice' -import { loadingVessel, resetLoadingVessel, setSelectedVessel, vesselsAdapter } from '@features/Vessel/slice' +import { loadingVessel, resetLoadingVessel, setSelectedVessel, vesselSelectors } from '@features/Vessel/slice' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { captureMessage } from '@sentry/react' @@ -23,8 +23,7 @@ export const showVessel = try { const { fishingActivities, map, vessel } = getState() const { selectedVesselTrackRequest } = vessel - const vesselsSelector = vessel.vessels - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = vesselSelectors.selectAll(getState()) const { defaultVesselTrackDepth } = map const { areFishingActivitiesShowedOnMap } = fishingActivities // TODO How to handle both the control unit dialog and the vessel sidebar ? diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/SaveVesselFiltersModal.tsx b/frontend/src/features/Filter/components/SaveVesselFiltersModal.tsx similarity index 92% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/SaveVesselFiltersModal.tsx rename to frontend/src/features/Filter/components/SaveVesselFiltersModal.tsx index 07dd16d12c..f648ee31c5 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/SaveVesselFiltersModal.tsx +++ b/frontend/src/features/Filter/components/SaveVesselFiltersModal.tsx @@ -1,28 +1,29 @@ import { COLORS } from '@constants/constants' import { StyledModalHeader } from '@features/commonComponents/StyledModalHeader' -import { TextInput, Icon, Size } from '@mtes-mct/monitor-ui' +import { addFilter } from '@features/Filter/useCases/addFilter' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { Icon, Size, TextInput } from '@mtes-mct/monitor-ui' import { useState } from 'react' import { CirclePicker } from 'react-color' import { Modal } from 'rsuite' import styled from 'styled-components' import { v4 as uuidv4 } from 'uuid' -import { TagList } from './TagList' +import { TagList } from './VesselFilters/TagList' type SaveVesselFiltersModalProps = Readonly<{ - addFilter: (filter: { color: string; filters: any; name: string; showed: boolean; uuid: string }) => void closeAndResetVesselList: () => void filters: any isOpen: boolean setIsOpen: (isOpen: boolean) => void }> export function SaveVesselFiltersModal({ - addFilter, closeAndResetVesselList, filters, isOpen, setIsOpen }: SaveVesselFiltersModalProps) { + const dispatch = useMainAppDispatch() const [filterName, setFilterName] = useState(undefined) const [filterColor, setFilterColor] = useState('#2c6e49') @@ -39,7 +40,7 @@ export function SaveVesselFiltersModal({ uuid: uuidv4() } - addFilter(filter) + dispatch(addFilter(filter)) setIsOpen(false) setFilterName('') diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filter.tsx b/frontend/src/features/Filter/components/VesselFilters/Filter.tsx similarity index 72% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filter.tsx rename to frontend/src/features/Filter/components/VesselFilters/Filter.tsx index 68d8d0ff53..7002fb65fe 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filter.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/Filter.tsx @@ -1,38 +1,27 @@ import { COLORS } from '@constants/constants' +import { hideAllFilters } from '@features/Filter/useCases/hideAllFilters' +import { removeFilter } from '@features/Filter/useCases/removeFilter' +import { showFilter } from '@features/Filter/useCases/showFilter' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useState } from 'react' import styled from 'styled-components' import { TagList } from './TagList' -import ChevronIconSVG from '../../../../icons/Chevron_simple_gris.svg?react' -import CloseIconSVG from '../../../../icons/Croix_grise.svg?react' -import FilterSVG from '../../../../icons/Icone_filtres_dark.svg?react' -import ShowIconSVG from '../../../../icons/oeil_affiche.svg?react' -import HideIconSVG from '../../../../icons/oeil_masque.svg?react' +import ChevronIconSVG from '../../../icons/Chevron_simple_gris.svg?react' +import CloseIconSVG from '../../../icons/Croix_grise.svg?react' +import FilterSVG from '../../../icons/Icone_filtres_dark.svg?react' +import ShowIconSVG from '../../../icons/oeil_affiche.svg?react' +import HideIconSVG from '../../../icons/oeil_masque.svg?react' -import type { VesselFilter } from 'domain/types/filter' +import type { VesselFilter } from '../../types' type FilterProps = Readonly<{ filter: VesselFilter - hideFilters: () => void index: number isLastItem: boolean - removeFilter: (uuid: string) => void - removeTagFromFilter: (removeObject: { - type: string | undefined - uuid: string | undefined - value: number | string - }) => void - showFilter: (uuid: string) => void }> -export function Filter({ - filter, - hideFilters, - index, - isLastItem, - removeFilter, - removeTagFromFilter, - showFilter -}: FilterProps) { +export function Filter({ filter, index, isLastItem }: FilterProps) { + const dispatch = useMainAppDispatch() const [isOpen, setIsOpen] = useState(false) return ( @@ -44,14 +33,14 @@ export function Filter({ {filter.name ? filter.name.replace(/_/g, ' ') : `Filtre n°${index}`} {filter.showed ? ( - hideFilters()} title="Cacher le filtre" /> + dispatch(hideAllFilters())} title="Cacher le filtre" /> ) : ( - showFilter(filter.uuid)} title="Afficher le filtre" /> + dispatch(showFilter(filter.uuid))} title="Afficher le filtre" /> )} - removeFilter(filter.uuid)} title="Supprimer le filtre de ma sélection" /> + dispatch(removeFilter(filter.uuid))} title="Supprimer le filtre de ma sélection" /> - + ) diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterParameters.tsx b/frontend/src/features/Filter/components/VesselFilters/FilterParameters.tsx similarity index 66% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterParameters.tsx rename to frontend/src/features/Filter/components/VesselFilters/FilterParameters.tsx index 55e756bfc2..556d698594 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterParameters.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/FilterParameters.tsx @@ -1,20 +1,16 @@ +import { hideOrShowNonFilteredVessels } from '@features/Filter/useCases/hideOrShowNonFilteredVessels' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { useMainAppSelector } from '@hooks/useMainAppSelector' import styled from 'styled-components' -import { setDisplayedComponents } from '../../../../../domain/shared_slices/DisplayedComponent' -import { MapPropertyTrigger } from '../../../../commonComponents/MapPropertyTrigger' -import HidingOtherVesselsSVG from '../../../../icons/Bouton_masquer_pistes_actif.svg?react' -import ShowingOtherVesselsSVG from '../../../../icons/Bouton_masquer_pistes_inactif.svg?react' +import { setDisplayedComponents } from '../../../../domain/shared_slices/DisplayedComponent' +import { MapPropertyTrigger } from '../../../commonComponents/MapPropertyTrigger' +import HidingOtherVesselsSVG from '../../../icons/Bouton_masquer_pistes_actif.svg?react' +import ShowingOtherVesselsSVG from '../../../icons/Bouton_masquer_pistes_inactif.svg?react' -type FilterParametersProps = Readonly<{ - nonFilteredVesselsAreHidden: boolean - setNonFilteredVesselsAreHidden: (isChecked: boolean) => void -}> -export function FilterParameters({ - nonFilteredVesselsAreHidden, - setNonFilteredVesselsAreHidden -}: FilterParametersProps) { +export function FilterParameters() { const dispatch = useMainAppDispatch() + const nonFilteredVesselsAreHidden = useMainAppSelector(state => state.filter.nonFilteredVesselsAreHidden) const handleCreateFilter = () => { dispatch( @@ -35,7 +31,7 @@ export function FilterParameters({ Icon={nonFilteredVesselsAreHidden ? ShowingOtherVesselsSVG : HidingOtherVesselsSVG} inverse text="les autres navires" - updateBooleanProperty={isChecked => setNonFilteredVesselsAreHidden(isChecked)} + updateBooleanProperty={isChecked => dispatch(hideOrShowNonFilteredVessels(isChecked))} /> ) diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterTag.tsx b/frontend/src/features/Filter/components/VesselFilters/FilterTag.tsx similarity index 66% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterTag.tsx rename to frontend/src/features/Filter/components/VesselFilters/FilterTag.tsx index e1ce06e956..415e746894 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/FilterTag.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/FilterTag.tsx @@ -1,26 +1,18 @@ +import { removeTagFromFilter } from '@features/Filter/useCases/removeTagFromFilter' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Icon, THEME } from '@mtes-mct/monitor-ui' import styled from 'styled-components' +import type { FilterTag as FilterTagType } from '@features/Filter/types' + type FilterTagProps = Readonly<{ iconElement: JSX.Element | undefined - removeTagFromFilter: - | ((removeObject: { type: string | undefined; uuid: string | undefined; value: number | string }) => void) - | undefined + tag: FilterTagType text: string - type: string | undefined uuid: string | undefined - value: number | string }> -export function FilterTag({ iconElement, removeTagFromFilter, text, type, uuid, value }: FilterTagProps) { - const callRemoveTagFromFilter = () => { - if (!removeTagFromFilter) { - return - } - - const removeObject = { type, uuid, value } - - removeTagFromFilter(removeObject) - } +export function FilterTag({ iconElement, tag, text, uuid }: FilterTagProps) { + const dispatch = useMainAppDispatch() return ( @@ -29,7 +21,7 @@ export function FilterTag({ iconElement, removeTagFromFilter, text, type, uuid, dispatch(removeTagFromFilter({ ...tag, uuid }))} size={10} /> diff --git a/frontend/src/features/Filter/components/VesselFilters/Filters.tsx b/frontend/src/features/Filter/components/VesselFilters/Filters.tsx new file mode 100644 index 0000000000..b5b411b60d --- /dev/null +++ b/frontend/src/features/Filter/components/VesselFilters/Filters.tsx @@ -0,0 +1,67 @@ +import { COLORS } from '@constants/constants' +import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { useMemo } from 'react' +import styled from 'styled-components' + +import { Filter } from './Filter' +import { FilterParameters } from './FilterParameters' +import { MapBox } from '../../../../domain/entities/map/constants' +import { MapToolBox } from '../../../MainWindow/components/MapButtons/shared/MapToolBox' + +export function Filters() { + const filters = useMainAppSelector(state => state.filter.filters) + const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) + + const isOpen = useMemo(() => rightMapBoxOpened === MapBox.FILTERS, [rightMapBoxOpened]) + + return ( + +
Mes filtres
+ {filters.length > 0 ? ( + + {filters.map((filter, index) => ( + + ))} + + ) : ( + Aucun filtre + )} + +
+ ) +} + +const FiltersSelectedList = styled.ul` + margin: 0; + background-color: ${COLORS.white}; + border-radius: 0; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + padding: 0; + max-height: 550px; + overflow-x: hidden; + color: ${COLORS.gunMetal}; +` + +const LastPositionInfo = styled.div` + font-size: 13px; + margin: 15px; + color: ${COLORS.gunMetal}; +` + +const Header = styled.div<{ + $isFirst: boolean +}>` + background: ${COLORS.charcoal}; + color: ${COLORS.gainsboro}; + padding: 9px 0 7px 15px; + font-size: 16px; + text-align: left; + border-top-left-radius: ${p => (p.$isFirst ? '2px' : '0')}; + border-top-right-radius: ${p => (p.$isFirst ? '2px' : '0')}; +` + +const VesselFilterBox = styled(MapToolBox)` + width: 305px; + top: 124px; +` diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagIconType.tsx b/frontend/src/features/Filter/components/VesselFilters/TagIconType.tsx similarity index 70% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagIconType.tsx rename to frontend/src/features/Filter/components/VesselFilters/TagIconType.tsx index 7fc8e5bc96..37b3844476 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagIconType.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/TagIconType.tsx @@ -1,12 +1,12 @@ import styled from 'styled-components' -import FilterControlSVG from '../../../../icons/Label_controle.svg?react' -import FilterGearSVG from '../../../../icons/Label_engin_de_peche.svg?react' -import FilterSpeciesSVG from '../../../../icons/Label_poisson.svg?react' -import FilterDistrictSVG from '../../../../icons/Label_quartier.svg?react' -import FilterFleetSegmentSVG from '../../../../icons/Label_segment_de_flotte.svg?react' -import FilterLengthSVG from '../../../../icons/Label_taille_navire.svg?react' -import FilterZoneSVG from '../../../../icons/Label_zone.svg?react' +import FilterControlSVG from '../../../icons/Label_controle.svg?react' +import FilterGearSVG from '../../../icons/Label_engin_de_peche.svg?react' +import FilterSpeciesSVG from '../../../icons/Label_poisson.svg?react' +import FilterDistrictSVG from '../../../icons/Label_quartier.svg?react' +import FilterFleetSegmentSVG from '../../../icons/Label_segment_de_flotte.svg?react' +import FilterLengthSVG from '../../../icons/Label_taille_navire.svg?react' +import FilterZoneSVG from '../../../icons/Label_zone.svg?react' const Zone = styled(FilterZoneSVG)` height: 13px; diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagList.tsx b/frontend/src/features/Filter/components/VesselFilters/TagList.tsx similarity index 85% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagList.tsx rename to frontend/src/features/Filter/components/VesselFilters/TagList.tsx index 72e79cf538..70e5cf2039 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/TagList.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/TagList.tsx @@ -6,27 +6,22 @@ import styled from 'styled-components' import { FilterTag } from './FilterTag' import { IconTypes } from './TagIconType' -import { vesselSize } from '../../../../../domain/entities/vessel/vessel' +import { vesselSize } from '../../../../domain/entities/vessel/vessel' -import type { FilterValues } from 'domain/types/filter' +import type { FilterValues } from '../../types' type TagListProps = Readonly<{ className?: string | undefined filters: FilterValues - removeTagFromFilter?: (removeObject: { - type: string | undefined - uuid: string | undefined - value: number | string - }) => void uuid?: string }> -export function TagList({ className, filters, removeTagFromFilter, uuid }: TagListProps) { +export function TagList({ className, filters, uuid }: TagListProps) { const [tags, setTags] = useState< Array<{ iconElement: JSX.Element text: string type: string - value: number | string + value: string | number }> >([]) @@ -35,7 +30,7 @@ export function TagList({ className, filters, removeTagFromFilter, uuid }: TagLi iconElement: JSX.Element text: string type: string - value: number | string + value: string | number }> = [] if (filters.countriesFiltered?.length) { @@ -140,15 +135,7 @@ export function TagList({ className, filters, removeTagFromFilter, uuid }: TagLi {tags?.length ? ( <> {tags.map(tag => ( - + ))} ) : ( diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx b/frontend/src/features/Filter/components/VesselFilters/index.tsx similarity index 90% rename from frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx rename to frontend/src/features/Filter/components/VesselFilters/index.tsx index 4807a10253..3a04212694 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx +++ b/frontend/src/features/Filter/components/VesselFilters/index.tsx @@ -6,10 +6,10 @@ import { useCallback, useMemo, useRef } from 'react' import styled from 'styled-components' import { Filters } from './Filters' -import { MapBox } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' -import FilterSVG from '../../../../icons/standardized/Filter.svg?react' -import { MapToolButton } from '../shared/MapToolButton' +import { MapBox } from '../../../../domain/entities/map/constants' +import { setRightMapBoxOpened } from '../../../../domain/shared_slices/Global' +import FilterSVG from '../../../icons/standardized/Filter.svg?react' +import { MapToolButton } from '../../../MainWindow/components/MapButtons/shared/MapToolButton' export function VesselFiltersMapButton() { const dispatch = useMainAppDispatch() diff --git a/frontend/src/features/Vessel/layers/FilterLayer.jsx b/frontend/src/features/Filter/layers/FilterLayer.jsx similarity index 100% rename from frontend/src/features/Vessel/layers/FilterLayer.jsx rename to frontend/src/features/Filter/layers/FilterLayer.jsx diff --git a/frontend/src/domain/shared_slices/Filter.ts b/frontend/src/features/Filter/slice.ts similarity index 55% rename from frontend/src/domain/shared_slices/Filter.ts rename to frontend/src/features/Filter/slice.ts index ca89d7f7df..015fabca05 100644 --- a/frontend/src/domain/shared_slices/Filter.ts +++ b/frontend/src/features/Filter/slice.ts @@ -2,83 +2,66 @@ import { createSlice } from '@reduxjs/toolkit' import { getLocalStorageState } from '../../utils' -import type { VesselFilter } from '../types/filter' +import type { FilterTag, VesselFilter } from './types' import type { PayloadAction } from '@reduxjs/toolkit' -const vesselsFiltersLocalStorageKey = 'vesselsFilters' -const nonFilteredVesselsAreHiddenLocalStorageKey = 'nonFilteredVesselsAreHidden' +/** + * @deprecated Replaced by `persistReducer`. + * See and `reducer.ts` and `MAIN_PERSISTOR_FILTER_MIGRATIONS` + */ +export const vesselsFiltersLocalStorageKey = 'vesselsFilters' + +/** + * @deprecated Replaced by `persistReducer`. + * See and `reducer.ts` and `MAIN_PERSISTOR_FILTER_MIGRATIONS` + */ +export const nonFilteredVesselsAreHiddenLocalStorageKey = 'nonFilteredVesselsAreHidden' export type FilterState = { filters: VesselFilter[] nonFilteredVesselsAreHidden: boolean } -const INITIAL_STATE: FilterState = { +export const INITIAL_STATE: FilterState = { + // TODO Remove after init of `persistReducer` (used for migration from localstorage). filters: getLocalStorageState([], vesselsFiltersLocalStorageKey), + // TODO Remove after init of `persistReducer` (used for migration from localstorage). nonFilteredVesselsAreHidden: getLocalStorageState(false, nonFilteredVesselsAreHiddenLocalStorageKey) } - const filterSlice = createSlice({ initialState: INITIAL_STATE, name: 'filter', reducers: { - /** - * Add a new filter - * @function addFilter - * @memberOf FilterReducer - * @param {Object=} state - * @param {{payload: VesselFilter}} action - The filter to add - */ - addFilter(state, action) { + filterAdded(state, action: PayloadAction) { state.filters = state.filters.map(filter => { filter.showed = false return filter }) state.filters = state.filters.concat(action.payload) - window.localStorage.setItem(vesselsFiltersLocalStorageKey, JSON.stringify(state.filters)) }, - /** - * Hide all filters - * @function hideFilters - * @memberOf FilterReducer - * @param {Object=} state - */ - hideFilters(state) { + filterRemoved(state, action: PayloadAction) { + const uuidToRemove = action.payload + state.filters = state.filters.filter(filter => filter.uuid !== uuidToRemove) + }, + + filtersHidden(state) { state.filters = state.filters.map(filter => ({ ...filter, showed: false })) - window.localStorage.setItem(vesselsFiltersLocalStorageKey, JSON.stringify(state.filters)) + // prevents no filters shown & nonFilteredVesselsAreHidden = true leading to empty map state.nonFilteredVesselsAreHidden = false - window.localStorage.setItem( - nonFilteredVesselsAreHiddenLocalStorageKey, - JSON.stringify(state.nonFilteredVesselsAreHidden) - ) }, - /** - * Delete a given filter - * @function removeFilter - * @memberOf FilterReducer - * @param {Object=} state - * @param {{payload: string}} action - The filter UUID - */ - removeFilter(state, action) { - const uuidToRemove = action.payload - state.filters = state.filters.filter(filter => filter.uuid !== uuidToRemove) - window.localStorage.setItem(vesselsFiltersLocalStorageKey, JSON.stringify(state.filters)) + filterShowed(state, action: PayloadAction) { + const uuidToShow = action.payload + + state.filters = state.filters.map(filter => ({ ...filter, showed: filter.uuid === uuidToShow })) }, /** * Remove tag from a given filter and delete filter if the filter contains no tag */ - removeTagFromFilter( - state, - action: PayloadAction<{ - type: string - uuid: string - value: string - }> - ) { + filterTagRemoved(state, action: PayloadAction) { const filterUUID = action.payload.uuid const tagType = action.payload.type const tagValue = action.payload.value @@ -110,41 +93,21 @@ const filterSlice = createSlice({ return [...filtersPile, filter as VesselFilter] }, []) - - window.localStorage.setItem(vesselsFiltersLocalStorageKey, JSON.stringify(state.filters)) }, - /** - * Hide non filtered vessels - * @function setNonFilteredVesselsAreHidden - * @memberOf FilterReducer - * @param {Object=} state - * @param {{ - * payload: boolean - * }} action - The boolean - */ - setNonFilteredVesselsAreHidden(state, action) { + setNonFilteredVesselsAreHidden(state, action: PayloadAction) { state.nonFilteredVesselsAreHidden = action.payload - window.localStorage.setItem(nonFilteredVesselsAreHiddenLocalStorageKey, JSON.stringify(action.payload)) - }, - - /** - * Show a given filter - * @function showFilter - * @memberOf FilterReducer - * @param {Object=} state - * @param {{payload: string}} action - The filter UUID - */ - showFilter(state, action) { - const uuidToShow = action.payload - - state.filters = state.filters.map(filter => ({ ...filter, showed: filter.uuid === uuidToShow })) - window.localStorage.setItem(vesselsFiltersLocalStorageKey, JSON.stringify(state.filters)) } } }) -export const { addFilter, hideFilters, removeFilter, removeTagFromFilter, setNonFilteredVesselsAreHidden, showFilter } = - filterSlice.actions +export const { + filterAdded, + filterRemoved, + filtersHidden, + filterShowed, + filterTagRemoved, + setNonFilteredVesselsAreHidden +} = filterSlice.actions export const filterReducer = filterSlice.reducer diff --git a/frontend/src/domain/types/filter.ts b/frontend/src/features/Filter/types.ts similarity index 76% rename from frontend/src/domain/types/filter.ts rename to frontend/src/features/Filter/types.ts index 1e0327750d..13fad4b469 100644 --- a/frontend/src/domain/types/filter.ts +++ b/frontend/src/features/Filter/types.ts @@ -1,4 +1,4 @@ -import type { GeoJSON } from './GeoJSON' +import type { GeoJSON } from '../../domain/types/GeoJSON' export type VesselFilter = { color: string @@ -24,3 +24,9 @@ export type ZoneSelected = { feature: GeoJSON.Feature name: string } + +export type FilterTag = { + type: string + uuid?: string | undefined + value: string | number +} diff --git a/frontend/src/features/Filter/useCases/addFilter.ts b/frontend/src/features/Filter/useCases/addFilter.ts new file mode 100644 index 0000000000..4d42c4b95e --- /dev/null +++ b/frontend/src/features/Filter/useCases/addFilter.ts @@ -0,0 +1,16 @@ +import { filterAdded } from '@features/Filter/slice' +import { applyFilterToVessels } from '@features/Vessel/useCases/applyFilterToVessels' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import type { VesselFilter } from '@features/Filter/types' +import type { MainAppThunk } from '@store' + +export const addFilter = + (filter: VesselFilter): MainAppThunk => + async dispatch => { + await dispatch(filterAdded(filter)) + + await dispatch(applyFilterToVessels()) + + dispatch(renderVessels()) + } diff --git a/frontend/src/features/Filter/useCases/hideAllFilters.ts b/frontend/src/features/Filter/useCases/hideAllFilters.ts new file mode 100644 index 0000000000..91a63b362a --- /dev/null +++ b/frontend/src/features/Filter/useCases/hideAllFilters.ts @@ -0,0 +1,19 @@ +import { filtersHidden } from '@features/Filter/slice' +import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/constants' +import { applyFilterToVessels } from '@features/Vessel/useCases/applyFilterToVessels' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import { booleanToInt } from '../../../utils' + +import type { MainAppThunk } from '@store' + +export const hideAllFilters = (): MainAppThunk => async dispatch => { + await dispatch(filtersHidden()) + await dispatch(applyFilterToVessels()) + + VESSELS_VECTOR_LAYER.updateStyleVariables({ + nonFilteredVesselsAreHidden: booleanToInt(false) + }) + + dispatch(renderVessels()) +} diff --git a/frontend/src/features/Filter/useCases/hideOrShowNonFilteredVessels.ts b/frontend/src/features/Filter/useCases/hideOrShowNonFilteredVessels.ts new file mode 100644 index 0000000000..26e09b04d7 --- /dev/null +++ b/frontend/src/features/Filter/useCases/hideOrShowNonFilteredVessels.ts @@ -0,0 +1,19 @@ +import { setNonFilteredVesselsAreHidden } from '@features/Filter/slice' +import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/constants' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import { booleanToInt } from '../../../utils' + +import type { MainAppThunk } from '@store' + +export const hideOrShowNonFilteredVessels = + (areHidden: boolean): MainAppThunk => + async dispatch => { + await dispatch(setNonFilteredVesselsAreHidden(areHidden)) + + VESSELS_VECTOR_LAYER.updateStyleVariables({ + nonFilteredVesselsAreHidden: booleanToInt(areHidden) + }) + + dispatch(renderVessels()) + } diff --git a/frontend/src/features/Filter/useCases/removeFilter.ts b/frontend/src/features/Filter/useCases/removeFilter.ts new file mode 100644 index 0000000000..65feb6d9e2 --- /dev/null +++ b/frontend/src/features/Filter/useCases/removeFilter.ts @@ -0,0 +1,15 @@ +import { filterRemoved } from '@features/Filter/slice' +import { applyFilterToVessels } from '@features/Vessel/useCases/applyFilterToVessels' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import type { MainAppThunk } from '@store' + +export const removeFilter = + (filterUUID: string): MainAppThunk => + async dispatch => { + await dispatch(filterRemoved(filterUUID)) + + await dispatch(applyFilterToVessels()) + + dispatch(renderVessels()) + } diff --git a/frontend/src/features/Filter/useCases/removeTagFromFilter.ts b/frontend/src/features/Filter/useCases/removeTagFromFilter.ts new file mode 100644 index 0000000000..eaf65bbf21 --- /dev/null +++ b/frontend/src/features/Filter/useCases/removeTagFromFilter.ts @@ -0,0 +1,26 @@ +import { filterTagRemoved } from '@features/Filter/slice' +import { applyFilterToVessels } from '@features/Vessel/useCases/applyFilterToVessels' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import type { FilterTag } from '@features/Filter/types' +import type { MainAppThunk } from '@store' + +export const removeTagFromFilter = + (tag: FilterTag): MainAppThunk => + async dispatch => { + if (!tag.uuid) { + return + } + + await dispatch( + filterTagRemoved({ + type: tag.type, + uuid: tag.uuid, + value: tag.value + }) + ) + + await dispatch(applyFilterToVessels()) + + dispatch(renderVessels()) + } diff --git a/frontend/src/features/Filter/useCases/showFilter.ts b/frontend/src/features/Filter/useCases/showFilter.ts new file mode 100644 index 0000000000..cd455331bd --- /dev/null +++ b/frontend/src/features/Filter/useCases/showFilter.ts @@ -0,0 +1,28 @@ +import { filterShowed } from '@features/Filter/slice' +import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/constants' +import { applyFilterToVessels } from '@features/Vessel/useCases/applyFilterToVessels' +import { renderVessels } from '@features/Vessel/useCases/renderVessels' + +import { customHexToRGB } from '../../../utils' + +import type { MainAppThunk } from '@store' + +export const showFilter = + (filterUUID: string): MainAppThunk => + async (dispatch, getState) => { + await dispatch(filterShowed(filterUUID)) + const showedFilter = getState().filter?.filters?.find(filter => filter.showed) + + await dispatch(applyFilterToVessels()) + + if (showedFilter?.color) { + const [red, green, blue] = customHexToRGB(showedFilter?.color) + VESSELS_VECTOR_LAYER.updateStyleVariables({ + filterColorBlue: blue, + filterColorGreen: green, + filterColorRed: red + }) + } + + dispatch(renderVessels()) + } diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx deleted file mode 100644 index 31eb8a0b3c..0000000000 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { COLORS } from '@constants/constants' -import { useMainAppDispatch } from '@hooks/useMainAppDispatch' -import { useMainAppSelector } from '@hooks/useMainAppSelector' -import { useCallback, useMemo } from 'react' -import styled from 'styled-components' - -import { Filter } from './Filter' -import { FilterParameters } from './FilterParameters' -import { MapBox } from '../../../../../domain/entities/map/constants' -import { - hideFilters, - removeFilter, - removeTagFromFilter, - setNonFilteredVesselsAreHidden, - showFilter -} from '../../../../../domain/shared_slices/Filter' -import { MapToolBox } from '../shared/MapToolBox' - -export function Filters() { - const dispatch = useMainAppDispatch() - const { filters, nonFilteredVesselsAreHidden } = useMainAppSelector(state => state.filter) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - - const isOpen = useMemo(() => rightMapBoxOpened === MapBox.FILTERS, [rightMapBoxOpened]) - - const removeFilterCallback = useCallback( - filterUUID => { - dispatch(removeFilter(filterUUID)) - }, - [dispatch] - ) - - const showFilterCallback = useCallback( - filterUUID => { - dispatch(showFilter(filterUUID)) - }, - [dispatch] - ) - - const hideFiltersCallback = useCallback(() => { - dispatch(hideFilters()) - }, [dispatch]) - - const removeTagFromFilterCallback = useCallback( - removeObject => { - dispatch(removeTagFromFilter(removeObject)) - }, - [dispatch] - ) - - const setNonFilteredVesselsAreHiddenCallback = useCallback( - areHidden => { - dispatch(setNonFilteredVesselsAreHidden(areHidden)) - }, - [dispatch] - ) - - return ( - -
Mes filtres
- {filters.length > 0 ? ( - - {filters.map((filter, index) => ( - - ))} - - ) : ( - Aucun filtre - )} - -
- ) -} - -const FiltersSelectedList = styled.ul` - margin: 0; - background-color: ${COLORS.white}; - border-radius: 0; - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - padding: 0; - max-height: 550px; - overflow-x: hidden; - color: ${COLORS.gunMetal}; -` - -const LastPositionInfo = styled.div` - font-size: 13px; - margin: 15px; - color: ${COLORS.gunMetal}; -` - -const Header = styled.div<{ - $isFirst: boolean -}>` - background: ${COLORS.charcoal}; - color: ${COLORS.gainsboro}; - padding: 9px 0 7px 15px; - font-size: 16px; - text-align: left; - border-top-left-radius: ${p => (p.$isFirst ? '2px' : '0')}; - border-top-right-radius: ${p => (p.$isFirst ? '2px' : '0')}; -` - -const VesselFilterBox = styled(MapToolBox)` - width: 305px; - top: 124px; -` diff --git a/frontend/src/features/MainWindow/components/MapButtons/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/index.tsx index d15f291697..386d1d95c1 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/index.tsx @@ -7,10 +7,10 @@ import { AlertsMapButton } from './AlertsMapButton' import { BeaconMalfunctionsMapButton } from './BeaconMalfunctionsMapButton' import { FavoriteVessels } from './FavoriteVessels' import { MissionsMenu } from './Missions' -import { VesselFiltersMapButton } from './VesselFilters' import { VesselLabelsMapButton } from './VesselLabels' import { VesselVisibilityMapButton } from './VesselVisibility' import { useIsSuperUser } from '../../../../auth/hooks/useIsSuperUser' +import { VesselFiltersMapButton } from '../../../Filter/components/VesselFilters' import { InterestPointMapButton } from '../../../InterestPoint/components/InterestPointMapButton' import { MeasurementMapButton } from '../../../Measurement/components/MeasurementMapButton' import { PriorNotificationListButton } from '../../../PriorNotification/components/PriorNotificationListButton' diff --git a/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/VesselEstimatedPositionCard.jsx b/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/VesselEstimatedPositionCard.jsx index b4241841f1..3650a00f1b 100644 --- a/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/VesselEstimatedPositionCard.jsx +++ b/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/VesselEstimatedPositionCard.jsx @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components' -import { COLORS } from '@constants/constants.js' +import { COLORS } from '@constants/constants' const VesselEstimatedPositionCard = ({ coordinates }) => { return ( diff --git a/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/index.jsx b/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/index.jsx index 8898a45498..7df6c0ba3a 100644 --- a/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/index.jsx +++ b/frontend/src/features/Vessel/components/VesselEstimatedPositionOverlay/index.jsx @@ -1,13 +1,13 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' import Overlay from 'ol/Overlay' -import { COLORS } from '@constants/constants.js' -import { LayerProperties } from '../../../../domain/entities/layers/constants.js' -import VesselEstimatedPositionCard from './VesselEstimatedPositionCard.jsx' -import { getCoordinates } from '../../../../coordinates.js' -import { WSG84_PROJECTION } from '../../../../domain/entities/map/constants.js' +import { COLORS } from '@constants/constants' +import { LayerProperties } from '../../../../domain/entities/layers/constants' +import VesselEstimatedPositionCard from './VesselEstimatedPositionCard' +import { getCoordinates } from '../../../../coordinates' +import { WSG84_PROJECTION } from '../../../../domain/entities/map/constants' import { useSelector } from 'react-redux' -import { monitorfishMap } from '../../../map/monitorfishMap.js' +import { monitorfishMap } from '../../../map/monitorfishMap' const VesselEstimatedPositionOverlay = ({ pointerMoveEventPixel, feature }) => { const { coordinatesFormat } = useSelector(state => state.map) diff --git a/frontend/src/features/Vessel/components/VesselLoader.tsx b/frontend/src/features/Vessel/components/VesselLoader.tsx index 5856064c38..d0312c0186 100644 --- a/frontend/src/features/Vessel/components/VesselLoader.tsx +++ b/frontend/src/features/Vessel/components/VesselLoader.tsx @@ -1,5 +1,5 @@ import { FulfillingBouncingCircleSpinner } from '@components/FulfillingBouncingCircleSpinner' -import { showVesselsLastPosition } from '@features/Vessel/layers/VesselsLayer/useCases/showVesselsLastPosition' +import { showVesselsLastPosition } from '@features/Vessel/useCases/showVesselsLastPosition' import { useIsInLightMode } from '@hooks/useIsInLightMode' import { skipToken } from '@reduxjs/toolkit/query' import { useEffect, useState } from 'react' diff --git a/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx b/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx index 833aa777c1..861fe24ff2 100644 --- a/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx @@ -4,13 +4,13 @@ import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' +import { LayerProperties } from '../../../domain/entities/layers/constants' -import { getVesselAlertAndBeaconMalfunctionStyle } from './style.js' -import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel.js' -import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser.js' -import { monitorfishMap } from '../../map/monitorfishMap.js' -import { vesselsAdapter } from '../slice.ts' +import { getVesselAlertAndBeaconMalfunctionStyle } from './style' +import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel' +import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' +import { monitorfishMap } from '../../map/monitorfishMap' +import { vesselSelectors } from '../slice' const VesselAlertAndBeaconMalfunctionLayer = () => { const isSuperUser = useIsSuperUser() @@ -19,8 +19,7 @@ const VesselAlertAndBeaconMalfunctionLayer = () => { selectedVesselIdentity, vesselsTracksShowed } = useSelector(state => state.vessel) - const vesselsSelector = useSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useSelector(vesselSelectors.selectAll) const { nonFilteredVesselsAreHidden diff --git a/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx b/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx index a839dca90d..4d38dda033 100644 --- a/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx @@ -4,18 +4,18 @@ import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' +import { LayerProperties } from '../../../domain/entities/layers/constants' -import { getVesselAlertStyle } from './style.js' +import { getVesselAlertStyle } from './style' import { getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed -} from '../../../domain/entities/vessel/vessel.js' -import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser.js' -import { monitorfishMap } from '../../map/monitorfishMap.js' -import { vesselsAdapter } from '../slice.ts' +} from '../../../domain/entities/vessel/vessel' +import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' +import { monitorfishMap } from '../../map/monitorfishMap' +import { vesselsAdapter, vesselSelectors } from '../slice' const VesselAlertLayer = () => { const isSuperUser = useIsSuperUser() @@ -25,8 +25,7 @@ const VesselAlertLayer = () => { selectedVesselIdentity, vesselsTracksShowed } = useSelector(state => state.vessel) - const vesselsSelector = useSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useSelector(vesselSelectors.selectAll) const { nonFilteredVesselsAreHidden diff --git a/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx b/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx index 6ebad40da4..7c570aeb7e 100644 --- a/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx @@ -4,13 +4,13 @@ import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' +import { LayerProperties } from '../../../domain/entities/layers/constants' -import { getVesselBeaconMalfunctionStyle } from './style.js' -import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel.js' -import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser.js' -import { monitorfishMap } from '../../map/monitorfishMap.js' -import { vesselsAdapter } from '../slice.ts' +import { getVesselBeaconMalfunctionStyle } from './style' +import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel' +import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' +import { monitorfishMap } from '../../map/monitorfishMap' +import { vesselsAdapter, vesselSelectors } from '../slice' const VesselBeaconMalfunctionLayer = () => { const isSuperUser = useIsSuperUser() @@ -20,8 +20,7 @@ const VesselBeaconMalfunctionLayer = () => { vesselsTracksShowed, selectedVesselIdentity } = useSelector(state => state.vessel) - const vesselsSelector = useSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useSelector(vesselSelectors.selectAll) const { nonFilteredVesselsAreHidden diff --git a/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx b/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx index 0c4eb6478a..d08b117d70 100644 --- a/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx @@ -1,13 +1,13 @@ import React, { useEffect, useRef } from 'react' import { useSelector } from 'react-redux' import VectorSource from 'ol/source/Vector' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' -import { EstimatedPosition } from '../../../domain/entities/estimatedPosition.js' -import { getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed } from '../../../domain/entities/vessel/vessel.js' +import { LayerProperties } from '../../../domain/entities/layers/constants' +import { EstimatedPosition } from '../../../domain/entities/estimatedPosition' +import { getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { Vector } from 'ol/layer' -import { getEstimatedPositionStyle } from './styles/vesselEstimatedPosition.style.jsx' -import { monitorfishMap } from '../../map/monitorfishMap.js' -import { vesselsAdapter } from '../slice.ts' +import { getEstimatedPositionStyle } from './styles/vesselEstimatedPosition.style' +import { monitorfishMap } from '../../map/monitorfishMap' +import { vesselsAdapter, vesselSelectors } from '../slice' const VesselEstimatedPositionLayer = () => { const { @@ -15,8 +15,7 @@ const VesselEstimatedPositionLayer = () => { vesselsTracksShowed, selectedVesselIdentity } = useSelector(state => state.vessel) - const vesselsSelector = useSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useSelector(vesselSelectors.selectAll) const { nonFilteredVesselsAreHidden diff --git a/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx b/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx index 9fff73f5cd..da5131a23c 100644 --- a/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx @@ -4,18 +4,18 @@ import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' +import { LayerProperties } from '../../../domain/entities/layers/constants' -import { getVesselInfractionSuspicionStyle } from './style.js' +import { getVesselInfractionSuspicionStyle } from './style' import { getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed -} from '../../../domain/entities/vessel/vessel.js' -import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser.js' -import { monitorfishMap } from '../../map/monitorfishMap.js' -import { vesselsAdapter } from '../slice.ts' +} from '../../../domain/entities/vessel/vessel' +import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' +import { monitorfishMap } from '../../map/monitorfishMap' +import { vesselsAdapter, vesselSelectors } from '../slice' const VesselInfractionSuspicionLayer = () => { const isSuperUser = useIsSuperUser() @@ -25,8 +25,7 @@ const VesselInfractionSuspicionLayer = () => { selectedVesselIdentity, vesselsTracksShowed } = useSelector(state => state.vessel) - const vesselsSelector = useSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useSelector(vesselSelectors.selectAll) const { nonFilteredVesselsAreHidden diff --git a/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx b/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx index 946cc9a66b..e6f0eb0965 100644 --- a/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx @@ -6,12 +6,12 @@ import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' import { transform } from 'ol/proj' -import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../domain/entities/map/constants.js' -import { LayerProperties } from '../../../domain/entities/layers/constants.js' -import { Vessel } from '../../../domain/entities/vessel/vessel.js' +import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../domain/entities/map/constants' +import { LayerProperties } from '../../../domain/entities/layers/constants' +import { Vessel } from '../../../domain/entities/vessel/vessel' -import { getSelectedVesselStyle } from './style.js' -import { monitorfishMap } from '../../map/monitorfishMap.js' +import { getSelectedVesselStyle } from './style' +import { monitorfishMap } from '../../map/monitorfishMap' const VesselSelectedLayer = () => { const { selectedVessel, vesselsTracksShowed } = useSelector(state => state.vessel) diff --git a/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx b/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx index 70d3d91407..b5767b9db9 100644 --- a/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx +++ b/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx @@ -18,7 +18,7 @@ import { VesselLabelLine } from '../../../domain/entities/vesselLabelLine' import { getLabelLineStyle } from '../../map/layers/styles/labelLine.style' import { monitorfishMap } from '../../map/monitorfishMap' import { VesselLabelOverlay } from '../components/VesselLabelOverlay' -import { vesselsAdapter } from '../slice' +import { vesselSelectors } from '../slice' import type { VesselLastPositionFeature } from '../../../domain/entities/vessel/types' import type { VectorLayerWithName } from '../../../domain/types/layer' @@ -37,8 +37,8 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { const hideNonSelectedVessels = useMainAppSelector(state => state.vessel.hideNonSelectedVessels) const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) - const vesselsSelector = useMainAppSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useMainAppSelector(vesselSelectors.selectAll) + const vesselsTracksShowed = useMainAppSelector(state => state.vessel.vesselsTracksShowed) const areVesselsDisplayed = useMainAppSelector(state => state.displayedComponent.areVesselsDisplayed) const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) diff --git a/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx b/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx index 81017d8929..54d3357f2f 100644 --- a/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx +++ b/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx @@ -1,10 +1,8 @@ import { COLORS } from '@constants/constants' import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/constants' -import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { memo, useEffect } from 'react' -import { applyFilterToVessels } from './useCases/applyFilterToVessels' import { MonitorFishLayer } from '../../../../domain/entities/layers/types' import { getVesselLastPositionVisibilityDates, Vessel } from '../../../../domain/entities/vessel/vessel' import { theme } from '../../../../ui/theme' @@ -13,11 +11,8 @@ import { monitorfishMap } from '../../../map/monitorfishMap' import { getWebGLVesselStyleVariables } from '../style' function UnmemoizedVesselsLayer() { - const dispatch = useMainAppDispatch() const areVesselsDisplayed = useMainAppSelector(state => state.displayedComponent.areVesselsDisplayed) - const hideNonSelectedVessels = useMainAppSelector(state => state.vessel.hideNonSelectedVessels) - const hideVesselsAtPort = useMainAppSelector(state => state.map.hideVesselsAtPort) const selectedBaseLayer = useMainAppSelector(state => state.map.selectedBaseLayer) const vesselsLastPositionVisibility = useMainAppSelector(state => state.map.vesselsLastPositionVisibility) @@ -81,12 +76,6 @@ function UnmemoizedVesselsLayer() { VESSELS_VECTOR_LAYER.updateStyleVariables({ hideNonSelectedVessels: booleanToInt(hideNonSelectedVessels) }) }, [hideNonSelectedVessels]) - useEffect(() => { - VESSELS_VECTOR_LAYER.updateStyleVariables({ - nonFilteredVesselsAreHidden: booleanToInt(nonFilteredVesselsAreHidden) - }) - }, [nonFilteredVesselsAreHidden]) - useEffect(() => { VESSELS_VECTOR_LAYER.updateStyleVariables({ previewFilteredVesselsMode: booleanToInt(previewFilteredVesselsMode) }) }, [previewFilteredVesselsMode]) @@ -96,18 +85,6 @@ function UnmemoizedVesselsLayer() { VESSELS_VECTOR_LAYER.updateStyleVariables({ isLight: booleanToInt(isLight) }) }, [selectedBaseLayer]) - useEffect(() => { - dispatch(applyFilterToVessels()) - if (showedFilter?.color) { - const [red, green, blue] = customHexToRGB(showedFilter?.color) - VESSELS_VECTOR_LAYER.updateStyleVariables({ - filterColorBlue: blue, - filterColorGreen: green, - filterColorRed: red - }) - } - }, [showedFilter?.color, filters, showedFilter, dispatch]) - useEffect(() => { const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) diff --git a/frontend/src/features/Vessel/layers/style.ts b/frontend/src/features/Vessel/layers/style.ts index d2953d8479..d8653d69ea 100644 --- a/frontend/src/features/Vessel/layers/style.ts +++ b/frontend/src/features/Vessel/layers/style.ts @@ -85,7 +85,8 @@ export const getWebGLVesselStyle = (): WebGLStyle => { 'icon-rotation': ['*', ['get', 'course'], Math.PI / 180], 'icon-scale': 0.8, 'icon-size': [25, 25], - 'icon-src': 'boat_icons.png' + 'icon-src': 'boat_icons.png', + variables: {} } } @@ -104,8 +105,10 @@ export const getWebGLVesselStyleVariables = ({ filterColorBlue, filterColorGreen, filterColorRed, + filterPreview: 0, hideNonSelectedVessels: booleanToInt(hideNonSelectedVessels), hideVesselsAtPort: booleanToInt(hideVesselsAtPort), + isFiltered: 0, isLight: booleanToInt(isLight), nonFilteredVesselsAreHidden: booleanToInt(nonFilteredVesselsAreHidden), previewFilteredVesselsMode: booleanToInt(previewFilteredVesselsMode), diff --git a/frontend/src/features/Vessel/slice.ts b/frontend/src/features/Vessel/slice.ts index 5bf9b3c50d..092df9fd9c 100644 --- a/frontend/src/features/Vessel/slice.ts +++ b/frontend/src/features/Vessel/slice.ts @@ -27,6 +27,9 @@ export const vesselsAdapter = createEntityAdapter({ sortComparer: false }) +// @ts-ignore +export const vesselSelectors = vesselsAdapter.getSelectors(state => state.vessel.vessels) + // TODO Properly type this redux state. export type VesselState = { fishingActivitiesShowedOnMap: FishingActivityShowedOnMap[] @@ -87,7 +90,7 @@ const vesselSlice = createSlice({ vesselFeatureId: VesselFeatureId }> ) { - const vessel = vesselsAdapter.getSelectors().selectById(state.vessels, action.payload.vesselFeatureId) + const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) if (vessel) { const nextVesselReportings = vessel?.vesselProperties?.reportings?.concat(action.payload.reportingType) @@ -188,7 +191,7 @@ const vesselSlice = createSlice({ vesselFeatureId: VesselFeatureId }> ) { - const vessel = vesselsAdapter.getSelectors().selectById(state.vessels, action.payload.vesselFeatureId) + const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) if (vessel) { const filteredAlerts = vessel?.vesselProperties?.alerts?.filter(alert => alert !== action.payload.alertType) @@ -254,7 +257,7 @@ const vesselSlice = createSlice({ vesselFeatureId: VesselFeatureId }> ) { - const vessel = vesselsAdapter.getSelectors().selectById(state.vessels, action.payload.vesselFeatureId) + const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) if (vessel) { const vesselReportingWithoutFirstFoundReportingType = vessel.vesselProperties.reportings?.reduce( filterFirstFoundReportingType(action.payload.reportingType), @@ -308,7 +311,7 @@ const vesselSlice = createSlice({ */ removeVesselReportings(state, action) { const vesselsFeatureIds = action.payload.map(reporting => reporting.vesselFeatureId) - const vessels = vesselsAdapter.getSelectors().selectAll(state.vessels) + const vessels = vesselSelectors.selectAll(state.vessels) vesselsAdapter.setMany( state.vessels, @@ -386,8 +389,8 @@ const vesselSlice = createSlice({ }, setAllVesselsAsUnfiltered(state) { - const vessels = vesselsAdapter.getSelectors().selectAll(state.vessels) - const vesselIds = vesselsAdapter.getSelectors().selectIds(state.vessels) + const vessels = vesselSelectors.selectAll(state.vessels) + const vesselIds = vesselSelectors.selectIds(state.vessels) // Check if any vessel has `isFiltered` set to true if (!vessels.some(vessel => vessel.isFiltered)) { @@ -408,7 +411,7 @@ const vesselSlice = createSlice({ setFilteredVesselsFeatures(state, action: PayloadAction) { const filteredVesselsFeaturesUids = action.payload - const vesselIds = vesselsAdapter.getSelectors().selectIds(state.vessels) + const vesselIds = vesselSelectors.selectIds(state.vessels) // Update only the vessels that match the filtered IDs vesselsAdapter.updateMany( @@ -445,7 +448,7 @@ const vesselSlice = createSlice({ */ setPreviewFilteredVesselsFeatures(state, action) { const previewFilteredVesselsFeaturesUids = action.payload - const vesselIds = vesselsAdapter.getSelectors().selectIds(state.vessels) + const vesselIds = vesselSelectors.selectIds(state.vessels) // Update only the vessels that match the filtered IDs vesselsAdapter.updateMany( diff --git a/frontend/src/features/Vessel/layers/VesselsLayer/useCases/applyFilterToVessels.ts b/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts similarity index 62% rename from frontend/src/features/Vessel/layers/VesselsLayer/useCases/applyFilterToVessels.ts rename to frontend/src/features/Vessel/useCases/applyFilterToVessels.ts index 6c7454bd48..e10b094434 100644 --- a/frontend/src/features/Vessel/layers/VesselsLayer/useCases/applyFilterToVessels.ts +++ b/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts @@ -1,24 +1,24 @@ -import { setError } from '../../../../../domain/shared_slices/Global' -import { getFilteredVessels } from '../../../../../domain/use_cases/vessel/getFilteredVessels' -import NoVesselsInFilterError from '../../../../../errors/NoVesselsInFilterError' -import { setAllVesselsAsUnfiltered, setFilteredVesselsFeatures, vesselsAdapter } from '../../../slice' +import { setError } from '../../../domain/shared_slices/Global' +import { getFilteredVessels } from '../../../domain/use_cases/vessel/getFilteredVessels' +import NoVesselsInFilterError from '../../../errors/NoVesselsInFilterError' +import { setAllVesselsAsUnfiltered, setFilteredVesselsFeatures, vesselSelectors } from '../slice' import type { MainAppThunk } from '@store' export const applyFilterToVessels = (): MainAppThunk => async (dispatch, getState) => { const showedFilter = getState().filter?.filters?.find(filter => filter.showed) - const vesselsSelector = getState().vessel.vessels - if (!vesselsSelector) { - return - } - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = vesselSelectors.selectAll(getState()) if (!showedFilter) { dispatch(setAllVesselsAsUnfiltered()) + + return } const filteredVessels = await dispatch(getFilteredVessels(vessels, showedFilter.filters)) if (!filteredVessels?.length) { dispatch(setError(new NoVesselsInFilterError("Il n'y a pas de navire dans ce filtre"))) + + return } const filteredVesselsUids = filteredVessels.map(vessel => vessel.vesselFeatureId) diff --git a/frontend/src/features/Vessel/useCases/renderVessels.ts b/frontend/src/features/Vessel/useCases/renderVessels.ts new file mode 100644 index 0000000000..1abdbbf28a --- /dev/null +++ b/frontend/src/features/Vessel/useCases/renderVessels.ts @@ -0,0 +1,13 @@ +import { VESSELS_VECTOR_SOURCE } from '@features/Vessel/layers/VesselsLayer/constants' +import { vesselSelectors } from '@features/Vessel/slice' +import { buildFeature } from '@features/Vessel/utils' + +import type { MainAppThunk } from '@store' + +export const renderVessels = (): MainAppThunk => async (_, getState) => { + const vessels = vesselSelectors.selectAll(getState()) + const features = vessels.map(vessel => buildFeature(vessel)) + + VESSELS_VECTOR_SOURCE.clear(true) + VESSELS_VECTOR_SOURCE.addFeatures(features) +} diff --git a/frontend/src/features/Vessel/layers/VesselsLayer/useCases/showVesselsLastPosition.ts b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts similarity index 64% rename from frontend/src/features/Vessel/layers/VesselsLayer/useCases/showVesselsLastPosition.ts rename to frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts index a43ecd627d..a5838a9ffa 100644 --- a/frontend/src/features/Vessel/layers/VesselsLayer/useCases/showVesselsLastPosition.ts +++ b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts @@ -1,22 +1,17 @@ import { reportingIsAnInfractionSuspicion } from '@features/Reporting/utils' import { VESSELS_VECTOR_LAYER, VESSELS_VECTOR_SOURCE } from '@features/Vessel/layers/VesselsLayer/constants' -import Feature from 'ol/Feature' -import Point from 'ol/geom/Point' +import { buildFeature } from '@features/Vessel/utils' import { transform } from 'ol/proj' import { applyFilterToVessels } from './applyFilterToVessels' -import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../../../domain/entities/map/constants' -import { Vessel } from '../../../../../domain/entities/vessel/vessel' -import { resetIsUpdatingVessels } from '../../../../../domain/shared_slices/Global' -import getUniqueSpeciesAndDistricts from '../../../../../domain/use_cases/species/getUniqueSpeciesAndDistricts' -import { customHexToRGB } from '../../../../../utils' -import { setVessels, setVesselsSpeciesAndDistricts } from '../../../slice' +import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../domain/entities/map/constants' +import { Vessel } from '../../../domain/entities/vessel/vessel' +import { resetIsUpdatingVessels } from '../../../domain/shared_slices/Global' +import getUniqueSpeciesAndDistricts from '../../../domain/use_cases/species/getUniqueSpeciesAndDistricts' +import { customHexToRGB } from '../../../utils' +import { setVessels, setVesselsSpeciesAndDistricts } from '../slice' -import type { - VesselEnhancedLastPositionWebGLObject, - VesselLastPosition, - VesselLastPositionFeature -} from '../../../../../domain/entities/vessel/types' +import type { VesselEnhancedLastPositionWebGLObject, VesselLastPosition } from '../../../domain/entities/vessel/types' import type { MainAppThunk } from '@store' export const showVesselsLastPosition = @@ -56,10 +51,13 @@ export const showVesselsLastPosition = function convertToEnhancedLastPositions(vessels: VesselLastPosition[]): VesselEnhancedLastPositionWebGLObject[] { return vessels.map(vessel => ({ coordinates: transform([vessel.longitude, vessel.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION), + course: vessel.course, filterPreview: 0, hasBeaconMalfunction: !!vessel.beaconMalfunctionId, + isAtPort: vessel.isAtPort, isFiltered: 0, lastPositionSentAt: new Date(vessel.dateTime).getTime(), + speed: vessel.speed, vesselFeatureId: Vessel.getVesselFeatureId(vessel), vesselProperties: { ...vessel, @@ -76,26 +74,3 @@ function convertToEnhancedLastPositions(vessels: VesselLastPosition[]): VesselEn } })) } - -function buildFeature(vessel: VesselEnhancedLastPositionWebGLObject): VesselLastPositionFeature { - const propertiesUsedForStyling = { - coordinates: vessel.coordinates, - course: vessel.vesselProperties.course, - filterPreview: vessel.filterPreview, - hasBeaconMalfunction: vessel.hasBeaconMalfunction, - isAtPort: vessel.vesselProperties.isAtPort, - isFiltered: vessel.isFiltered, - lastPositionSentAt: vessel.lastPositionSentAt, - speed: vessel.vesselProperties.speed - } - - const feature = new Feature({ - vesselFeatureId: vessel.vesselFeatureId, - ...propertiesUsedForStyling, - geometry: new Point(vessel.coordinates) - }) as VesselLastPositionFeature - feature.setId(vessel.vesselFeatureId) - feature.vesselProperties = vessel.vesselProperties - - return feature -} diff --git a/frontend/src/features/Vessel/utils.ts b/frontend/src/features/Vessel/utils.ts new file mode 100644 index 0000000000..52324ca6c9 --- /dev/null +++ b/frontend/src/features/Vessel/utils.ts @@ -0,0 +1,30 @@ +import Feature from 'ol/Feature' +import Point from 'ol/geom/Point' + +import type { + VesselEnhancedLastPositionWebGLObject, + VesselLastPositionFeature +} from '../../domain/entities/vessel/types' + +export function buildFeature(vessel: VesselEnhancedLastPositionWebGLObject): VesselLastPositionFeature { + const propertiesUsedForStyling = { + coordinates: vessel.coordinates, + course: vessel.vesselProperties.course, + filterPreview: vessel.filterPreview, + hasBeaconMalfunction: vessel.hasBeaconMalfunction, + isAtPort: vessel.vesselProperties.isAtPort, + isFiltered: vessel.isFiltered, + lastPositionSentAt: vessel.lastPositionSentAt, + speed: vessel.vesselProperties.speed + } + + const feature = new Feature({ + vesselFeatureId: vessel.vesselFeatureId, + ...propertiesUsedForStyling, + geometry: new Point(vessel.coordinates) + }) as VesselLastPositionFeature + feature.setId(vessel.vesselFeatureId) + feature.vesselProperties = vessel.vesselProperties + + return feature +} diff --git a/frontend/src/features/VesselList/VesselListFilters.tsx b/frontend/src/features/VesselList/VesselListFilters.tsx index af1f7c3439..1eb3e81549 100644 --- a/frontend/src/features/VesselList/VesselListFilters.tsx +++ b/frontend/src/features/VesselList/VesselListFilters.tsx @@ -1,4 +1,4 @@ -import { FilterTag } from '@features/MainWindow/components/MapButtons/VesselFilters/FilterTag' +import { FilterTag } from '@features/Filter/components/VesselFilters/FilterTag' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Checkbox, CheckboxGroup, MultiCascader, SelectPicker, Tag, TagPicker } from 'rsuite' import styled from 'styled-components' @@ -98,7 +98,7 @@ function UnmemoizedVesselListFilters({ })) }, [districts.districts]) - const { callRemoveZoneSelected, zonesSelected } = zones + const { zonesSelected } = zones const showZonesSelected = useCallback( () => zonesSelected?.length && zonesSelected.find(zone => zone.code === LayersType.FREE_DRAW) @@ -109,16 +109,17 @@ function UnmemoizedVesselListFilters({ callRemoveZoneSelected(zoneSelected)} + tag={{ + type: 'zonesSelected', + value: 'Effacer la zone définie' + }} text="Effacer la zone définie" - type={undefined} uuid={undefined} - value="Effacer la zone définie" /> )) : null, - [zonesSelected, callRemoveZoneSelected] + [zonesSelected] ) const getZones = useCallback(async () => { diff --git a/frontend/src/features/VesselList/index.tsx b/frontend/src/features/VesselList/index.tsx index b935af03f9..ddbc882fe4 100644 --- a/frontend/src/features/VesselList/index.tsx +++ b/frontend/src/features/VesselList/index.tsx @@ -1,5 +1,5 @@ +import { SaveVesselFiltersModal } from '@features/Filter/components/SaveVesselFiltersModal' import { MapToolButton } from '@features/MainWindow/components/MapButtons/shared/MapToolButton' -import { SaveVesselFiltersModal } from '@features/MainWindow/components/MapButtons/VesselFilters/SaveVesselFiltersModal' import { THEME } from '@mtes-mct/monitor-ui' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { batch } from 'react-redux' @@ -16,7 +16,6 @@ import { LayerType as LayersType, LayerType } from '../../domain/entities/layers import { InteractionListener, InteractionType } from '../../domain/entities/map/constants' import { VesselLocation } from '../../domain/entities/vessel/vessel' import { setDisplayedComponents } from '../../domain/shared_slices/DisplayedComponent' -import { addFilter } from '../../domain/shared_slices/Filter' import { setBlockVesselsUpdate, setPreviewFilteredVesselsMode } from '../../domain/shared_slices/Global' import { animateToExtent } from '../../domain/shared_slices/Map' import { addVesselListFilterZone } from '../../domain/use_cases/vessel/addVesselListFilterZone' @@ -36,7 +35,7 @@ import { useGetFleetSegmentsQuery } from '../FleetSegment/apis' import VesselListSVG from '../icons/Icone_liste_navires.svg?react' import PreviewSVG from '../icons/Oeil_apercu_carte.svg?react' import { setProcessingRegulationSearchedZoneExtent } from '../Regulation/slice' -import { setPreviewFilteredVesselsFeatures, vesselsAdapter } from '../Vessel/slice' +import { setPreviewFilteredVesselsFeatures, vesselSelectors } from '../Vessel/slice' import type { VesselEnhancedLastPositionWebGLObject } from '../../domain/entities/vessel/types' @@ -68,8 +67,7 @@ export function VesselList({ namespace }) { const isVesselListModalDisplayed = useMainAppSelector(state => state.displayedComponent.isVesselListModalDisplayed) const { drawedGeometry } = useListenForDrawedGeometry(InteractionListener.VESSELS_LIST) const { uniqueVesselsDistricts: districts, uniqueVesselsSpecies: species } = useMainAppSelector(state => state.vessel) - const vesselsSelector = useMainAppSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useMainAppSelector(vesselSelectors.selectAll) const getFleetSegmentsQuery = useGetFleetSegmentsQuery() const gears = useMainAppSelector(state => state.gear.gears) @@ -241,25 +239,14 @@ export function VesselList({ namespace }) { dispatch(resetZonesSelected()) }, [dispatch]) - const addFilterCallback = useCallback( - filter => { - dispatch(addFilter(filter)) - }, - [dispatch] - ) - const selectBox = useCallback(() => { - batch(() => { - dispatch(addVesselListFilterZone(InteractionType.SQUARE)) - dispatch(setBlockVesselsUpdate(true)) - }) + dispatch(addVesselListFilterZone(InteractionType.SQUARE)) + dispatch(setBlockVesselsUpdate(true)) }, [dispatch]) const selectPolygon = useCallback(() => { - batch(() => { - dispatch(addVesselListFilterZone(InteractionType.POLYGON)) - dispatch(setBlockVesselsUpdate(true)) - }) + dispatch(addVesselListFilterZone(InteractionType.POLYGON)) + dispatch(setBlockVesselsUpdate(true)) }, [dispatch]) const callRemoveZoneSelected = useCallback( @@ -508,7 +495,6 @@ export function VesselList({ namespace }) { setIsOpen={setDownloadVesselListModalIsOpen} /> state.vessel.selectedVesselIdentity) - const vesselsSelector = useMainAppSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useMainAppSelector(vesselSelectors.selectAll) const searchQueryRef = useRef('') const wrapperRef = useRef(null) diff --git a/frontend/src/features/map/Map.tsx b/frontend/src/features/map/Map.tsx index f5807a4654..0d7a79e3d3 100644 --- a/frontend/src/features/map/Map.tsx +++ b/frontend/src/features/map/Map.tsx @@ -17,6 +17,7 @@ import { FeatureWithCodeAndEntityId } from '../../libs/FeatureWithCodeAndEntityI import { AdministrativeLayers } from '../AdministrativeZone/layers/AdministrativeLayers' import { BaseLayer } from '../BaseMap/layers/BaseLayer' import { DrawLayer } from '../Draw/layer' +import FilterLayer from '../Filter/layers/FilterLayer' import InterestPointLayer from '../InterestPoint/layers/InterestPointLayer' import { MeasurementLayer } from '../Measurement/layers/MeasurementLayer' import { MissionOverlay } from '../Mission/components/MissionOverlay' @@ -34,7 +35,6 @@ import { SelectedStationOverlay } from '../Station/components/SelectedStationOve import { StationLayer } from '../Station/components/StationLayer' import { VesselCardOverlay } from '../Vessel/components/VesselCardOverlay' import VesselEstimatedPositionOverlay from '../Vessel/components/VesselEstimatedPositionOverlay' -import FilterLayer from '../Vessel/layers/FilterLayer' import VesselAlertAndBeaconMalfunctionLayer from '../Vessel/layers/VesselAlertAndBeaconMalfunctionLayer' import VesselAlertLayer from '../Vessel/layers/VesselAlertLayer' import VesselBeaconMalfunctionLayer from '../Vessel/layers/VesselBeaconMalfunctionLayer' diff --git a/frontend/src/features/map/MapMenu.tsx b/frontend/src/features/map/MapMenu.tsx index 6bacaecafe..9e6479d106 100644 --- a/frontend/src/features/map/MapMenu.tsx +++ b/frontend/src/features/map/MapMenu.tsx @@ -7,13 +7,12 @@ import MapMenuOverlay from './overlays/MapMenuOverlay' import { HIT_PIXEL_TO_TOLERANCE } from '../../constants/constants' import { LayerProperties } from '../../domain/entities/layers/constants' import { MonitorFishLayer } from '../../domain/entities/layers/types' -import { vesselsAdapter } from '../Vessel/slice' +import { vesselSelectors } from '../Vessel/slice' import type { VesselEnhancedLastPositionWebGLObject } from '../../domain/entities/vessel/types' export function MapMenu() { - const vesselsSelector = useMainAppSelector(state => state.vessel.vessels) - const vessels = vesselsAdapter.getSelectors().selectAll(vesselsSelector) + const vessels = useMainAppSelector(vesselSelectors.selectAll) const [coordinates, setCoordinates] = useState([]) const vessel = useRef() diff --git a/frontend/src/store/reducers.ts b/frontend/src/store/reducers.ts index 8b41967032..15a5c62041 100644 --- a/frontend/src/store/reducers.ts +++ b/frontend/src/store/reducers.ts @@ -4,6 +4,7 @@ import { controlUnitDialogReducer } from '@features/ControlUnit/components/Contr import { controlUnitListDialogPersistedReducer } from '@features/ControlUnit/components/ControlUnitListDialog/slice' import { customZoneReducer, type CustomZoneState } from '@features/CustomZone/slice' import { drawReducer } from '@features/Draw/slice' +import { filterReducer, type FilterState } from '@features/Filter/slice' import { interestPointReducer } from '@features/InterestPoint/slice' import { logbookReducer } from '@features/Logbook/slice' import { mainWindowReducer } from '@features/MainWindow/slice' @@ -30,7 +31,6 @@ import { controlReducer } from '../domain/shared_slices/Control' import { displayedComponentReducer } from '../domain/shared_slices/DisplayedComponent' import { displayedErrorReducer } from '../domain/shared_slices/DisplayedError' import { favoriteVesselReducer } from '../domain/shared_slices/FavoriteVessel' -import { filterReducer } from '../domain/shared_slices/Filter' import { gearReducer } from '../domain/shared_slices/Gear' import { globalSliceReducer } from '../domain/shared_slices/Global' import { infractionReducer } from '../domain/shared_slices/Infraction' @@ -82,7 +82,12 @@ export const mainReducer = { displayedError: displayedErrorReducer, draw: drawReducer, favoriteVessel: favoriteVesselReducer, - filter: filterReducer, + filter: persistReducerTyped( + { + ...getCommonPersistReducerConfig('mainPersistorFilter', ['filters', 'nonFilteredVesselsAreHidden']) + }, + filterReducer + ), fishingActivities: logbookReducer, infraction: infractionReducer, interestPoint: interestPointReducer,