diff --git a/apps/fishing-map/features/dataviews/dataviews.utils.ts b/apps/fishing-map/features/dataviews/dataviews.utils.ts index cfb3a80b79..4c47c4e682 100644 --- a/apps/fishing-map/features/dataviews/dataviews.utils.ts +++ b/apps/fishing-map/features/dataviews/dataviews.utils.ts @@ -47,17 +47,17 @@ export function dataviewHasVesselGroupId(dataview: UrlDataviewInstance, vesselGr export const getVesselInfoDataviewInstanceDatasetConfig = ( vesselId: string, - { info }: VesselInstanceDatasets + { info }: VesselInstanceDatasets, + includeRelatedIdentities = true ) => { return { datasetId: info, params: [{ id: 'vesselId', value: vesselId }], query: [ { id: 'dataset', value: info }, - { - id: 'includes', - value: [INCLUDES_RELATED_SELF_REPORTED_INFO_ID], - }, + ...(includeRelatedIdentities + ? [{ id: 'includes', value: [INCLUDES_RELATED_SELF_REPORTED_INFO_ID] }] + : []), ], endpoint: EndpointId.Vessel, } as DataviewDatasetConfig diff --git a/apps/fishing-map/features/search/advanced/SearchAdvanced.tsx b/apps/fishing-map/features/search/advanced/SearchAdvanced.tsx index d14de52064..9e143dbba6 100644 --- a/apps/fishing-map/features/search/advanced/SearchAdvanced.tsx +++ b/apps/fishing-map/features/search/advanced/SearchAdvanced.tsx @@ -28,6 +28,7 @@ import SearchPlaceholder, { SearchEmptyState, SearchNoResultsState, } from 'features/search/SearchPlaceholders' +import { selectIsGFWUser } from 'features/user/selectors/user.selectors' import LocalStorageLoginLink from 'routes/LoginLink' import { useLocationConnect } from 'routes/routes.hook' import { AsyncReducerStatus } from 'utils/async-slice' @@ -47,12 +48,14 @@ function SearchAdvanced({ const dispatch = useAppDispatch() const { searchPagination, searchSuggestion, searchSuggestionClicked } = useSearchConnect() const advancedSearchAllowed = useSelector(isAdvancedSearchAllowed) + const { searchFilters, setSearchFilters } = useSearchFiltersConnect() const searchStatus = useSelector(selectSearchStatus) const searchQuery = useSelector(selectSearchQuery) const searchStatusCode = useSelector(selectSearchStatusCode) const { dispatchQueryParams } = useLocationConnect() const { hasFilters } = useSearchFiltersConnect() const searchFilterErrors = useSearchFiltersErrors() + const isGFWUser = useSelector(selectIsGFWUser) const ref = useEventKeyListener(['Enter'], fetchResults) const resetSearchState = useCallback(() => { @@ -76,12 +79,27 @@ function SearchAdvanced({ dispatchQueryParams({ query: e.target.value }) } + const handleSearchIdChange = (e: ChangeEvent) => { + setSearchFilters({ id: e.target.value }) + } + const hasSearchFilterErrors = Object.keys(searchFilterErrors).length > 0 return (
+ {isGFWUser && ( +
+ + +
+ )} return ( { if (tableContainerRef.current) { @@ -479,8 +491,8 @@ function SearchAdvancedResults({ fetchResults, fetchMoreResults }: SearchCompone cell.column.id === 'shipname' ? 'var(--color-white)' : vesselSelectedIds.includes(getVesselProperty(row.original, 'id')) - ? 'var(--color-terthiary-blue)' - : 'transparent', + ? 'var(--color-terthiary-blue)' + : 'transparent', textAlign: cell.column.id === 'mrt-row-select' ? 'center' : 'left', borderRight: 'var(--border)', borderBottom: 'var(--border)', diff --git a/apps/fishing-map/features/search/search.config.selectors.ts b/apps/fishing-map/features/search/search.config.selectors.ts index a8dbeb6c8d..c19d023e90 100644 --- a/apps/fishing-map/features/search/search.config.selectors.ts +++ b/apps/fishing-map/features/search/search.config.selectors.ts @@ -13,6 +13,7 @@ function selectVesselSearchStateProperty

(pr }) } +export const selectSearchId = selectVesselSearchStateProperty('id') export const selectSearchQuery = selectVesselSearchStateProperty('query') export const selectSearchOption = selectVesselSearchStateProperty('searchOption') export const selectSearchInfoSource = selectVesselSearchStateProperty('infoSource') @@ -35,6 +36,7 @@ export const selectSearchOrigin = selectVesselSearchStateProperty('origin') export const selectSearchFilters = createSelector( [ + selectSearchId, selectSearchFlag, selectSearchSources, selectSearchTransmissionDateFrom, @@ -53,6 +55,7 @@ export const selectSearchFilters = createSelector( selectSearchInfoSource, ], ( + id, flag, sources, transmissionDateFrom, @@ -71,6 +74,7 @@ export const selectSearchFilters = createSelector( infoSource ): VesselSearchState => { return { + id, flag, sources, transmissionDateFrom, diff --git a/apps/fishing-map/features/search/search.config.ts b/apps/fishing-map/features/search/search.config.ts index 27d7508598..9c89c10a7d 100644 --- a/apps/fishing-map/features/search/search.config.ts +++ b/apps/fishing-map/features/search/search.config.ts @@ -8,6 +8,7 @@ export const RESULTS_PER_PAGE = 20 export const SSVID_LENGTH = 9 export const IMO_LENGTH = 7 export const EMPTY_FILTERS = { + id: undefined, query: undefined, flag: undefined, infoSource: undefined, diff --git a/apps/fishing-map/features/search/search.types.ts b/apps/fishing-map/features/search/search.types.ts index 7ec76cdca4..2cc05f6e84 100644 --- a/apps/fishing-map/features/search/search.types.ts +++ b/apps/fishing-map/features/search/search.types.ts @@ -3,6 +3,7 @@ import type { GearType, VesselIdentitySourceEnum, VesselType } from '@globalfish import type { SearchType } from './search.config' export type VesselSearchState = { + id?: string query?: string shipname?: string sources?: string[] diff --git a/apps/fishing-map/features/vessel/Vessel.module.css b/apps/fishing-map/features/vessel/Vessel.module.css index 1a535975df..11918ae0fb 100644 --- a/apps/fishing-map/features/vessel/Vessel.module.css +++ b/apps/fishing-map/features/vessel/Vessel.module.css @@ -12,6 +12,15 @@ z-index: 1; } +.fullProfileMessage { + color: var(--color-secondary-blue); + padding: var(--space-S) var(--space-S) var(--space-S) var(--space-M); + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: var(--border); +} + .linkTooltip { max-width: none; } diff --git a/apps/fishing-map/features/vessel/Vessel.tsx b/apps/fishing-map/features/vessel/Vessel.tsx index f88bae5aea..4850cac40f 100644 --- a/apps/fishing-map/features/vessel/Vessel.tsx +++ b/apps/fishing-map/features/vessel/Vessel.tsx @@ -6,7 +6,7 @@ import { isAuthError } from '@globalfishingwatch/api-client' import type { Dataview } from '@globalfishingwatch/api-types' import { VesselIdentitySourceEnum } from '@globalfishingwatch/api-types' import type { Tab } from '@globalfishingwatch/ui-components' -import { Spinner, Tabs } from '@globalfishingwatch/ui-components' +import { Button, Spinner, Tabs } from '@globalfishingwatch/ui-components' import { VESSEL_PROFILE_DATAVIEWS_INSTANCES } from 'data/default-workspaces/context-layers' import { BASEMAP_DATAVIEW_SLUG } from 'data/workspaces' @@ -17,7 +17,7 @@ import { getDatasetsInDataviews } from 'features/datasets/datasets.utils' import { fetchDataviewsByIdsThunk } from 'features/dataviews/dataviews.slice' import { useClickedEventConnect } from 'features/map/map-interactions.hooks' import { useFetchDataviewResources } from 'features/resources/resources.hooks' -import { selectIsGuestUser } from 'features/user/selectors/user.selectors' +import { selectIsGFWUser, selectIsGuestUser } from 'features/user/selectors/user.selectors' import VesselAreas from 'features/vessel/areas/VesselAreas' import Insights from 'features/vessel/insights/Insights' import RelatedVessels from 'features/vessel/related-vessels/RelatedVessels' @@ -28,6 +28,7 @@ import { selectVesselInfoStatus, } from 'features/vessel/selectors/vessel.selectors' import { + selectIncludeRelatedIdentities, selectVesselAreaSubsection, selectVesselDatasetId, selectVesselSection, @@ -61,6 +62,8 @@ const Vessel = () => { const { dispatchQueryParams } = useLocationConnect() const { removeDataviewInstance, upsertDataviewInstance } = useDataviewInstancesConnect() const vesselId = useSelector(selectVesselId) + const isGFWUser = useSelector(selectIsGFWUser) + const includeRelatedIdentities = useSelector(selectIncludeRelatedIdentities) const vesselSection = useSelector(selectVesselSection) const vesselArea = useSelector(selectVesselAreaSubsection) const datasetId = useSelector(selectVesselDatasetId) @@ -156,7 +159,7 @@ const Vessel = () => { infoStatus === AsyncReducerStatus.Idle || (infoStatus === AsyncReducerStatus.Error && infoError?.status === 401) ) { - dispatch(fetchVesselInfoThunk({ vesselId, datasetId })) + dispatch(fetchVesselInfoThunk({ vesselId, datasetId, includeRelatedIdentities })) } }, [datasetId, dispatch, vesselId, urlWorkspaceId]) @@ -177,6 +180,16 @@ const Vessel = () => { [dispatchQueryParams, updateAreaLayersVisibility, vesselArea] ) + const handleFullProfileClick = useCallback(() => { + dispatchQueryParams({ + includeRelatedIdentities: true, + start: undefined, + end: undefined, + vesselSelfReportedId: undefined, + }) + window.location.reload() + }, [dispatchQueryParams]) + if (infoStatus === AsyncReducerStatus.Loading) { return } @@ -201,6 +214,18 @@ const Vessel = () => { {infoStatus === AsyncReducerStatus.Finished && ( + {isGFWUser && !includeRelatedIdentities && ( +

+
+ Identity and activity of a single vessel id (only for GFW users): +
+ {vesselId} +
+ +
+ )}
diff --git a/apps/fishing-map/features/vessel/vessel.config.selectors.ts b/apps/fishing-map/features/vessel/vessel.config.selectors.ts index c7b7df623e..35c309c18f 100644 --- a/apps/fishing-map/features/vessel/vessel.config.selectors.ts +++ b/apps/fishing-map/features/vessel/vessel.config.selectors.ts @@ -26,6 +26,9 @@ export const selectVesselRelatedSubsection = selectVesselProfileStateProperty('v export const selectViewOnlyVessel = selectVesselProfileStateProperty('viewOnlyVessel') export const selectVesselRegistryId = selectVesselProfileStateProperty('vesselRegistryId') export const selectVesselSelfReportedId = selectVesselProfileStateProperty('vesselSelfReportedId') +export const selectIncludeRelatedIdentities = selectVesselProfileStateProperty( + 'includeRelatedIdentities' +) export const selectVesselIdentityId = createSelector( [selectVesselIdentitySource, selectVesselRegistryId, selectVesselSelfReportedId], diff --git a/apps/fishing-map/features/vessel/vessel.config.ts b/apps/fishing-map/features/vessel/vessel.config.ts index 515f518661..7da9da7ecd 100644 --- a/apps/fishing-map/features/vessel/vessel.config.ts +++ b/apps/fishing-map/features/vessel/vessel.config.ts @@ -28,6 +28,7 @@ export const DEFAULT_VESSEL_STATE: VesselProfileState = { vesselArea: 'eez', vesselRelated: 'encounters', viewOnlyVessel: true, + includeRelatedIdentities: true, } export type VesselRenderField = { diff --git a/apps/fishing-map/features/vessel/vessel.slice.ts b/apps/fishing-map/features/vessel/vessel.slice.ts index 7b24bddbe8..f8d92093bc 100644 --- a/apps/fishing-map/features/vessel/vessel.slice.ts +++ b/apps/fishing-map/features/vessel/vessel.slice.ts @@ -91,11 +91,19 @@ const initialState: VesselState = { type VesselSliceState = { vessel: VesselState } -type FetchVesselThunkParams = { vesselId: string; datasetId: string } +type FetchVesselThunkParams = { + vesselId: string + datasetId: string + includeRelatedIdentities: boolean +} export const fetchVesselInfoThunk = createAsyncThunk( 'vessel/fetchInfo', async ( - { vesselId, datasetId }: FetchVesselThunkParams = {} as FetchVesselThunkParams, + { + vesselId, + datasetId, + includeRelatedIdentities, + }: FetchVesselThunkParams = {} as FetchVesselThunkParams, { dispatch, rejectWithValue, getState } ) => { try { @@ -118,9 +126,13 @@ export const fetchVesselInfoThunk = createAsyncThunk( }) dispatch(fetchDatasetsByIdsThunk({ ids: datasetsToFetch })) - const datasetConfig = getVesselInfoDataviewInstanceDatasetConfig(vesselId, { - info: dataset.id, - }) + const datasetConfig = getVesselInfoDataviewInstanceDatasetConfig( + vesselId, + { + info: dataset.id, + }, + includeRelatedIdentities + ) if (guestUser) { // This changes the order of the query params to avoid the cache datasetConfig.query?.push(CACHE_FALSE_PARAM) diff --git a/apps/fishing-map/features/vessel/vessel.types.ts b/apps/fishing-map/features/vessel/vessel.types.ts index e4fd8c6295..d641a8650d 100644 --- a/apps/fishing-map/features/vessel/vessel.types.ts +++ b/apps/fishing-map/features/vessel/vessel.types.ts @@ -14,6 +14,7 @@ export type VesselProfileState = { vesselIdentitySource: VesselIdentitySourceEnum vesselActivityMode: VesselProfileActivityMode viewOnlyVessel: boolean + includeRelatedIdentities?: boolean } export type VesselProfileStateProperty = keyof VesselProfileState diff --git a/libs/api-client/src/utils/search.ts b/libs/api-client/src/utils/search.ts index 4cbd0f1662..857e6ae783 100644 --- a/libs/api-client/src/utils/search.ts +++ b/libs/api-client/src/utils/search.ts @@ -133,6 +133,9 @@ export const getAdvancedSearchQuery = ( const getFieldValue = (value: string) => { const operator = field?.operator || params?.operator || '=' + if (field.key === 'id') { + return `selfReportedInfo.id ${operator} '${value}'` + } if (field.key === 'owner') { return `registryOwners.name ${operator} ${value}` }