From f767df889cddf354b85bccc37aefc3ba38273d94 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Wed, 28 Aug 2024 20:40:28 +0200 Subject: [PATCH 01/23] create vesselGroups from vessel section --- .../features/dataviews/dataviews.utils.ts | 26 +++++----- .../vessel-groups/VesselGroupAddButton.tsx | 52 ++++++++++++++----- .../vessel-groups/vessel-groups.slice.ts | 28 ++++++++-- .../workspace/vessels/VesselsSection.tsx | 47 +++++++++++++++-- .../public/locales/source/translations.json | 1 + libs/api-types/src/vesselGroups.ts | 2 +- 6 files changed, 122 insertions(+), 34 deletions(-) diff --git a/apps/fishing-map/features/dataviews/dataviews.utils.ts b/apps/fishing-map/features/dataviews/dataviews.utils.ts index 8eae64d91c..bd4203b28c 100644 --- a/apps/fishing-map/features/dataviews/dataviews.utils.ts +++ b/apps/fishing-map/features/dataviews/dataviews.utils.ts @@ -216,20 +216,22 @@ export const getContextDataviewInstance = (datasetId: string): DataviewInstance< export const getVesselGroupDataviewInstance = ( vesselGroupId: string -): DataviewInstance => { - const contextDataviewInstance = { - id: `${VESSEL_GROUP_DATAVIEW_PREFIX}${Date.now()}`, - category: DataviewCategory.VesselGroups, - config: { - colorCyclingType: 'fill' as ColorCyclingType, - visible: true, - filters: { - 'vessel-groups': [vesselGroupId], +): DataviewInstance | undefined => { + if (vesselGroupId) { + const contextDataviewInstance = { + id: `${VESSEL_GROUP_DATAVIEW_PREFIX}${Date.now()}`, + category: DataviewCategory.VesselGroups, + config: { + colorCyclingType: 'fill' as ColorCyclingType, + visible: true, + filters: { + 'vessel-groups': [vesselGroupId], + }, }, - }, - dataviewId: PRESENCE_DATAVIEW_SLUG, + dataviewId: PRESENCE_DATAVIEW_SLUG, + } + return contextDataviewInstance } - return contextDataviewInstance } export const getDataviewInstanceFromDataview = (dataview: Dataview) => { diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index 843123a58d..bc35529ee0 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -3,18 +3,22 @@ import cx from 'classnames' import { useTranslation } from 'react-i18next' import React from 'react' import { Button, ButtonType, ButtonSize } from '@globalfishingwatch/ui-components' +import { VesselGroupUpsert } from '@globalfishingwatch/api-types' import { VesselLastIdentity } from 'features/search/search.slice' import { setVesselGroupEditId, setNewVesselGroupSearchVessels, setVesselGroupsModalOpen, MAX_VESSEL_GROUP_VESSELS, + updateVesselGroupVesselsThunk, + createVesselGroupThunk, } from 'features/vessel-groups/vessel-groups.slice' import { useAppDispatch } from 'features/app/app.hooks' import { IdentityVesselData } from 'features/vessel/vessel.slice' import { ReportVesselWithDatasets } from 'features/area-report/reports.selectors' +import { getCurrentIdentityVessel } from 'features/vessel/vessel.utils' import styles from './VesselGroupListTooltip.module.css' -import VesselGroupListTooltip from './VesselGroupListTooltip' +import VesselGroupListTooltip, { NEW_VESSEL_GROUP_ID } from './VesselGroupListTooltip' type VesselGroupAddButtonProps = { mode?: 'auto' | 'manual' @@ -79,12 +83,36 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { const handleAddToVesselGroupClick = useCallback( async (vesselGroupId?: string) => { if (mode === 'auto') { - console.log('TODO') - // const vesselGroup = { - // id: vesselGroupId, - // vessels, - // } - // dispatchedAction = await dispatch(updateVesselGroupThunk(vesselGroup)) + const vesselGroup: VesselGroupUpsert = { + vessels: vessels.flatMap((vessel) => { + const { id, dataset } = getCurrentIdentityVessel(vessel as IdentityVesselData) + if (!id || !dataset) { + return [] + } + return { + vesselId: id, + dataset: dataset as any, + } + }), + } + const thunkFn: any = + vesselGroupId === NEW_VESSEL_GROUP_ID + ? createVesselGroupThunk + : updateVesselGroupVesselsThunk + if (vesselGroupId === NEW_VESSEL_GROUP_ID) { + const name = prompt(t('vesselGroup.enterName', 'Enter vessel group name')) + if (name) { + vesselGroup.name = name + } + } else { + vesselGroup.id = vesselGroupId + } + const dispatchedAction = await dispatch(thunkFn(vesselGroup)) + if (thunkFn.fulfilled.match(dispatchedAction)) { + if (onAddToVesselGroup) { + onAddToVesselGroup(dispatchedAction.payload.id) + } + } } else { const vesselsWithDataset = vessels.map((vessel) => ({ ...vessel, @@ -94,7 +122,7 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { (vessel as ReportVesselWithDatasets)?.infoDataset?.id, })) if (vesselsWithDataset?.length) { - if (vesselGroupId) { + if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { dispatch(setVesselGroupEditId(vesselGroupId)) } dispatch(setNewVesselGroupSearchVessels(vesselsWithDataset)) @@ -102,12 +130,12 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { } else { console.warn('No related activity datasets founds for', vesselsWithDataset) } - } - if (onAddToVesselGroup) { - onAddToVesselGroup(vesselGroupId) + if (onAddToVesselGroup) { + onAddToVesselGroup(vesselGroupId) + } } }, - [dispatch, mode, onAddToVesselGroup, vessels] + [dispatch, mode, onAddToVesselGroup, t, vessels] ) return ( diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts index 0bf84b7a91..9646fa2580 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts @@ -336,7 +336,7 @@ export const createVesselGroupThunk = createAsyncThunk( async (vesselGroupCreate: VesselGroupUpsert, { dispatch, getState }) => { const vesselGroupUpsert: VesselGroupUpsert = { ...vesselGroupCreate, - vessels: removeDuplicatedVesselGroupvessels(vesselGroupCreate.vessels), + vessels: removeDuplicatedVesselGroupvessels(vesselGroupCreate.vessels || []), } const saveVesselGroup: any = async (vesselGroup: VesselGroupUpsert, tries = 0) => { let vesselGroupUpdated: VesselGroup @@ -366,12 +366,11 @@ export const updateVesselGroupThunk = createAsyncThunk( 'vessel-groups/update', async (vesselGroupUpsert: VesselGroupUpsert & { id: string }) => { const { id, ...rest } = vesselGroupUpsert - const url = `/vessel-groups/${id}` const vesselGroup: VesselGroupUpsert = { ...rest, - vessels: removeDuplicatedVesselGroupvessels(rest.vessels), + vessels: removeDuplicatedVesselGroupvessels(rest.vessels || []), } - const vesselGroupUpdated = await GFWAPI.fetch(url, { + const vesselGroupUpdated = await GFWAPI.fetch(`/vessel-groups/${id}`, { method: 'PATCH', body: vesselGroup, } as FetchOptions) @@ -379,6 +378,27 @@ export const updateVesselGroupThunk = createAsyncThunk( } ) +export const updateVesselGroupVesselsThunk = createAsyncThunk( + 'vessel-groups/update-vessels', + async ( + { id, vessels = [] }: Pick & { id: string }, + { getState, dispatch } + ) => { + let vesselGroup = selectVesselGroupById(id)(getState() as any) + if (!vesselGroup) { + vesselGroup = await GFWAPI.fetch(`/vessel-groups/${id}`) + } + if (vesselGroup) { + return dispatch( + updateVesselGroupThunk({ + id: vesselGroup.id, + vessels: [...vesselGroup.vessels, ...vessels], + }) + ) + } + } +) + export const deleteVesselGroupThunk = createAsyncThunk< VesselGroup, string, diff --git a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx index 01257ced96..6ba0d24b6f 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx @@ -4,6 +4,8 @@ import { SortableContext } from '@dnd-kit/sortable' import cx from 'classnames' import { useTranslation, Trans } from 'react-i18next' import { IconButton, Switch } from '@globalfishingwatch/ui-components' +import { DatasetTypes, ResourceStatus } from '@globalfishingwatch/api-types' +import { resolveDataviewDatasetResource } from '@globalfishingwatch/dataviews-client' import { useLocationConnect } from 'routes/routes.hook' import styles from 'features/workspace/shared/Sections.module.css' import { isBasicSearchAllowed } from 'features/search/search.selectors' @@ -21,9 +23,13 @@ import { } from 'features/timebar/timebar-vessel.hooks' import { getVesselShipNameLabel } from 'utils/info' import { selectResources, ResourcesState } from 'features/resources/resources.slice' -import { VESSEL_DATAVIEW_INSTANCE_PREFIX } from 'features/dataviews/dataviews.utils' +import { + getVesselGroupDataviewInstance, + VESSEL_DATAVIEW_INSTANCE_PREFIX, +} from 'features/dataviews/dataviews.utils' import { selectReadOnly } from 'features/app/selectors/app.selectors' import VesselGroupAddButton from 'features/vessel-groups/VesselGroupAddButton' +import { NEW_VESSEL_GROUP_ID } from 'features/vessel-groups/VesselGroupListTooltip' import VesselEventsLegend from './VesselEventsLegend' import VesselLayerPanel from './VesselLayerPanel' import VesselsFromPositions from './VesselsFromPositions' @@ -68,9 +74,20 @@ function VesselsSection(): React.ReactElement { deleteDataviewInstance(dataviews.map((d) => d.id)) }, [dataviews, deleteDataviewInstance]) - const onAddToVesselGroupClick = useCallback(() => { - console.log('todo') - }, []) + const onAddToVesselGroupClick = useCallback( + (vesselGroupId?: string) => { + if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { + const dataviewInstance = getVesselGroupDataviewInstance(vesselGroupId) + if (dataviewInstance) { + const dataviewsToDelete = dataviews.flatMap((d) => + d.config?.visible ? { id: d.id, deleted: true } : [] + ) + upsertDataviewInstance([...dataviewsToDelete, dataviewInstance]) + } + } + }, + [dataviews, upsertDataviewInstance] + ) const onSetSortOrderClick = useCallback(() => { sortOrder.current = sortOrder.current === 'ASC' ? 'DESC' : 'ASC' @@ -104,6 +121,20 @@ function VesselsSection(): React.ReactElement { }) }, [dispatchLocation, workspace]) + const vesselResources = dataviews.flatMap((dataview) => { + if (!dataview.config?.visible) { + return [] + } + const { url: infoUrl } = resolveDataviewDatasetResource(dataview, DatasetTypes.Vessels) + return resources[infoUrl] || [] + }) + const areVesselsLoading = vesselResources.some( + (resource) => resource.status === ResourceStatus.Loading + ) + const vesselsToVesselGroup = areVesselsLoading + ? [] + : vesselResources.map((resource) => resource.data) + return (
@@ -123,9 +154,15 @@ function VesselsSection(): React.ReactElement {
{dataviews.length > 1 && ( - + +export type VesselGroupUpsert = Partial> From 8a6643167e073ab6430b822c04e3db62f709a121 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 09:55:53 +0200 Subject: [PATCH 02/23] fix build --- .../search/advanced/SearchAdvancedResults.tsx | 6 +++--- apps/fishing-map/features/search/search.slice.ts | 4 +++- .../features/vessel-groups/VesselGroupAddButton.tsx | 11 ++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx b/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx index 99d17189b7..248384835f 100644 --- a/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx +++ b/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx @@ -191,7 +191,7 @@ function SearchAdvancedResults({ fetchResults, fetchMoreResults }: SearchCompone accessorFn: (vessel: IdentityVesselData) => { const bestIdentityMatch = getBestMatchCriteriaIdentity(vessel) const vesselData = getSearchIdentityResolved(vessel) - const { shipname, nShipname } = vesselData + const { dataset, shipname, nShipname } = vesselData const otherNamesLabel = getVesselOtherNamesLabel(getOtherVesselNames(vessel, nShipname)) const { transmissionDateFrom, transmissionDateTo } = vesselData const name = shipname ? formatInfoField(shipname, 'name') : EMPTY_FIELD_PLACEHOLDER @@ -201,7 +201,7 @@ function SearchAdvancedResults({ fetchResults, fetchMoreResults }: SearchCompone return ( onVesselClick(e, vesselData)} query={vesselQuery} @@ -328,7 +328,7 @@ function SearchAdvancedResults({ fetchResults, fetchMoreResults }: SearchCompone header: t('vessel.transmission_other', 'Transmissions'), }, ] - }, [fetchResults, i18n.language, onVesselClick, searchFilters?.infoSource, t]) + }, [fetchResults, i18n.language, isSearchLocation, onVesselClick, searchFilters?.infoSource, t]) const fetchMoreOnBottomReached = useCallback(() => { if (tableContainerRef.current) { diff --git a/apps/fishing-map/features/search/search.slice.ts b/apps/fishing-map/features/search/search.slice.ts index da819100e6..b10f1b6f29 100644 --- a/apps/fishing-map/features/search/search.slice.ts +++ b/apps/fishing-map/features/search/search.slice.ts @@ -23,7 +23,9 @@ import { VesselSearchState } from 'types' import { IdentityVesselData, VesselDataIdentity } from 'features/vessel/vessel.slice' import { getVesselId, getVesselIdentities } from 'features/vessel/vessel.utils' -export type VesselLastIdentity = Omit & VesselDataIdentity +export type VesselLastIdentity = Omit & { + dataset: Dataset | string +} & VesselDataIdentity interface SearchState { selectedVessels: string[] diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index bc35529ee0..bb92ac6525 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -91,11 +91,11 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { } return { vesselId: id, - dataset: dataset as any, + dataset: typeof dataset === 'string' ? dataset : dataset.id, } }), } - const thunkFn: any = + const thunkFn = vesselGroupId === NEW_VESSEL_GROUP_ID ? createVesselGroupThunk : updateVesselGroupVesselsThunk @@ -107,7 +107,7 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { } else { vesselGroup.id = vesselGroupId } - const dispatchedAction = await dispatch(thunkFn(vesselGroup)) + const dispatchedAction = await dispatch(thunkFn(vesselGroup as any)) if (thunkFn.fulfilled.match(dispatchedAction)) { if (onAddToVesselGroup) { onAddToVesselGroup(dispatchedAction.payload.id) @@ -118,8 +118,9 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { ...vessel, id: (vessel as VesselLastIdentity)?.id || (vessel as ReportVesselWithDatasets)?.vesselId, dataset: - (vessel as VesselLastIdentity)?.dataset?.id || - (vessel as ReportVesselWithDatasets)?.infoDataset?.id, + typeof vessel?.dataset === 'string' + ? vessel.dataset + : vessel.dataset?.id || (vessel as ReportVesselWithDatasets)?.infoDataset?.id, })) if (vesselsWithDataset?.length) { if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { From 2137a522827868b3bf4bc1d9b06430b6bbc034b8 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 09:56:03 +0200 Subject: [PATCH 03/23] fix userVesselGroups icon styles --- .../features/user/UserVesselGroups.tsx | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/fishing-map/features/user/UserVesselGroups.tsx b/apps/fishing-map/features/user/UserVesselGroups.tsx index 5e2857daa2..0107261e34 100644 --- a/apps/fishing-map/features/user/UserVesselGroups.tsx +++ b/apps/fishing-map/features/user/UserVesselGroups.tsx @@ -83,21 +83,23 @@ function UserVesselGroups() { {getVesselGroupLabel(vesselGroup)}{' '} ({vesselGroup.vessels.length}) - - onEditClick(vesselGroup)} - /> - onDeleteClick(vesselGroup)} - /> +
+ + onEditClick(vesselGroup)} + /> + onDeleteClick(vesselGroup)} + /> +
) }) From 33771db437b479d623c8014a5b23dfc1734ea882 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 10:01:50 +0200 Subject: [PATCH 04/23] fix back vesselGroup --- apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx index 8ecaef2fa7..d000e94ce6 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx @@ -159,6 +159,7 @@ function VesselGroupModal(): React.ReactElement { dispatch(setVesselGroupSearchVessels(undefined)) dispatch(resetVesselGroupStatus('')) abortSearch() + setShowBackButton(false) } else { close() } From a7962bb5cc962f5e93c38ad0b35df54a099f796a Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 16:19:05 +0200 Subject: [PATCH 05/23] add to existing vesselGroup directly --- .../vessel-groups/VesselGroupAddButton.tsx | 91 ++++------------ .../VesselGroupListTooltip.module.css | 8 ++ .../vessel-groups/VesselGroupListTooltip.tsx | 10 +- .../vessel-groups/vessel-groups.hooks.ts | 100 ++++++++++++++++-- .../vessel-groups/vessel-groups.slice.ts | 26 +++-- .../public/locales/source/translations.json | 1 - libs/api-types/src/vesselGroups.ts | 2 +- 7 files changed, 147 insertions(+), 91 deletions(-) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index bb92ac6525..d0a6fd52a0 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -3,28 +3,20 @@ import cx from 'classnames' import { useTranslation } from 'react-i18next' import React from 'react' import { Button, ButtonType, ButtonSize } from '@globalfishingwatch/ui-components' -import { VesselGroupUpsert } from '@globalfishingwatch/api-types' -import { VesselLastIdentity } from 'features/search/search.slice' -import { - setVesselGroupEditId, - setNewVesselGroupSearchVessels, - setVesselGroupsModalOpen, - MAX_VESSEL_GROUP_VESSELS, - updateVesselGroupVesselsThunk, - createVesselGroupThunk, -} from 'features/vessel-groups/vessel-groups.slice' -import { useAppDispatch } from 'features/app/app.hooks' -import { IdentityVesselData } from 'features/vessel/vessel.slice' -import { ReportVesselWithDatasets } from 'features/area-report/reports.selectors' -import { getCurrentIdentityVessel } from 'features/vessel/vessel.utils' +import { MAX_VESSEL_GROUP_VESSELS } from 'features/vessel-groups/vessel-groups.slice' import styles from './VesselGroupListTooltip.module.css' import VesselGroupListTooltip, { NEW_VESSEL_GROUP_ID } from './VesselGroupListTooltip' +import { + AddVesselGroupVessel, + useVesselGroupsModal, + useVesselGroupsUpdate, +} from './vessel-groups.hooks' type VesselGroupAddButtonProps = { mode?: 'auto' | 'manual' children?: React.ReactNode - vessels: (VesselLastIdentity | ReportVesselWithDatasets | IdentityVesselData)[] - onAddToVesselGroup?: (vesselGroupId?: string) => void + vessels: AddVesselGroupVessel[] + onAddToVesselGroup?: (vesselGroupId: string) => void } type VesselGroupAddButtonToggleProps = { @@ -71,72 +63,27 @@ export function VesselGroupAddActionButton({ } function VesselGroupAddButton(props: VesselGroupAddButtonProps) { - const { - vessels, - onAddToVesselGroup, - mode = 'manual', - children = , - } = props - const { t } = useTranslation() - const dispatch = useAppDispatch() + const { vessels, onAddToVesselGroup, children = } = props + const addVesselsToVesselGroup = useVesselGroupsUpdate() + const createVesselGroupWithVessels = useVesselGroupsModal() const handleAddToVesselGroupClick = useCallback( - async (vesselGroupId?: string) => { - if (mode === 'auto') { - const vesselGroup: VesselGroupUpsert = { - vessels: vessels.flatMap((vessel) => { - const { id, dataset } = getCurrentIdentityVessel(vessel as IdentityVesselData) - if (!id || !dataset) { - return [] - } - return { - vesselId: id, - dataset: typeof dataset === 'string' ? dataset : dataset.id, - } - }), - } - const thunkFn = - vesselGroupId === NEW_VESSEL_GROUP_ID - ? createVesselGroupThunk - : updateVesselGroupVesselsThunk - if (vesselGroupId === NEW_VESSEL_GROUP_ID) { - const name = prompt(t('vesselGroup.enterName', 'Enter vessel group name')) - if (name) { - vesselGroup.name = name - } - } else { - vesselGroup.id = vesselGroupId - } - const dispatchedAction = await dispatch(thunkFn(vesselGroup as any)) - if (thunkFn.fulfilled.match(dispatchedAction)) { - if (onAddToVesselGroup) { - onAddToVesselGroup(dispatchedAction.payload.id) + async (vesselGroupId: string) => { + if (vesselGroupId !== NEW_VESSEL_GROUP_ID) { + if (vessels.length) { + const vesselGroup = await addVesselsToVesselGroup(vesselGroupId, vessels) + if (onAddToVesselGroup && vesselGroup) { + onAddToVesselGroup(vesselGroup?.id) } } } else { - const vesselsWithDataset = vessels.map((vessel) => ({ - ...vessel, - id: (vessel as VesselLastIdentity)?.id || (vessel as ReportVesselWithDatasets)?.vesselId, - dataset: - typeof vessel?.dataset === 'string' - ? vessel.dataset - : vessel.dataset?.id || (vessel as ReportVesselWithDatasets)?.infoDataset?.id, - })) - if (vesselsWithDataset?.length) { - if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { - dispatch(setVesselGroupEditId(vesselGroupId)) - } - dispatch(setNewVesselGroupSearchVessels(vesselsWithDataset)) - dispatch(setVesselGroupsModalOpen(true)) - } else { - console.warn('No related activity datasets founds for', vesselsWithDataset) - } + createVesselGroupWithVessels(vesselGroupId, vessels) if (onAddToVesselGroup) { onAddToVesselGroup(vesselGroupId) } } }, - [dispatch, mode, onAddToVesselGroup, t, vessels] + [addVesselsToVesselGroup, createVesselGroupWithVessels, onAddToVesselGroup, vessels] ) return ( diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.module.css b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.module.css index 4393efb627..bb3bd236cf 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.module.css +++ b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.module.css @@ -1,12 +1,20 @@ .groupOptions { padding: var(--space-S); padding-right: 0; + max-height: 40rem; + overflow: auto; } .groupOption { cursor: pointer; padding: var(--space-XS); padding-right: var(--space-M); + display: flex; + align-items: center; +} + +.groupLoading { + margin-left: var(--space-S); } .groupOptionNew { diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx index 2a50ab7422..0a1a2e68cd 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx @@ -3,7 +3,7 @@ import cx from 'classnames' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import React from 'react' -import { Popover } from '@globalfishingwatch/ui-components' +import { Popover, Spinner } from '@globalfishingwatch/ui-components' import { useVesselGroupsOptions } from 'features/vessel-groups/vessel-groups.hooks' import { selectHasUserGroupsPermissions } from 'features/user/selectors/user.permissions.selectors' import styles from './VesselGroupListTooltip.module.css' @@ -30,6 +30,7 @@ function VesselGroupListTooltip(props: VesselGroupListTooltipProps) { {hasUserGroupsPermissions && ( @@ -45,9 +46,14 @@ function VesselGroupListTooltip(props: VesselGroupListTooltipProps) {
  • onAddToVesselGroup?.(group.id)} + onClick={ + !group.loading && onAddToVesselGroup + ? () => onAddToVesselGroup(group.id) + : undefined + } > {group.label} + {group.loading && }
  • ))} diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts index 3d420ab339..c4204a896c 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts @@ -1,22 +1,104 @@ -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { MultiSelectOption } from '@globalfishingwatch/ui-components' +import { VesselGroup } from '@globalfishingwatch/api-types' import { selectAllVisibleVesselGroups } from 'features/user/selectors/user.permissions.selectors' import { getVesselGroupLabel } from 'features/vessel-groups/vessel-groups.utils' +import { IdentityVesselData } from 'features/vessel/vessel.slice' +import { getCurrentIdentityVessel } from 'features/vessel/vessel.utils' +import { VesselLastIdentity } from 'features/search/search.slice' +import { ReportVesselWithDatasets } from 'features/area-report/reports.selectors' +import { useAppDispatch } from 'features/app/app.hooks' +import { + selectVesselGroupsStatusId, + setNewVesselGroupSearchVessels, + setVesselGroupEditId, + setVesselGroupsModalOpen, + UpdateVesselGroupThunkParams, + updateVesselGroupVesselsThunk, +} from './vessel-groups.slice' +import { NEW_VESSEL_GROUP_ID } from './VesselGroupListTooltip' + +export type AddVesselGroupVessel = + | VesselLastIdentity + | ReportVesselWithDatasets + | IdentityVesselData export const useVesselGroupsOptions = () => { const { t } = useTranslation() const vesselGroups = useSelector(selectAllVisibleVesselGroups) + const vesselGroupsStatusId = useSelector(selectVesselGroupsStatusId) return useMemo(() => { - const vesselGroupsOptions: MultiSelectOption[] = vesselGroups.map((vesselGroup) => ({ - id: vesselGroup.id.toString(), - label: t('vesselGroup.label', `{{name}} ({{count}} IDs)`, { - name: getVesselGroupLabel(vesselGroup), - count: vesselGroup.vessels.length, - }), - })) + const vesselGroupsOptions: (MultiSelectOption & { loading?: boolean })[] = vesselGroups.map( + (vesselGroup) => ({ + id: vesselGroup.id.toString(), + label: t('vesselGroup.label', `{{name}} ({{count}} IDs)`, { + name: getVesselGroupLabel(vesselGroup), + count: vesselGroup.vessels.length, + }), + loading: vesselGroup.id === vesselGroupsStatusId, + }) + ) return vesselGroupsOptions - }, [t, vesselGroups]) + }, [t, vesselGroups, vesselGroupsStatusId]) +} + +export const useVesselGroupsUpdate = () => { + const dispatch = useAppDispatch() + const addVesselsToVesselGroup = useCallback( + async (vesselGroupId: string, vessels: AddVesselGroupVessel[]) => { + const vesselGroup: UpdateVesselGroupThunkParams = { + id: vesselGroupId, + vessels: vessels.flatMap((vessel) => { + const { id, dataset } = getCurrentIdentityVessel(vessel as IdentityVesselData) + if (!id || !dataset) { + return [] + } + return { + vesselId: id, + dataset: typeof dataset === 'string' ? dataset : dataset.id, + } + }), + } + const dispatchedAction = await dispatch(updateVesselGroupVesselsThunk(vesselGroup)) + if (updateVesselGroupVesselsThunk.fulfilled.match(dispatchedAction)) { + return dispatchedAction.payload?.payload as VesselGroup + } else { + return undefined + } + }, + [dispatch] + ) + + return addVesselsToVesselGroup +} + +export const useVesselGroupsModal = () => { + const dispatch = useAppDispatch() + const createVesselGroupWithVessels = useCallback( + async (vesselGroupId: string, vessels: AddVesselGroupVessel[]) => { + const vesselsWithDataset = vessels.map((vessel) => ({ + ...vessel, + id: (vessel as VesselLastIdentity)?.id || (vessel as ReportVesselWithDatasets)?.vesselId, + dataset: + typeof vessel?.dataset === 'string' + ? vessel.dataset + : vessel.dataset?.id || (vessel as ReportVesselWithDatasets)?.infoDataset?.id, + })) + if (vesselsWithDataset?.length) { + if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { + dispatch(setVesselGroupEditId(vesselGroupId)) + } + dispatch(setNewVesselGroupSearchVessels(vesselsWithDataset)) + dispatch(setVesselGroupsModalOpen(true)) + } else { + console.warn('No related activity datasets founds for', vesselsWithDataset) + } + }, + [dispatch] + ) + + return createVesselGroupWithVessels } diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts index 9646fa2580..e368759d6b 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts @@ -331,6 +331,16 @@ const removeDuplicatedVesselGroupvessels = (vessels: VesselGroupVessel[]) => { return uniqBy(vessels, (vessel) => [vessel.vesselId, vessel.dataset].join(',')) } +export const fetchVesselGroupByIdThunk = createAsyncThunk( + 'vessel-groups/fetchById', + async (vesselGroupId: string) => { + if (vesselGroupId) { + const vesselGroup = await GFWAPI.fetch(`/vessel-groups/${id}`) + return vesselGroup + } + } +) + export const createVesselGroupThunk = createAsyncThunk( 'vessel-groups/create', async (vesselGroupCreate: VesselGroupUpsert, { dispatch, getState }) => { @@ -362,9 +372,12 @@ export const createVesselGroupThunk = createAsyncThunk( } ) +export type UpdateVesselGroupThunkParams = VesselGroupUpsert & { + id: string +} export const updateVesselGroupThunk = createAsyncThunk( 'vessel-groups/update', - async (vesselGroupUpsert: VesselGroupUpsert & { id: string }) => { + async (vesselGroupUpsert: UpdateVesselGroupThunkParams) => { const { id, ...rest } = vesselGroupUpsert const vesselGroup: VesselGroupUpsert = { ...rest, @@ -380,13 +393,13 @@ export const updateVesselGroupThunk = createAsyncThunk( export const updateVesselGroupVesselsThunk = createAsyncThunk( 'vessel-groups/update-vessels', - async ( - { id, vessels = [] }: Pick & { id: string }, - { getState, dispatch } - ) => { + async ({ id, vessels = [] }: UpdateVesselGroupThunkParams, { getState, dispatch }) => { let vesselGroup = selectVesselGroupById(id)(getState() as any) if (!vesselGroup) { - vesselGroup = await GFWAPI.fetch(`/vessel-groups/${id}`) + const action = await dispatch(fetchVesselGroupByIdThunk(id)) + if (fetchVesselGroupByIdThunk.fulfilled.match(action) && action.payload) { + vesselGroup = action.payload + } } if (vesselGroup) { return dispatch( @@ -513,6 +526,7 @@ export const { slice: vesselGroupsSlice, entityAdapter } = createAsyncSlice< }, thunks: { fetchThunk: fetchUserVesselGroupsThunk, + fetchByIdThunk: fetchVesselGroupByIdThunk, updateThunk: updateVesselGroupThunk, createThunk: createVesselGroupThunk, deleteThunk: deleteVesselGroupThunk, diff --git a/apps/fishing-map/public/locales/source/translations.json b/apps/fishing-map/public/locales/source/translations.json index 70989e102b..c0a1282ff7 100644 --- a/apps/fishing-map/public/locales/source/translations.json +++ b/apps/fishing-map/public/locales/source/translations.json @@ -1004,7 +1004,6 @@ "createNewGroup": "Create new group", "csvError": "Uploaded CSV file has multiple columns and there is no obvious ID column", "edit": "Edit list of vessels", - "enterName": "Enter vessel group name", "emptyState": "Your vessel groups will appear here", "groupName": "Group name", "idField": "ID field", diff --git a/libs/api-types/src/vesselGroups.ts b/libs/api-types/src/vesselGroups.ts index b27c8db03e..f1481a1589 100644 --- a/libs/api-types/src/vesselGroups.ts +++ b/libs/api-types/src/vesselGroups.ts @@ -15,4 +15,4 @@ export interface VesselGroup { createdAt?: string } -export type VesselGroupUpsert = Partial> +export type VesselGroupUpsert = Partial> From 83bea4472cf42bbb7e89a6aa7625747c5da44fca Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 16:29:56 +0200 Subject: [PATCH 06/23] not duplicate vessel groups on update --- .../vessel-groups/VesselGroupAddButton.tsx | 3 ++- .../vessel-groups/VesselGroupListTooltip.tsx | 7 +++--- .../vessel-groups/vessel-groups.hooks.ts | 3 ++- .../vessel-groups/vessel-groups.selectors.ts | 8 +++++++ .../vessel-groups/VesselGroupsSection.tsx | 6 ++--- .../workspace/vessels/VesselsSection.tsx | 24 ++++++++++++------- 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index d0a6fd52a0..b318bf2332 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -5,11 +5,12 @@ import React from 'react' import { Button, ButtonType, ButtonSize } from '@globalfishingwatch/ui-components' import { MAX_VESSEL_GROUP_VESSELS } from 'features/vessel-groups/vessel-groups.slice' import styles from './VesselGroupListTooltip.module.css' -import VesselGroupListTooltip, { NEW_VESSEL_GROUP_ID } from './VesselGroupListTooltip' +import VesselGroupListTooltip from './VesselGroupListTooltip' import { AddVesselGroupVessel, useVesselGroupsModal, useVesselGroupsUpdate, + NEW_VESSEL_GROUP_ID, } from './vessel-groups.hooks' type VesselGroupAddButtonProps = { diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx index 0a1a2e68cd..606a8ddd82 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx @@ -4,12 +4,13 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import React from 'react' import { Popover, Spinner } from '@globalfishingwatch/ui-components' -import { useVesselGroupsOptions } from 'features/vessel-groups/vessel-groups.hooks' +import { + NEW_VESSEL_GROUP_ID, + useVesselGroupsOptions, +} from 'features/vessel-groups/vessel-groups.hooks' import { selectHasUserGroupsPermissions } from 'features/user/selectors/user.permissions.selectors' import styles from './VesselGroupListTooltip.module.css' -export const NEW_VESSEL_GROUP_ID = 'new-vessel-group' - type VesselGroupListTooltipProps = { children?: React.ReactNode onAddToVesselGroup?: (vesselGroupId: string) => void diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts index c4204a896c..3c31aa448e 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts @@ -18,7 +18,8 @@ import { UpdateVesselGroupThunkParams, updateVesselGroupVesselsThunk, } from './vessel-groups.slice' -import { NEW_VESSEL_GROUP_ID } from './VesselGroupListTooltip' + +export const NEW_VESSEL_GROUP_ID = 'new-vessel-group' export type AddVesselGroupVessel = | VesselLastIdentity diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts index dfe792a602..9788d4ed66 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts @@ -8,6 +8,7 @@ import { } from 'features/vessel-groups/vessel-groups.slice' import { selectWorkspaceDataviewInstances } from 'features/workspace/workspace.selectors' import { selectHasUserGroupsPermissions } from 'features/user/selectors/user.permissions.selectors' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.selectors' export const selectAllVesselGroupSearchVessels = createSelector( [selectVesselGroupSearchVessels, selectNewVesselGroupSearchVessels], @@ -52,3 +53,10 @@ export const selectIsVessselGroupsFiltering = createSelector( return workspaceVesselGroupIds.length > 0 } ) + +export const selectVessselGroupsInWorkspace = createSelector( + [selectDataviewInstancesResolvedVisible], + (dataviews = []) => { + return dataviews.flatMap((dataview) => dataview.config?.filters?.['vessel-groups'] || []) + } +) diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx index aac74a39c9..fb9db8578d 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx @@ -3,20 +3,18 @@ import { useSelector } from 'react-redux' import { SortableContext } from '@dnd-kit/sortable' import cx from 'classnames' import { useTranslation } from 'react-i18next' -import { IconButton } from '@globalfishingwatch/ui-components' import styles from 'features/workspace/shared/Sections.module.css' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' import { selectReadOnly } from 'features/app/selectors/app.selectors' -import VesselGroupListTooltip, { - NEW_VESSEL_GROUP_ID, -} from 'features/vessel-groups/VesselGroupListTooltip' +import VesselGroupListTooltip from 'features/vessel-groups/VesselGroupListTooltip' import { getVesselGroupDataviewInstance } from 'features/dataviews/dataviews.utils' import { selectVesselGroupDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' import { selectAllVisibleVesselGroups } from 'features/user/selectors/user.permissions.selectors' import { useAppDispatch } from 'features/app/app.hooks' import { setVesselGroupsModalOpen } from 'features/vessel-groups/vessel-groups.slice' import UserLoggedIconButton from 'features/user/UserLoggedIconButton' +import { NEW_VESSEL_GROUP_ID } from 'features/vessel-groups/vessel-groups.hooks' import VesselGroupLayerPanel from './VesselGroupsLayerPanel' function VesselGroupSection(): React.ReactElement { diff --git a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx index 6ba0d24b6f..49b273ec1e 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx @@ -29,7 +29,8 @@ import { } from 'features/dataviews/dataviews.utils' import { selectReadOnly } from 'features/app/selectors/app.selectors' import VesselGroupAddButton from 'features/vessel-groups/VesselGroupAddButton' -import { NEW_VESSEL_GROUP_ID } from 'features/vessel-groups/VesselGroupListTooltip' +import { selectVessselGroupsInWorkspace } from 'features/vessel-groups/vessel-groups.selectors' +import { NEW_VESSEL_GROUP_ID } from 'features/vessel-groups/vessel-groups.hooks' import VesselEventsLegend from './VesselEventsLegend' import VesselLayerPanel from './VesselLayerPanel' import VesselsFromPositions from './VesselsFromPositions' @@ -48,6 +49,7 @@ function VesselsSection(): React.ReactElement { const dataviews = useSelector(selectVesselsDataviews) const workspace = useSelector(selectWorkspace) const guestUser = useSelector(selectIsGuestUser) + const vesselGroupsInWorkspace = useSelector(selectVessselGroupsInWorkspace) const { upsertDataviewInstance, deleteDataviewInstance } = useDataviewInstancesConnect() const vesselTracksData = useTimebarVesselTracksData() const hasVesselsWithNoTrack = hasTracksWithNoData(vesselTracksData) @@ -77,16 +79,20 @@ function VesselsSection(): React.ReactElement { const onAddToVesselGroupClick = useCallback( (vesselGroupId?: string) => { if (vesselGroupId && vesselGroupId !== NEW_VESSEL_GROUP_ID) { - const dataviewInstance = getVesselGroupDataviewInstance(vesselGroupId) - if (dataviewInstance) { - const dataviewsToDelete = dataviews.flatMap((d) => - d.config?.visible ? { id: d.id, deleted: true } : [] - ) - upsertDataviewInstance([...dataviewsToDelete, dataviewInstance]) - } + const isVesselGroupInWorkspace = vesselGroupsInWorkspace.includes(vesselGroupId) + const dataviewInstance = !isVesselGroupInWorkspace + ? getVesselGroupDataviewInstance(vesselGroupId) + : undefined + const dataviewsToDelete = dataviews.flatMap((d) => + d.config?.visible ? { id: d.id, deleted: true } : [] + ) + upsertDataviewInstance([ + ...dataviewsToDelete, + ...(dataviewInstance ? [dataviewInstance] : []), + ]) } }, - [dataviews, upsertDataviewInstance] + [dataviews, upsertDataviewInstance, vesselGroupsInWorkspace] ) const onSetSortOrderClick = useCallback(() => { From e891aed144e1484d43ec15e2d5b3ccfb2c363cfd Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 17:53:41 +0200 Subject: [PATCH 07/23] avoid request vessel groups multiple times --- .../features/vessel-groups/vessel-groups.selectors.ts | 7 ------- .../features/vessel-groups/vessel-groups.slice.ts | 11 +++++++++-- .../features/workspace/vessels/VesselsSection.tsx | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts index 9788d4ed66..76919948ca 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts @@ -53,10 +53,3 @@ export const selectIsVessselGroupsFiltering = createSelector( return workspaceVesselGroupIds.length > 0 } ) - -export const selectVessselGroupsInWorkspace = createSelector( - [selectDataviewInstancesResolvedVisible], - (dataviews = []) => { - return dataviews.flatMap((dataview) => dataview.config?.filters?.['vessel-groups'] || []) - } -) diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts index e368759d6b..b2466fa325 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts @@ -25,6 +25,7 @@ import { } from 'utils/async-slice' import { DEFAULT_PAGINATION_PARAMS } from 'data/config' import { getVesselId } from 'features/vessel/vessel.utils' +import { RootState } from 'store' import { fetchDatasetByIdThunk, selectDatasetById } from '../datasets/datasets.slice' export const MAX_VESSEL_GROUP_VESSELS = 1000 @@ -280,10 +281,16 @@ export const getVesselInVesselGroupThunk = createAsyncThunk( export const fetchWorkspaceVesselGroupsThunk = createAsyncThunk( 'workspace-vessel-groups/fetch', - async (ids: string[] = [], { signal, rejectWithValue }) => { + async (ids: string[] = [], { signal, rejectWithValue, getState }) => { + const vesselGroupsLoaded = (selectAllVesselGroups(getState() as RootState) || [])?.map( + (vg) => vg.id + ) + const vesselGroupsNotLoaded = Array.from( + new Set(ids.filter((id) => !vesselGroupsLoaded.includes(id))) + ) try { const vesselGroupsParams = { - ...(ids?.length && { ids }), + ...(vesselGroupsNotLoaded?.length && { ids: vesselGroupsNotLoaded }), cache: false, ...DEFAULT_PAGINATION_PARAMS, } diff --git a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx index 49b273ec1e..930215fe4b 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx @@ -29,7 +29,7 @@ import { } from 'features/dataviews/dataviews.utils' import { selectReadOnly } from 'features/app/selectors/app.selectors' import VesselGroupAddButton from 'features/vessel-groups/VesselGroupAddButton' -import { selectVessselGroupsInWorkspace } from 'features/vessel-groups/vessel-groups.selectors' +import { selectWorkspaceVessselGroupsIds } from 'features/vessel-groups/vessel-groups.selectors' import { NEW_VESSEL_GROUP_ID } from 'features/vessel-groups/vessel-groups.hooks' import VesselEventsLegend from './VesselEventsLegend' import VesselLayerPanel from './VesselLayerPanel' @@ -49,7 +49,7 @@ function VesselsSection(): React.ReactElement { const dataviews = useSelector(selectVesselsDataviews) const workspace = useSelector(selectWorkspace) const guestUser = useSelector(selectIsGuestUser) - const vesselGroupsInWorkspace = useSelector(selectVessselGroupsInWorkspace) + const vesselGroupsInWorkspace = useSelector(selectWorkspaceVessselGroupsIds) const { upsertDataviewInstance, deleteDataviewInstance } = useDataviewInstancesConnect() const vesselTracksData = useTimebarVesselTracksData() const hasVesselsWithNoTrack = hasTracksWithNoData(vesselTracksData) From ee79077fa1eba166763a9db2c376f858d7d3d76e Mon Sep 17 00:00:00 2001 From: j8seangel Date: Thu, 29 Aug 2024 18:05:03 +0200 Subject: [PATCH 08/23] include all sources in vessel groups instance --- apps/fishing-map/data/workspaces.ts | 8 ++++++++ apps/fishing-map/features/dataviews/dataviews.utils.ts | 2 ++ 2 files changed, 10 insertions(+) diff --git a/apps/fishing-map/data/workspaces.ts b/apps/fishing-map/data/workspaces.ts index 3e7a2f71d2..58755ff81b 100644 --- a/apps/fishing-map/data/workspaces.ts +++ b/apps/fishing-map/data/workspaces.ts @@ -76,6 +76,14 @@ export const TEMPLATE_DATAVIEW_SLUGS = [ TEMPLATE_CLUSTERS_DATAVIEW_SLUG, ] +export const DEFAULT_PRESENCE_VESSEL_GROUP_DATASETS = [ + 'public-global-presence:v3.0', + 'public-chile-presence:v20211126', + 'public-panama-presence:v20211126', + 'public-norway-presence:v20220112', + 'public-png-presence:v20230210', +] + const PRESENCE_DATAVIEWS = [ VIIRS_MATCH_DATAVIEW_SLUG, // we ensure the + icon woks for the presence category PRESENCE_DATAVIEW_SLUG, // In case the workspace doesn't have the dataview added, diff --git a/apps/fishing-map/features/dataviews/dataviews.utils.ts b/apps/fishing-map/features/dataviews/dataviews.utils.ts index bd4203b28c..a3cee0e811 100644 --- a/apps/fishing-map/features/dataviews/dataviews.utils.ts +++ b/apps/fishing-map/features/dataviews/dataviews.utils.ts @@ -20,6 +20,7 @@ import { TEMPLATE_CLUSTERS_DATAVIEW_SLUG, TEMPLATE_VESSEL_DATAVIEW_SLUG, PRESENCE_DATAVIEW_SLUG, + DEFAULT_PRESENCE_VESSEL_GROUP_DATASETS, } from 'data/workspaces' import { VesselInstanceDatasets, @@ -227,6 +228,7 @@ export const getVesselGroupDataviewInstance = ( filters: { 'vessel-groups': [vesselGroupId], }, + datasets: DEFAULT_PRESENCE_VESSEL_GROUP_DATASETS, }, dataviewId: PRESENCE_DATAVIEW_SLUG, } From bee422691e49da4505b374218d82f92262ffc8c9 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 08:12:12 +0200 Subject: [PATCH 09/23] fix disabled vesselGroupAdd button --- .../features/vessel-groups/VesselGroupAddButton.tsx | 6 +++++- .../features/vessel-groups/VesselGroupListTooltip.tsx | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index b318bf2332..7fb417ee0d 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -87,7 +87,11 @@ function VesselGroupAddButton(props: VesselGroupAddButtonProps) { [addVesselsToVesselGroup, createVesselGroupWithVessels, onAddToVesselGroup, vessels] ) return ( - + ) } diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx index 606a8ddd82..c96cbfa4d2 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx @@ -5,6 +5,7 @@ import { useSelector } from 'react-redux' import React from 'react' import { Popover, Spinner } from '@globalfishingwatch/ui-components' import { + AddVesselGroupVessel, NEW_VESSEL_GROUP_ID, useVesselGroupsOptions, } from 'features/vessel-groups/vessel-groups.hooks' @@ -13,6 +14,7 @@ import styles from './VesselGroupListTooltip.module.css' type VesselGroupListTooltipProps = { children?: React.ReactNode + vessels?: AddVesselGroupVessel[] onAddToVesselGroup?: (vesselGroupId: string) => void } From 411e002b03ffb990270d06cd89524c7d54ee3d90 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 08:44:31 +0200 Subject: [PATCH 10/23] fix vesselGroupModal height --- .../vessel-groups/VesselGroupModal.module.css | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupModal.module.css b/apps/fishing-map/features/vessel-groups/VesselGroupModal.module.css index b2fb99fc06..3faa080496 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupModal.module.css +++ b/apps/fishing-map/features/vessel-groups/VesselGroupModal.module.css @@ -1,9 +1,15 @@ -.modalContainer { +.modal .modalContainer { height: 58rem; display: flex; flex-direction: column; } +@media only screen and (width >= 720px) { + .modal .modalContainer { + height: 58rem; + } +} + .modalContent { display: flex; flex-direction: column; @@ -81,8 +87,7 @@ .vesselsTableContainer { max-height: 340px; - overflow-y: scroll; - overflow-x: hidden; + overflow: hidden scroll; } .vesselsTable { From 452fbf345517e08d532fc07b7737b69d503cf355 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 08:45:27 +0200 Subject: [PATCH 11/23] add vesselGroup layer from modal creation --- .../vessels/ReportVesselsTableFooter.tsx | 2 +- .../features/search/SearchActions.tsx | 2 +- .../vessel-groups/VesselGroupAddButton.tsx | 12 +- .../vessel-groups/VesselGroupListTooltip.tsx | 6 +- .../vessel-groups/VesselGroupModal.tsx | 110 +++++++----------- .../vessel-groups/vessel-groups.selectors.ts | 29 ++++- .../vessel-groups/vessel-groups.slice.ts | 2 +- .../features/workspace/workspace.slice.ts | 7 +- .../public/locales/source/translations.json | 1 + 9 files changed, 94 insertions(+), 77 deletions(-) diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx index 80745beab4..ff38745b0a 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx @@ -95,7 +95,7 @@ export default function ReportVesselsTableFooter({ reportName }: ReportVesselsTa } const onAddToVesselGroup = () => { const dataviewIds = heatmapDataviews.map(({ id }) => id) - dispatch(setVesselGroupConfirmationMode('saveAndNavigate')) + dispatch(setVesselGroupConfirmationMode('saveAndSeeInWorkspace')) if (dataviewIds?.length) { dispatch(setVesselGroupCurrentDataviewIds(dataviewIds)) } diff --git a/apps/fishing-map/features/search/SearchActions.tsx b/apps/fishing-map/features/search/SearchActions.tsx index ceb279cd33..81d163b105 100644 --- a/apps/fishing-map/features/search/SearchActions.tsx +++ b/apps/fishing-map/features/search/SearchActions.tsx @@ -75,7 +75,7 @@ function SearchActions() { const onAddToVesselGroup = () => { const dataviewIds = heatmapDataviews.map(({ id }) => id) - dispatch(setVesselGroupConfirmationMode('saveAndNavigate')) + dispatch(setVesselGroupConfirmationMode('saveAndSeeInWorkspace')) if (dataviewIds?.length) { dispatch(setVesselGroupCurrentDataviewIds(dataviewIds)) } diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx index 7fb417ee0d..52f131aa99 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx @@ -2,8 +2,10 @@ import { useCallback } from 'react' import cx from 'classnames' import { useTranslation } from 'react-i18next' import React from 'react' +import { useSelector } from 'react-redux' import { Button, ButtonType, ButtonSize } from '@globalfishingwatch/ui-components' import { MAX_VESSEL_GROUP_VESSELS } from 'features/vessel-groups/vessel-groups.slice' +import { selectIsGuestUser } from 'features/user/selectors/user.selectors' import styles from './VesselGroupListTooltip.module.css' import VesselGroupListTooltip from './VesselGroupListTooltip' import { @@ -38,17 +40,21 @@ export function VesselGroupAddActionButton({ onToggleClick, }: VesselGroupAddButtonToggleProps) { const { t } = useTranslation() + const guestUser = useSelector(selectIsGuestUser) const tooManyVessels = vessels && vessels?.length > MAX_VESSEL_GROUP_VESSELS + const disabled = guestUser || !vessels?.length || tooManyVessels return ( - + {workspaceToNavigate && ( + + )}
    ))}
    diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts index 76919948ca..63fb2bb433 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts @@ -1,14 +1,20 @@ import { createSelector } from '@reduxjs/toolkit' import { isAdvancedSearchAllowed } from 'features/search/search.selectors' -import { selectUrlDataviewInstances } from 'routes/routes.selectors' +import { selectLocationQuery, selectUrlDataviewInstances } from 'routes/routes.selectors' import { MAX_VESSEL_GROUP_VESSELS, selectNewVesselGroupSearchVessels, selectVesselGroupSearchVessels, } from 'features/vessel-groups/vessel-groups.slice' -import { selectWorkspaceDataviewInstances } from 'features/workspace/workspace.selectors' +import { + selectLastVisitedWorkspace, + selectWorkspace, + selectWorkspaceDataviewInstances, +} from 'features/workspace/workspace.selectors' import { selectHasUserGroupsPermissions } from 'features/user/selectors/user.permissions.selectors' -import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.selectors' +import { LastWorkspaceVisited } from 'features/workspace/workspace.slice' +import { WORKSPACE } from 'routes/routes' +import { DEFAULT_WORKSPACE_CATEGORY, DEFAULT_WORKSPACE_ID } from 'data/workspaces' export const selectAllVesselGroupSearchVessels = createSelector( [selectVesselGroupSearchVessels, selectNewVesselGroupSearchVessels], @@ -47,6 +53,23 @@ export const selectWorkspaceVessselGroupsIds = createSelector( } ) +export const selectVesselGroupWorkspaceToNavigate = createSelector( + [selectLastVisitedWorkspace, selectWorkspace, selectLocationQuery], + (lastVisitedWorkspace, workspace, query): LastWorkspaceVisited => { + if (lastVisitedWorkspace) { + return lastVisitedWorkspace + } + return { + type: WORKSPACE, + payload: { + category: workspace?.category || DEFAULT_WORKSPACE_CATEGORY, + workspaceId: workspace?.id || DEFAULT_WORKSPACE_ID, + }, + query: query, + } + } +) + export const selectIsVessselGroupsFiltering = createSelector( [selectWorkspaceVessselGroupsIds], (workspaceVesselGroupIds = []) => { diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts index b2466fa325..3c4023e107 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.slice.ts @@ -34,7 +34,7 @@ export const MAX_VESSEL_GROUP_VESSELS = 1000 export const MAX_VESSEL_GROUP_SEARCH_VESSELS = 400 export type IdField = 'vesselId' | 'mmsi' -export type VesselGroupConfirmationMode = 'save' | 'saveAndNavigate' +export type VesselGroupConfirmationMode = 'save' | 'saveAndSeeInWorkspace' interface VesselGroupsState extends AsyncReducer { isModalOpen: boolean diff --git a/apps/fishing-map/features/workspace/workspace.slice.ts b/apps/fishing-map/features/workspace/workspace.slice.ts index 1e84606230..92ac83e729 100644 --- a/apps/fishing-map/features/workspace/workspace.slice.ts +++ b/apps/fishing-map/features/workspace/workspace.slice.ts @@ -59,7 +59,12 @@ import { } from './workspace.selectors' import { parseUpsertWorkspace } from './workspace.utils' -type LastWorkspaceVisited = { type: ROUTE_TYPES; payload: any; query: any; replaceQuery?: boolean } +export type LastWorkspaceVisited = { + type: ROUTE_TYPES + payload: any + query: any + replaceQuery?: boolean +} interface WorkspaceSliceState { status: AsyncReducerStatus diff --git a/apps/fishing-map/public/locales/source/translations.json b/apps/fishing-map/public/locales/source/translations.json index c0a1282ff7..fd17772a55 100644 --- a/apps/fishing-map/public/locales/source/translations.json +++ b/apps/fishing-map/public/locales/source/translations.json @@ -1015,6 +1015,7 @@ "remove": "Remove vessel group", "removeVessel": "Remove vessel from group", "saveAndFilter": "Save and filter workspace", + "saveAndSeeInWorkspace": "Save and see in workspace", "saveForLater": "Save for later", "saveForLaterTooltip": "You'll find the group in the activity layers filters or your user panel button", "searchLimit": "Search is limited up to {{limit}} vessels", From 6ad99b4d4a77d2c44bf9fd6c45a9d73ef8e72fa8 Mon Sep 17 00:00:00 2001 From: satellitestudiodesign Date: Fri, 30 Aug 2024 12:11:14 +0200 Subject: [PATCH 12/23] also show "add to group" when a single vessel is visible --- .../workspace/vessels/VesselsSection.tsx | 64 ++++++++++--------- .../public/locales/source/translations.json | 1 + 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx index 930215fe4b..25ac6659d7 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx @@ -15,7 +15,10 @@ import { WORKSPACE_SEARCH } from 'routes/routes' import { DEFAULT_WORKSPACE_CATEGORY, DEFAULT_WORKSPACE_ID } from 'data/workspaces' import { selectWorkspace } from 'features/workspace/workspace.selectors' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' -import { selectVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { + selectActiveVesselsDataviews, + selectVesselsDataviews, +} from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsGuestUser } from 'features/user/selectors/user.selectors' import { hasTracksWithNoData, @@ -47,6 +50,7 @@ function VesselsSection(): React.ReactElement { const { t } = useTranslation() const { dispatchLocation } = useLocationConnect() const dataviews = useSelector(selectVesselsDataviews) + const activeDataviews = useSelector(selectActiveVesselsDataviews) const workspace = useSelector(selectWorkspace) const guestUser = useSelector(selectIsGuestUser) const vesselGroupsInWorkspace = useSelector(selectWorkspaceVessselGroupsIds) @@ -55,7 +59,7 @@ function VesselsSection(): React.ReactElement { const hasVesselsWithNoTrack = hasTracksWithNoData(vesselTracksData) const hasVisibleDataviews = dataviews?.some((dataview) => dataview.config?.visible === true) const searchAllowed = useSelector(isBasicSearchAllowed) - const someVesselsVisible = dataviews.some((d) => d.config?.visible) + const someVesselsVisible = activeDataviews.length > 0 const readOnly = useSelector(selectReadOnly) const resources = useSelector(selectResources) const { dispatchQueryParams } = useLocationConnect() @@ -127,10 +131,7 @@ function VesselsSection(): React.ReactElement { }) }, [dispatchLocation, workspace]) - const vesselResources = dataviews.flatMap((dataview) => { - if (!dataview.config?.visible) { - return [] - } + const vesselResources = activeDataviews.flatMap((dataview) => { const { url: infoUrl } = resolveDataviewDatasetResource(dataview, DatasetTypes.Vessels) return resources[infoUrl] || [] }) @@ -158,34 +159,37 @@ function VesselsSection(): React.ReactElement { {!readOnly && (
    - {dataviews.length > 1 && ( - - - - + {activeDataviews.length > 0 && ( + - + + )} + {dataviews.length > 1 && ( + )} {dataviews.length > 0 && ( Date: Fri, 30 Aug 2024 14:51:45 +0200 Subject: [PATCH 13/23] fix upper case in vesselGroups list --- .../area-report/vessels/ReportVesselsTableFooter.module.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.module.css b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.module.css index 4615503fc1..7b925cae7c 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.module.css +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.module.css @@ -4,10 +4,12 @@ align-items: center; justify-content: space-between; margin-top: var(--space-S); - color: var(--color-secondary-blue); + gap: 1rem; +} + +.footer button { text-transform: uppercase; font: var(--font-S); - gap: 1rem; } .flex { From a2aa4d15be3b103e38ce955a7a98d1cdbdb079a2 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 15:46:53 +0200 Subject: [PATCH 14/23] refactor to cleanup types and follow are-report state config --- apps/fishing-map/data/config.ts | 6 - apps/fishing-map/features/app/App.tsx | 11 +- .../app/selectors/app.reports.selector.ts | 26 ++-- .../app/selectors/app.workspace.selectors.ts | 14 +- .../features/area-report/Report.tsx | 3 +- .../area-report/activity/ReportActivity.tsx | 4 +- .../activity/ReportActivityBeforeAfter.tsx | 2 +- .../ReportActivityBeforeAfterGraph.tsx | 4 +- .../activity/ReportActivityGraphSelector.tsx | 8 +- .../ReportActivityPeriodComparison.tsx | 2 +- .../ReportActivityPeriodComparisonGraph.tsx | 2 +- .../reports-timecomparison.hooks.ts | 8 +- .../area-report/reports-timeseries.hooks.ts | 5 +- .../area-report/reports.config.selectors.ts | 25 +++ .../features/area-report/reports.config.ts | 20 +++ .../features/area-report/reports.selectors.ts | 14 +- .../features/area-report/reports.types.ts | 50 ++++++ .../features/area-report/reports.utils.ts | 3 +- .../area-report/summary/ReportSummary.tsx | 4 +- .../area-report/vessels/ReportVessels.tsx | 4 +- .../vessels/ReportVesselsGraph.tsx | 2 +- .../vessels/ReportVesselsGraphSelector.tsx | 2 +- .../vessels/ReportVesselsTable.tsx | 2 +- .../vessels/ReportVesselsTableFooter.tsx | 18 +-- .../selectors/dataviews.selectors.ts | 10 +- apps/fishing-map/features/search/Search.tsx | 2 +- .../search/advanced/SearchAdvancedFilters.tsx | 2 +- .../search/advanced/SearchAdvancedResults.tsx | 3 +- .../search/advanced/advanced-search.utils.ts | 2 +- .../search/search.config.selectors.ts | 2 +- .../features/search/search.config.ts | 2 +- .../features/search/search.hook.ts | 2 +- .../features/search/search.slice.ts | 2 +- .../features/search/search.types.ts | 26 ++++ .../vessel-group-report/VesselGroupReport.tsx | 2 +- .../vessel-group-report.config.ts | 2 +- .../vessel-group.config.selectors.ts | 5 +- .../vessels/VesselGroupReportVesselsGraph.tsx | 2 +- .../VesselGroupReportVesselsGraphSelector.tsx | 2 +- .../vessels/VesselGroupReportVesselsTable.tsx | 8 +- .../vessel-groups/vessel-groups.types.ts | 21 +++ apps/fishing-map/features/vessel/Vessel.tsx | 2 +- .../vessel/activity/VesselActivity.tsx | 2 +- .../features/vessel/areas/VesselAreas.tsx | 2 +- .../vessel/related-vessels/RelatedVessels.tsx | 2 +- .../vessel/vessel.config.selectors.ts | 2 +- .../features/vessel/vessel.config.ts | 2 +- .../features/vessel/vessel.types.ts | 19 +++ .../features/workspace/workspace.slice.ts | 4 +- apps/fishing-map/routes/routes.selectors.ts | 1 + apps/fishing-map/types/index.ts | 143 ++---------------- 51 files changed, 277 insertions(+), 236 deletions(-) create mode 100644 apps/fishing-map/features/area-report/reports.config.selectors.ts create mode 100644 apps/fishing-map/features/area-report/reports.types.ts create mode 100644 apps/fishing-map/features/search/search.types.ts create mode 100644 apps/fishing-map/features/vessel-groups/vessel-groups.types.ts create mode 100644 apps/fishing-map/features/vessel/vessel.types.ts diff --git a/apps/fishing-map/data/config.ts b/apps/fishing-map/data/config.ts index a0f7a864cf..0e947e1e6a 100644 --- a/apps/fishing-map/data/config.ts +++ b/apps/fishing-map/data/config.ts @@ -108,12 +108,6 @@ export const DEFAULT_WORKSPACE: WorkspaceState & AppState = { visibleEvents: 'all', timebarGraph: TimebarGraphs.None, bivariateDataviews: undefined, - reportActivityGraph: REPORT_ACTIVITY_GRAPH_EVOLUTION, - reportCategory: undefined, - reportVesselFilter: '', - reportVesselGraph: REPORT_VESSELS_GRAPH_FLAG, - reportVesselPage: 0, - reportResultsPerPage: REPORT_VESSELS_PER_PAGE, userTab: UserTab.Info, } diff --git a/apps/fishing-map/features/app/App.tsx b/apps/fishing-map/features/app/App.tsx index 86810d5b59..70ed49b45c 100644 --- a/apps/fishing-map/features/app/App.tsx +++ b/apps/fishing-map/features/app/App.tsx @@ -17,6 +17,7 @@ import { selectLocationType, selectWorkspaceId, selectIsMapDrawing, + selectIsVesselGroupReportLocation, } from 'routes/routes.selectors' import menuBgImage from 'assets/images/menubg.jpg' import { useLocationConnect, useReplaceLoginUrl } from 'routes/routes.hook' @@ -54,11 +55,11 @@ import AppModals from 'features/modals/Modals' import { useMapFitBounds } from 'features/map/map-bounds.hooks' import { useSetMapCoordinates } from 'features/map/map-viewport.hooks' import { useDatasetDrag } from 'features/app/drag-dataset.hooks' -import { selectReportAreaBounds } from 'features/app/selectors/app.reports.selector' import { selectIsUserLogged } from 'features/user/selectors/user.selectors' import ErrorBoundary from 'features/app/ErrorBoundary' import { selectDebugOptions } from 'features/debug/debug.slice' import { useFitWorkspaceBounds } from 'features/workspace/workspace.hook' +import { selectReportAreaBounds } from 'features/area-report/reports.config.selectors' import { useAppDispatch } from './app.hooks' import { selectReadOnly, selectSidebarOpen } from './selectors/app.selectors' import { useAnalytics } from './analytics.hooks' @@ -76,6 +77,7 @@ declare global { const Main = () => { const isWorkspaceLocation = useSelector(selectIsWorkspaceLocation) + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) const locationType = useSelector(selectLocationType) const reportLocation = useSelector(selectIsAnyReportLocation) const workspaceStatus = useSelector(selectWorkspaceStatus) @@ -87,6 +89,7 @@ const Main = () => { const isWorkspacesRouteWithTimebar = isWorkspaceLocation || locationType === WORKSPACE_VESSEL || + isVesselGroupReportLocation || (reportLocation && !isTimeComparisonReport) const isWorkspaceMapReady = useSelector(selectIsWorkspaceMapReady) const showTimebar = @@ -129,7 +132,7 @@ function App() { const i18n = useTranslation() const { dispatchQueryParams } = useLocationConnect() const [menuOpen, setMenuOpen] = useState(false) - const workspaceLocation = useSelector(selectIsWorkspaceLocation) + const isWorkspaceLocation = useSelector(selectIsWorkspaceLocation) const vesselLocation = useSelector(selectIsVesselLocation) const isReportLocation = useSelector(selectIsAnyReportLocation) const reportAreaBounds = useSelector(selectReportAreaBounds) @@ -241,7 +244,7 @@ function App() { asideWidth = isReportLocation ? '45%' : '34rem' } else if (isAnySearchLocation) { asideWidth = '100%' - } else if (workspaceLocation) { + } else if (isWorkspaceLocation) { asideWidth = '39rem' } @@ -261,7 +264,7 @@ function App() { } main={
    } diff --git a/apps/fishing-map/features/app/selectors/app.reports.selector.ts b/apps/fishing-map/features/app/selectors/app.reports.selector.ts index 765dc71328..a15c4dfe46 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -6,7 +6,6 @@ import { selectReportActiveCategories, } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportById } from 'features/area-report/reports.slice' -import { selectWorkspaceStateProperty } from 'features/workspace/workspace.selectors' import { selectLocationAreaId, selectLocationDatasetId, @@ -15,8 +14,16 @@ import { selectUrlBufferUnitQuery, selectUrlBufferValueQuery, } from 'routes/routes.selectors' -import { BufferOperation, BufferUnit, ReportCategory, ReportVesselGraph } from 'types' +import { BufferOperation, BufferUnit } from 'types' import { createDeepEqualSelector } from 'utils/selectors' +import { + selectReportBufferOperationSelector, + selectReportBufferUnitSelector, + selectReportBufferValueSelector, + selectReportCategorySelector, + selectReportVesselGraphSelector, +} from 'features/area-report/reports.config.selectors' +import { ReportCategory, ReportVesselGraph } from 'features/area-report/reports.types' export function isActivityReport(reportCategory: ReportCategory) { return reportCategory === ReportCategory.Fishing || reportCategory === ReportCategory.Presence @@ -44,7 +51,6 @@ export const selectReportAreaId = createSelector( } ) -const selectReportCategorySelector = selectWorkspaceStateProperty('reportCategory') export const selectReportCategory = createSelector( [selectReportCategorySelector, selectReportActiveCategories], (reportCategory, activeCategories): ReportCategory => { @@ -54,8 +60,6 @@ export const selectReportCategory = createSelector( } ) -export const selectReportAreaBounds = selectWorkspaceStateProperty('reportAreaBounds') - export const selectActiveReportDataviews = createDeepEqualSelector( [ selectReportCategory, @@ -79,9 +83,6 @@ export const selectActiveReportDataviews = createDeepEqualSelector( } ) -export const selectReportActivityGraph = selectWorkspaceStateProperty('reportActivityGraph') -const selectReportVesselGraphSelector = selectWorkspaceStateProperty('reportVesselGraph') - export const selectReportVesselGraph = createSelector( [selectReportVesselGraphSelector, selectReportCategory], (reportVesselGraph, reportCategory): ReportVesselGraph => { @@ -91,13 +92,6 @@ export const selectReportVesselGraph = createSelector( return reportVesselGraph } ) - -export const selectReportVesselFilter = selectWorkspaceStateProperty('reportVesselFilter') -export const selectReportVesselPage = selectWorkspaceStateProperty('reportVesselPage') -export const selectReportResultsPerPage = selectWorkspaceStateProperty('reportResultsPerPage') -export const selectReportTimeComparison = selectWorkspaceStateProperty('reportTimeComparison') - -const selectReportBufferValueSelector = selectWorkspaceStateProperty('reportBufferValue') export const selectReportBufferValue = createSelector( [selectReportBufferValueSelector, selectUrlBufferValueQuery], (workspaceBufferValue, urlBufferValue): number => { @@ -105,7 +99,6 @@ export const selectReportBufferValue = createSelector( } ) -const selectReportBufferUnitSelector = selectWorkspaceStateProperty('reportBufferUnit') export const selectReportBufferUnit = createSelector( [selectReportBufferUnitSelector, selectUrlBufferUnitQuery], (workspaceBufferUnit, urlBufferUnit): BufferUnit => { @@ -113,7 +106,6 @@ export const selectReportBufferUnit = createSelector( } ) -const selectReportBufferOperationSelector = selectWorkspaceStateProperty('reportBufferOperation') export const selectReportBufferOperation = createSelector( [selectReportBufferOperationSelector, selectUrlBufferOperationQuery], (workspaceBufferOperation, urlBufferOperation): BufferOperation => { diff --git a/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts b/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts index a927dcceaa..9869dec392 100644 --- a/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts +++ b/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts @@ -12,14 +12,8 @@ import { selectVisibleEvents, } from 'features/app/selectors/app.selectors' import { - selectReportActivityGraph, - selectReportAreaBounds, selectReportCategory, - selectReportResultsPerPage, - selectReportTimeComparison, - selectReportVesselFilter, selectReportVesselGraph, - selectReportVesselPage, selectReportBufferValue, selectReportBufferUnit, selectReportBufferOperation, @@ -36,6 +30,14 @@ import { selectViewport } from 'features/app/selectors/app.viewport.selectors' import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectDaysFromLatest, selectWorkspace } from 'features/workspace/workspace.selectors' import { DEFAULT_WORKSPACE_CATEGORY } from 'data/workspaces' +import { + selectReportActivityGraph, + selectReportAreaBounds, + selectReportResultsPerPage, + selectReportTimeComparison, + selectReportVesselFilter, + selectReportVesselPage, +} from 'features/area-report/reports.config.selectors' const selectWorkspaceReportState = createSelector( [ diff --git a/apps/fishing-map/features/area-report/Report.tsx b/apps/fishing-map/features/area-report/Report.tsx index 6de0b6bf72..8325818eb9 100644 --- a/apps/fishing-map/features/area-report/Report.tsx +++ b/apps/fishing-map/features/area-report/Report.tsx @@ -30,7 +30,7 @@ import { selectReportDataviewsWithPermissions, } from 'features/area-report/reports.selectors' import ReportVesselsPlaceholder from 'features/area-report/placeholders/ReportVesselsPlaceholder' -import { ReportCategory, TimebarVisualisations } from 'types' +import { TimebarVisualisations } from 'types' import { getDownloadReportSupported } from 'features/download/download.utils' import { SUPPORT_EMAIL } from 'data/config' import { @@ -78,6 +78,7 @@ import ReportVessels from './vessels/ReportVessels' import ReportDownload from './download/ReportDownload' import ReportEnvironment from './environment/ReportEnvironment' import styles from './Report.module.css' +import { ReportCategory } from './reports.types' export type ReportActivityUnit = 'hour' | 'detection' diff --git a/apps/fishing-map/features/area-report/activity/ReportActivity.tsx b/apps/fishing-map/features/area-report/activity/ReportActivity.tsx index 697ce27137..0554ce19ee 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivity.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivity.tsx @@ -10,12 +10,12 @@ import { useReportFilteredTimeSeries, } from 'features/area-report/reports-timeseries.hooks' import { selectTimeComparisonValues } from 'features/area-report/reports.selectors' -import { ReportActivityGraph } from 'types' -import { selectReportActivityGraph } from 'features/app/selectors/app.reports.selector' import ReportActivityPlaceholder from 'features/area-report/placeholders/ReportActivityPlaceholder' import ReportActivityPeriodComparison from 'features/area-report/activity/ReportActivityPeriodComparison' import ReportActivityPeriodComparisonGraph from 'features/area-report/activity/ReportActivityPeriodComparisonGraph' import UserGuideLink from 'features/help/UserGuideLink' +import { selectReportActivityGraph } from '../reports.config.selectors' +import { ReportActivityGraph } from '../reports.types' import ReportActivityEvolution from './ReportActivityEvolution' import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' diff --git a/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfter.tsx b/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfter.tsx index 5d0373dca9..3d9f00a1a6 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfter.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfter.tsx @@ -3,13 +3,13 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { InputDate, InputText, Select, SelectOption } from '@globalfishingwatch/ui-components' import { useReportTimeCompareConnect } from 'features/area-report/reports-timecomparison.hooks' -import { selectReportTimeComparison } from 'features/app/selectors/app.reports.selector' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' import { selectReportAreaIds } from 'features/area-report/reports.selectors' import { selectDatasetAreaDetail } from 'features/areas/areas.slice' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { MAX_MONTHS_TO_COMPARE, MAX_DAYS_TO_COMPARE } from 'features/area-report/reports.config' +import { selectReportTimeComparison } from '../reports.config.selectors' import styles from './ReportActivityBeforeAfter.module.css' export default function ReportActivityBeforeAfter() { diff --git a/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfterGraph.tsx b/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfterGraph.tsx index 199333e863..4ec9cea595 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfterGraph.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivityBeforeAfterGraph.tsx @@ -13,14 +13,14 @@ import { import { DateTime } from 'luxon' import { useSelector } from 'react-redux' import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' -import { selectReportTimeComparison } from 'features/app/selectors/app.reports.selector' -import { ReportActivityTimeComparison } from 'types' import i18n from 'features/i18n/i18n' import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' import { getUTCDateTime } from 'utils/dates' import { formatDate, tickFormatter } from 'features/area-report/reports.utils' import { formatI18nNumber } from 'features/i18n/i18nNumber' import { toFixed } from 'utils/shared' +import { selectReportTimeComparison } from '../reports.config.selectors' +import { ReportActivityTimeComparison } from '../reports.types' import styles from './ReportActivityEvolution.module.css' interface ComparisonGraphData { diff --git a/apps/fishing-map/features/area-report/activity/ReportActivityGraphSelector.tsx b/apps/fishing-map/features/area-report/activity/ReportActivityGraphSelector.tsx index 3677e3072e..76d2ba3aaf 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivityGraphSelector.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivityGraphSelector.tsx @@ -8,14 +8,12 @@ import { REPORT_ACTIVITY_GRAPH_BEFORE_AFTER, REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON, } from 'data/config' -import { - selectActiveReportDataviews, - selectReportActivityGraph, -} from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/app/selectors/app.reports.selector' import { useFitAreaInViewport } from 'features/area-report/reports.hooks' -import { ReportActivityGraph } from 'types' import { useSetReportTimeComparison } from 'features/area-report/reports-timecomparison.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' +import { selectReportActivityGraph } from '../reports.config.selectors' +import { ReportActivityGraph } from '../reports.types' type ReportActivityGraphSelectorProps = { loading: boolean diff --git a/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparison.tsx b/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparison.tsx index 63dc745cc8..229a1ba78e 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparison.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparison.tsx @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { InputDate, InputText, Select } from '@globalfishingwatch/ui-components' import { useReportTimeCompareConnect } from 'features/area-report/reports-timecomparison.hooks' -import { selectReportTimeComparison } from 'features/app/selectors/app.reports.selector' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' import { selectReportAreaIds } from 'features/area-report/reports.selectors' @@ -12,6 +11,7 @@ import Hint from 'features/help/Hint' import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { MAX_MONTHS_TO_COMPARE, MAX_DAYS_TO_COMPARE } from 'features/area-report/reports.config' +import { selectReportTimeComparison } from '../reports.config.selectors' import styles from './ReportActivityBeforeAfter.module.css' export default function ReportActivityGraph() { diff --git a/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparisonGraph.tsx b/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparisonGraph.tsx index ef24839898..cae2d3809d 100644 --- a/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparisonGraph.tsx +++ b/apps/fishing-map/features/area-report/activity/ReportActivityPeriodComparisonGraph.tsx @@ -14,12 +14,12 @@ import { Interval as TimeInterval } from 'luxon' import { useSelector } from 'react-redux' import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' import { selectLatestAvailableDataDate } from 'features/app/selectors/app.selectors' -import { selectReportTimeComparison } from 'features/app/selectors/app.reports.selector' import i18n, { t } from 'features/i18n/i18n' import { COLOR_GRADIENT, COLOR_PRIMARY_BLUE } from 'features/app/app.config' import { getUTCDateTime } from 'utils/dates' import { formatDate, formatTooltipValue, tickFormatter } from 'features/area-report/reports.utils' import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info' +import { selectReportTimeComparison } from '../reports.config.selectors' import styles from './ReportActivityEvolution.module.css' const DIFFERENCE = 'difference' diff --git a/apps/fishing-map/features/area-report/reports-timecomparison.hooks.ts b/apps/fishing-map/features/area-report/reports-timecomparison.hooks.ts index e88c4f5d4c..ba72a6532d 100644 --- a/apps/fishing-map/features/area-report/reports-timecomparison.hooks.ts +++ b/apps/fishing-map/features/area-report/reports-timecomparison.hooks.ts @@ -3,18 +3,16 @@ import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { DateTime } from 'luxon' import { SelectOption } from '@globalfishingwatch/ui-components' -import { ReportActivityGraph } from 'types' import { AVAILABLE_START, AVAILABLE_END } from 'data/config' -import { - selectReportActivityGraph, - selectReportTimeComparison, -} from 'features/app/selectors/app.reports.selector' +import {} from 'features/app/selectors/app.reports.selector' import { useTimerangeConnect } from 'features/timebar/timebar.hooks' import { useLocationConnect } from 'routes/routes.hook' import { getUTCDateTime } from 'utils/dates' import { formatI18nDate } from 'features/i18n/i18nDate' import { useFitAreaInViewport } from 'features/area-report/reports.hooks' import { MAX_DAYS_TO_COMPARE, MAX_MONTHS_TO_COMPARE } from 'features/area-report/reports.config' +import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' +import { ReportActivityGraph } from './reports.types' // TODO get this from start and endDate from datasets const MIN_DATE = AVAILABLE_START.slice(0, 10) diff --git a/apps/fishing-map/features/area-report/reports-timeseries.hooks.ts b/apps/fishing-map/features/area-report/reports-timeseries.hooks.ts index 7a1807f200..8e9d43bc29 100644 --- a/apps/fishing-map/features/area-report/reports-timeseries.hooks.ts +++ b/apps/fishing-map/features/area-report/reports-timeseries.hooks.ts @@ -20,9 +20,7 @@ import { } from '@globalfishingwatch/deck-loaders' import { selectActiveReportDataviews, - selectReportActivityGraph, selectReportCategory, - selectReportTimeComparison, } from 'features/app/selectors/app.reports.selector' import { FilteredPolygons } from 'features/area-report/reports-geo.utils' import { @@ -36,11 +34,12 @@ import { selectReportBufferHash, selectShowTimeComparison, } from 'features/area-report/reports.selectors' -import { ReportActivityGraph, ReportCategory } from 'types' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { AreaGeometry } from 'features/areas/areas.slice' import { useFilterCellsByPolygonWorker } from 'features/area-report/reports-geo.utils.workers.hooks' import { TimeRange } from 'features/timebar/timebar.slice' +import { ReportActivityGraph, ReportCategory } from './reports.types' +import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' interface EvolutionGraphData { date: string diff --git a/apps/fishing-map/features/area-report/reports.config.selectors.ts b/apps/fishing-map/features/area-report/reports.config.selectors.ts new file mode 100644 index 0000000000..643d165f02 --- /dev/null +++ b/apps/fishing-map/features/area-report/reports.config.selectors.ts @@ -0,0 +1,25 @@ +import { createSelector } from '@reduxjs/toolkit' +import { selectQueryParam } from 'routes/routes.selectors' +import { AreaReportState, AreaReportStateProperty } from './reports.types' +import { DEFAULT_AREA_REPORT_STATE } from './reports.config' + +type AreaReportProperty

    = Required[P] +function selectAreaReportStateProperty

    (property: P) { + return createSelector([selectQueryParam(property)], (urlProperty): AreaReportProperty

    => { + if (urlProperty !== undefined) return urlProperty + return DEFAULT_AREA_REPORT_STATE[property] as AreaReportProperty

    + }) +} + +export const selectReportCategorySelector = selectAreaReportStateProperty('reportCategory') +export const selectReportAreaBounds = selectAreaReportStateProperty('reportAreaBounds') +export const selectReportActivityGraph = selectAreaReportStateProperty('reportActivityGraph') +export const selectReportVesselGraphSelector = selectAreaReportStateProperty('reportVesselGraph') +export const selectReportVesselFilter = selectAreaReportStateProperty('reportVesselFilter') +export const selectReportVesselPage = selectAreaReportStateProperty('reportVesselPage') +export const selectReportResultsPerPage = selectAreaReportStateProperty('reportResultsPerPage') +export const selectReportTimeComparison = selectAreaReportStateProperty('reportTimeComparison') +export const selectReportBufferValueSelector = selectAreaReportStateProperty('reportBufferValue') +export const selectReportBufferUnitSelector = selectAreaReportStateProperty('reportBufferUnit') +export const selectReportBufferOperationSelector = + selectAreaReportStateProperty('reportBufferOperation') diff --git a/apps/fishing-map/features/area-report/reports.config.ts b/apps/fishing-map/features/area-report/reports.config.ts index f3c39a24b4..8d188c3f81 100644 --- a/apps/fishing-map/features/area-report/reports.config.ts +++ b/apps/fishing-map/features/area-report/reports.config.ts @@ -1,4 +1,10 @@ +import { + REPORT_ACTIVITY_GRAPH_EVOLUTION, + REPORT_VESSELS_GRAPH_FLAG, + REPORT_VESSELS_PER_PAGE, +} from 'data/config' import { BufferUnit, BufferOperation } from 'types' +import { AreaReportState } from './reports.types' export const REPORT_BUFFER_FEATURE_ID: string = 'buffer' export const DEFAULT_BUFFER_VALUE: number = 50 @@ -20,3 +26,17 @@ export const OTHERS_CATEGORY_LABEL = 'OTHERS' export const MAX_DAYS_TO_COMPARE = 100 export const MAX_MONTHS_TO_COMPARE = 12 + +export const DEFAULT_AREA_REPORT_STATE: AreaReportState = { + reportActivityGraph: REPORT_ACTIVITY_GRAPH_EVOLUTION, + reportCategory: undefined, + reportVesselFilter: '', + reportVesselGraph: REPORT_VESSELS_GRAPH_FLAG, + reportVesselPage: 0, + reportResultsPerPage: REPORT_VESSELS_PER_PAGE, + reportAreaBounds: undefined, + reportTimeComparison: undefined, + reportBufferValue: undefined, + reportBufferUnit: undefined, + reportBufferOperation: undefined, +} diff --git a/apps/fishing-map/features/area-report/reports.selectors.ts b/apps/fishing-map/features/area-report/reports.selectors.ts index 09a1199611..495d74d476 100644 --- a/apps/fishing-map/features/area-report/reports.selectors.ts +++ b/apps/fishing-map/features/area-report/reports.selectors.ts @@ -9,16 +9,11 @@ import { selectReportAreaId, selectReportDatasetId, selectActiveReportDataviews, - selectReportActivityGraph, selectReportBufferOperation, selectReportBufferUnit, selectReportBufferValue, selectReportCategory, - selectReportResultsPerPage, - selectReportTimeComparison, - selectReportVesselFilter, selectReportVesselGraph, - selectReportVesselPage, } from 'features/app/selectors/app.reports.selector' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { @@ -33,7 +28,6 @@ import { getReportCategoryFromDataview, getVesselsFiltered, } from 'features/area-report/reports.utils' -import { ReportCategory } from 'types' import { createDeepEqualSelector } from 'utils/selectors' import { EMPTY_FIELD_PLACEHOLDER, getVesselGearTypeLabel } from 'utils/info' import { sortStrings } from 'utils/shared' @@ -45,6 +39,14 @@ import { } from 'features/area-report/reports.config' import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectReportVesselsData, selectReportPreviewBuffer } from './report.slice' +import { + selectReportVesselFilter, + selectReportVesselPage, + selectReportResultsPerPage, + selectReportActivityGraph, + selectReportTimeComparison, +} from './reports.config.selectors' +import { ReportCategory } from './reports.types' const EMPTY_ARRAY: [] = [] diff --git a/apps/fishing-map/features/area-report/reports.types.ts b/apps/fishing-map/features/area-report/reports.types.ts new file mode 100644 index 0000000000..1ad1481fad --- /dev/null +++ b/apps/fishing-map/features/area-report/reports.types.ts @@ -0,0 +1,50 @@ +import { DatasetSubCategory, DataviewCategory } from '@globalfishingwatch/api-types' +import { + REPORT_ACTIVITY_GRAPH_EVOLUTION, + REPORT_ACTIVITY_GRAPH_BEFORE_AFTER, + REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON, + REPORT_VESSELS_GRAPH_FLAG, + REPORT_VESSELS_GRAPH_GEARTYPE, + REPORT_VESSELS_GRAPH_VESSELTYPE, +} from 'data/config' +import { Bbox, BufferOperation, BufferUnit } from 'types' + +export type ReportActivityGraph = + | typeof REPORT_ACTIVITY_GRAPH_EVOLUTION + | typeof REPORT_ACTIVITY_GRAPH_BEFORE_AFTER + | typeof REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON + +export enum ReportCategory { + Fishing = DatasetSubCategory.Fishing, + Presence = DatasetSubCategory.Presence, + Detections = DataviewCategory.Detections, + Environment = DataviewCategory.Environment, +} + +export type ReportVesselGraph = + | typeof REPORT_VESSELS_GRAPH_GEARTYPE + | typeof REPORT_VESSELS_GRAPH_VESSELTYPE + | typeof REPORT_VESSELS_GRAPH_FLAG + +export type ReportActivityTimeComparison = { + start: string + compareStart: string + duration: number + durationType: 'days' | 'months' +} + +export type AreaReportState = { + reportActivityGraph?: ReportActivityGraph + reportAreaBounds?: Bbox + reportCategory?: ReportCategory + reportTimeComparison?: ReportActivityTimeComparison + reportVesselFilter?: string + reportVesselGraph?: ReportVesselGraph + reportVesselPage?: number + reportBufferValue?: number + reportBufferUnit?: BufferUnit + reportBufferOperation?: BufferOperation + reportResultsPerPage?: number +} + +export type AreaReportStateProperty = keyof AreaReportState diff --git a/apps/fishing-map/features/area-report/reports.utils.ts b/apps/fishing-map/features/area-report/reports.utils.ts index 1fdd2e48be..95178d1161 100644 --- a/apps/fishing-map/features/area-report/reports.utils.ts +++ b/apps/fishing-map/features/area-report/reports.utils.ts @@ -18,7 +18,7 @@ import { getSchemaFilterOperationInDataview, SupportedDatasetSchema, } from 'features/datasets/datasets.utils' -import { Bbox, BufferOperation, BufferUnit, ReportCategory } from 'types' +import { Bbox, BufferOperation, BufferUnit } from 'types' import { Area, AreaGeometry } from 'features/areas/areas.slice' import { IdentityVesselData, VesselDataIdentity } from 'features/vessel/vessel.slice' import { VesselGroupReportVesselParsed } from 'features/vessel-group-report/vessels/vessel-group-report-vessels.types' @@ -30,6 +30,7 @@ import { REPORT_BUFFER_FEATURE_ID, } from './reports.config' import { ReportVesselWithDatasets } from './reports.selectors' +import { ReportCategory } from './reports.types' const ALWAYS_SHOWN_FILTERS = ['vessel-groups'] diff --git a/apps/fishing-map/features/area-report/summary/ReportSummary.tsx b/apps/fishing-map/features/area-report/summary/ReportSummary.tsx index 511fbf5451..f8a2544001 100644 --- a/apps/fishing-map/features/area-report/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/area-report/summary/ReportSummary.tsx @@ -9,7 +9,6 @@ import { formatI18nDate } from 'features/i18n/i18nDate' import { selectActiveReportDataviews, selectReportCategory, - selectReportTimeComparison, } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/area-report/summary/ReportSummaryTags' import { FIELDS, getCommonProperties } from 'features/area-report/reports.utils' @@ -26,7 +25,6 @@ import ReportSummaryPlaceholder from 'features/area-report/placeholders/ReportSu import ReportSummaryTagsPlaceholder from 'features/area-report/placeholders/ReportSummaryTagsPlaceholder' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' import { listAsSentence } from 'utils/shared' -import { ReportCategory } from 'types' import { getDateRangeHash, selectReportVesselsDateRangeHash, @@ -34,6 +32,8 @@ import { import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { useTimeCompareTimeDescription } from 'features/area-report/reports-timecomparison.hooks' import { selectReportVesselsHours, selectReportVesselsNumber } from '../reports.selectors' +import { selectReportTimeComparison } from '../reports.config.selectors' +import { ReportCategory } from '../reports.types' import styles from './ReportSummary.module.css' type ReportSummaryProps = { diff --git a/apps/fishing-map/features/area-report/vessels/ReportVessels.tsx b/apps/fishing-map/features/area-report/vessels/ReportVessels.tsx index b1897a1679..11ee903396 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVessels.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVessels.tsx @@ -5,13 +5,13 @@ import ReportVesselsGraphSelector from 'features/area-report/vessels/ReportVesse import { selectActiveReportDataviews, selectReportCategory, - selectReportVesselFilter, } from 'features/app/selectors/app.reports.selector' -import { ReportCategory } from 'types' import ReportSummaryTags from 'features/area-report/summary/ReportSummaryTags' import { FIELDS, getCommonProperties } from 'features/area-report/reports.utils' import { PROPERTIES_EXCLUDED } from 'features/area-report/summary/ReportSummary' import { ReportActivityUnit } from '../Report' +import { selectReportVesselFilter } from '../reports.config.selectors' +import { ReportCategory } from '../reports.types' import ReportVesselsGraph from './ReportVesselsGraph' import ReportVesselsFilter from './ReportVesselsFilter' import ReportVesselsTable from './ReportVesselsTable' diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsGraph.tsx b/apps/fishing-map/features/area-report/vessels/ReportVesselsGraph.tsx index a7c5685294..8b282ed486 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsGraph.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsGraph.tsx @@ -5,7 +5,6 @@ import { BarChart, Bar, XAxis, Tooltip, ResponsiveContainer, LabelList } from 'r import { useTranslation } from 'react-i18next' import { Tooltip as GFWTooltip } from '@globalfishingwatch/ui-components' import { selectReportVesselGraph } from 'features/app/selectors/app.reports.selector' -import { ReportVesselGraph } from 'types' import I18nNumber, { formatI18nNumber } from 'features/i18n/i18nNumber' import { useLocationConnect } from 'routes/routes.hook' import { ReportVesselsGraphPlaceholder } from 'features/area-report/placeholders/ReportVesselsPlaceholder' @@ -22,6 +21,7 @@ import { selectReportVesselsGraphDataGrouped, selectReportVesselsGraphDataOthers, } from '../reports.selectors' +import { ReportVesselGraph } from '../reports.types' import styles from './ReportVesselsGraph.module.css' const MAX_OTHER_TOOLTIP_ITEMS = 10 diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsGraphSelector.tsx b/apps/fishing-map/features/area-report/vessels/ReportVesselsGraphSelector.tsx index 855957b06a..4826d3a313 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsGraphSelector.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsGraphSelector.tsx @@ -12,8 +12,8 @@ import { selectReportCategory, selectReportVesselGraph, } from 'features/app/selectors/app.reports.selector' -import { ReportCategory, ReportVesselGraph } from 'types' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' +import { ReportCategory, ReportVesselGraph } from '../reports.types' export default function ReportVesselsGraphSelector() { const { dispatchQueryParams } = useLocationConnect() diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsTable.tsx b/apps/fishing-map/features/area-report/vessels/ReportVesselsTable.tsx index 6f058e0601..202654a8d6 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsTable.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsTable.tsx @@ -11,7 +11,6 @@ import { selectActiveReportDataviews, selectReportCategory, } from 'features/app/selectors/app.reports.selector' -import { ReportCategory } from 'types' import { selectUserData } from 'features/user/selectors/user.selectors' import DatasetLabel from 'features/datasets/DatasetLabel' import { EMPTY_API_VALUES } from 'features/area-report/reports.config' @@ -20,6 +19,7 @@ import VesselPin from 'features/vessel/VesselPin' import { GLOBAL_VESSELS_DATASET_ID } from 'data/workspaces' import { selectReportVesselsPaginated } from '../reports.selectors' import { ReportActivityUnit } from '../Report' +import { ReportCategory } from '../reports.types' import styles from './ReportVesselsTable.module.css' type ReportVesselTableProps = { diff --git a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx index ff38745b0a..f6407b075c 100644 --- a/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx +++ b/apps/fishing-map/features/area-report/vessels/ReportVesselsTableFooter.tsx @@ -9,7 +9,6 @@ import I18nNumber from 'features/i18n/i18nNumber' import { useLocationConnect } from 'routes/routes.hook' import VesselGroupAddButton from 'features/vessel-groups/VesselGroupAddButton' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' -import { selectReportVesselFilter } from 'features/app/selectors/app.reports.selector' import { REPORT_SHOW_MORE_VESSELS_PER_PAGE, REPORT_VESSELS_PER_PAGE } from 'data/config' import { useAppDispatch } from 'features/app/app.hooks' import { @@ -23,11 +22,11 @@ import { selectReportVesselsList, selectReportVesselsListWithAllInfo, selectReportVesselsPagination, - getVesselsFiltered, ReportVesselWithDatasets, selectReportAreaName, } from '../reports.selectors' -import { parseReportVesselsToIdentity } from '../reports.utils' +import { getVesselsFiltered, parseReportVesselsToIdentity } from '../reports.utils' +import { selectReportVesselFilter } from '../reports.config.selectors' import styles from './ReportVesselsTableFooter.module.css' type ReportVesselsTableFooterProps = { @@ -53,12 +52,13 @@ export default function ReportVesselsTableFooter({ reportName }: ReportVesselsTa const onDownloadVesselsClick = () => { if (allVesselsWithAllInfo?.length) { - const vessels = getVesselsFiltered(allVesselsWithAllInfo, reportVesselFilter)?.map( - (vessel) => { - const { dataviewId, category, sourceColor, flagTranslatedClean, ...rest } = vessel - return rest - } - ) as ReportVesselWithDatasets[] + const vessels = getVesselsFiltered( + allVesselsWithAllInfo, + reportVesselFilter + )?.map((vessel) => { + const { dataviewId, category, sourceColor, flagTranslatedClean, ...rest } = vessel + return rest + }) trackEvent({ category: TrackCategory.Analysis, action: `Click 'Download CSV'`, diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts index cd19b58427..e06adbb259 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts @@ -15,13 +15,10 @@ import { getRelatedDatasetByType, isPrivateDataset, } from 'features/datasets/datasets.utils' -import { - selectWorkspaceDataviewInstances, - selectWorkspaceStateProperty, -} from 'features/workspace/workspace.selectors' +import { selectWorkspaceDataviewInstances } from 'features/workspace/workspace.selectors' import { DEFAULT_BASEMAP_DATAVIEW_INSTANCE, DEFAULT_DATAVIEW_SLUGS } from 'data/workspaces' import { selectAllDataviews } from 'features/dataviews/dataviews.slice' -import { ReportCategory, TimebarVisualisations } from 'types' +import { TimebarVisualisations } from 'types' import { createDeepEqualSelector } from 'utils/selectors' import { selectIsAnyVesselLocation, @@ -43,6 +40,8 @@ import { isBathymetryDataview } from 'features/dataviews/dataviews.utils' import { selectDownloadActiveTabId } from 'features/download/downloadActivity.slice' import { HeatmapDownloadTab } from 'features/download/downloadActivity.config' import { selectViewOnlyVesselGroup } from 'features/vessel-group-report/vessel-group.config.selectors' +import { ReportCategory } from 'features/area-report/reports.types' +import { selectReportCategorySelector } from 'features/area-report/reports.config.selectors' import { selectContextAreasDataviews, selectActivityDataviews, @@ -82,7 +81,6 @@ export const selectReportActiveCategories = createSelector( } ) -const selectReportCategorySelector = selectWorkspaceStateProperty('reportCategory') const selectReportCategory = createSelector( [selectReportCategorySelector, selectReportActiveCategories], (reportCategory, activeCategories): ReportCategory => { diff --git a/apps/fishing-map/features/search/Search.tsx b/apps/fishing-map/features/search/Search.tsx index bf6511cc18..fda64ccf87 100644 --- a/apps/fishing-map/features/search/Search.tsx +++ b/apps/fishing-map/features/search/Search.tsx @@ -41,7 +41,7 @@ import { isBasicSearchAllowed, isAdvancedSearchAllowed, } from 'features/search/search.selectors' -import { VesselSearchState } from 'types' +import { VesselSearchState } from 'features/search/search.types' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import styles from './Search.module.css' diff --git a/apps/fishing-map/features/search/advanced/SearchAdvancedFilters.tsx b/apps/fishing-map/features/search/advanced/SearchAdvancedFilters.tsx index 7e3806f3d6..a58c73483e 100644 --- a/apps/fishing-map/features/search/advanced/SearchAdvancedFilters.tsx +++ b/apps/fishing-map/features/search/advanced/SearchAdvancedFilters.tsx @@ -24,7 +24,7 @@ import { DEFAULT_VESSEL_IDENTITY_ID, } from 'features/vessel/vessel.config' import { useSearchFiltersConnect, useSearchFiltersErrors } from 'features/search/search.hook' -import { VesselSearchState } from 'types' +import { VesselSearchState } from 'features/search/search.types' import { ADVANCED_SEARCH_FIELDS, getSearchDataview, diff --git a/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx b/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx index 248384835f..47d9bffcd8 100644 --- a/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx +++ b/apps/fishing-map/features/search/advanced/SearchAdvancedResults.tsx @@ -25,7 +25,8 @@ import { AsyncReducerStatus } from 'utils/async-slice' import { SearchComponentProps } from 'features/search/basic/SearchBasic' import { useAppDispatch } from 'features/app/app.hooks' import { FIRST_YEAR_OF_DATA } from 'data/config' -import { Locale, VesselSearchState } from 'types' +import { Locale } from 'types' +import { VesselSearchState } from 'features/search/search.types' import I18nDate from 'features/i18n/i18nDate' import { VesselIdentityProperty, diff --git a/apps/fishing-map/features/search/advanced/advanced-search.utils.ts b/apps/fishing-map/features/search/advanced/advanced-search.utils.ts index 9fdf5982ba..130cea3b98 100644 --- a/apps/fishing-map/features/search/advanced/advanced-search.utils.ts +++ b/apps/fishing-map/features/search/advanced/advanced-search.utils.ts @@ -1,6 +1,6 @@ import { Dataset } from '@globalfishingwatch/api-types' import { SchemaFieldDataview, isFieldInFieldsAllowed } from 'features/datasets/datasets.utils' -import { VesselSearchState } from 'types' +import { VesselSearchState } from 'features/search/search.types' export const ADVANCED_SEARCH_FIELDS = ['ssvid', 'imo', 'callsign', 'owner'] as const diff --git a/apps/fishing-map/features/search/search.config.selectors.ts b/apps/fishing-map/features/search/search.config.selectors.ts index 10f57a7517..a451c32c9b 100644 --- a/apps/fishing-map/features/search/search.config.selectors.ts +++ b/apps/fishing-map/features/search/search.config.selectors.ts @@ -1,7 +1,7 @@ import { createSelector } from '@reduxjs/toolkit' import { selectLocationQuery } from 'routes/routes.selectors' import { DEFAULT_SEARCH_STATE } from 'features/search/search.config' -import { VesselSearchState, VesselSearchStateProperty } from 'types' +import { VesselSearchState, VesselSearchStateProperty } from 'features/search/search.types' type VesselSearchProperty

    = Required[P] function selectVesselSearchStateProperty

    (property: P) { diff --git a/apps/fishing-map/features/search/search.config.ts b/apps/fishing-map/features/search/search.config.ts index 1129054a5e..54fb73e31a 100644 --- a/apps/fishing-map/features/search/search.config.ts +++ b/apps/fishing-map/features/search/search.config.ts @@ -1,4 +1,4 @@ -import { VesselSearchState } from 'types' +import { VesselSearchState } from 'features/search/search.types' export const MIN_SEARCH_CHARACTERS = 3 diff --git a/apps/fishing-map/features/search/search.hook.ts b/apps/fishing-map/features/search/search.hook.ts index 0dee94cecb..0cd34e23d0 100644 --- a/apps/fishing-map/features/search/search.hook.ts +++ b/apps/fishing-map/features/search/search.hook.ts @@ -7,7 +7,7 @@ import { selectSearchOption, selectSearchQuery, } from 'features/search/search.config.selectors' -import { VesselSearchState } from 'types' +import { VesselSearchState } from 'features/search/search.types' import { useAppDispatch } from 'features/app/app.hooks' import { MIN_SEARCH_CHARACTERS, RESULTS_PER_PAGE } from 'features/search/search.config' import { selectIsGFWUser } from 'features/user/selectors/user.selectors' diff --git a/apps/fishing-map/features/search/search.slice.ts b/apps/fishing-map/features/search/search.slice.ts index b10f1b6f29..99234cdaac 100644 --- a/apps/fishing-map/features/search/search.slice.ts +++ b/apps/fishing-map/features/search/search.slice.ts @@ -19,9 +19,9 @@ import { import { AsyncError, AsyncReducerStatus } from 'utils/async-slice' import { selectDatasetById } from 'features/datasets/datasets.slice' import { getRelatedDatasetByType, isFieldInFieldsAllowed } from 'features/datasets/datasets.utils' -import { VesselSearchState } from 'types' import { IdentityVesselData, VesselDataIdentity } from 'features/vessel/vessel.slice' import { getVesselId, getVesselIdentities } from 'features/vessel/vessel.utils' +import { VesselSearchState } from 'features/search/search.types' export type VesselLastIdentity = Omit & { dataset: Dataset | string diff --git a/apps/fishing-map/features/search/search.types.ts b/apps/fishing-map/features/search/search.types.ts new file mode 100644 index 0000000000..70197b4389 --- /dev/null +++ b/apps/fishing-map/features/search/search.types.ts @@ -0,0 +1,26 @@ +import { GearType, VesselIdentitySourceEnum, VesselType } from '@globalfishingwatch/api-types' +import { SearchType } from './search.config' + +export type VesselSearchState = { + query?: string + shipname?: string + sources?: string[] + searchOption?: SearchType + infoSource?: VesselIdentitySourceEnum + ssvid?: string + imo?: string + callsign?: string + codMarinha?: string + nationalId?: string + flag?: string[] + geartypes?: GearType[] + shiptypes?: VesselType[] + targetSpecies?: string + transmissionDateFrom?: string + transmissionDateTo?: string + owner?: string + fleet?: string[] + origin?: string +} + +export type VesselSearchStateProperty = keyof VesselSearchState diff --git a/apps/fishing-map/features/vessel-group-report/VesselGroupReport.tsx b/apps/fishing-map/features/vessel-group-report/VesselGroupReport.tsx index d61566aa9e..234786c370 100644 --- a/apps/fishing-map/features/vessel-group-report/VesselGroupReport.tsx +++ b/apps/fishing-map/features/vessel-group-report/VesselGroupReport.tsx @@ -4,9 +4,9 @@ import { useCallback, useEffect, useMemo } from 'react' import { Tab, Tabs } from '@globalfishingwatch/ui-components' import { selectReportVesselGroupId } from 'routes/routes.selectors' import { AsyncReducerStatus } from 'utils/async-slice' -import { VesselGroupReportSection } from 'types' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { useLocationConnect } from 'routes/routes.hook' +import { VesselGroupReportSection } from 'features/vessel-groups/vessel-groups.types' import { useFetchVesselGroupReport } from './vessel-group-report.hooks' import { selectVesselGroupReportData, diff --git a/apps/fishing-map/features/vessel-group-report/vessel-group-report.config.ts b/apps/fishing-map/features/vessel-group-report/vessel-group-report.config.ts index ca16d5d123..2e2fadf24b 100644 --- a/apps/fishing-map/features/vessel-group-report/vessel-group-report.config.ts +++ b/apps/fishing-map/features/vessel-group-report/vessel-group-report.config.ts @@ -1,5 +1,5 @@ import { REPORT_VESSELS_PER_PAGE } from 'data/config' -import { VesselGroupReportState } from 'types' +import { VesselGroupReportState } from 'features/vessel-groups/vessel-groups.types' export const OTHER_CATEGORY_LABEL = 'OTHER' diff --git a/apps/fishing-map/features/vessel-group-report/vessel-group.config.selectors.ts b/apps/fishing-map/features/vessel-group-report/vessel-group.config.selectors.ts index 0fdbdc6f2c..3164a0e0e6 100644 --- a/apps/fishing-map/features/vessel-group-report/vessel-group.config.selectors.ts +++ b/apps/fishing-map/features/vessel-group-report/vessel-group.config.selectors.ts @@ -1,6 +1,9 @@ import { createSelector } from '@reduxjs/toolkit' -import { VesselGroupReportState, VesselGroupReportStateProperty } from 'types' import { selectQueryParam } from 'routes/routes.selectors' +import { + VesselGroupReportState, + VesselGroupReportStateProperty, +} from 'features/vessel-groups/vessel-groups.types' import { DEFAULT_VESSEL_GROUP_REPORT_STATE } from './vessel-group-report.config' type VesselGroupReportProperty

    = diff --git a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraph.tsx b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraph.tsx index 0b24657cd4..eee11c7728 100644 --- a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraph.tsx +++ b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraph.tsx @@ -3,7 +3,6 @@ import cx from 'classnames' import { useSelector } from 'react-redux' import { BarChart, Bar, XAxis, Tooltip, ResponsiveContainer, LabelList } from 'recharts' import { useTranslation } from 'react-i18next' -import { VesselGroupReportVesselsSubsection } from 'types' import I18nNumber, { formatI18nNumber } from 'features/i18n/i18nNumber' import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/area-report/reports.config' import { formatInfoField } from 'utils/info' @@ -12,6 +11,7 @@ import { selectVesselGroupReportVesselsGraphDataGrouped } from 'features/vessel- import { selectReportVesselGroupId } from 'routes/routes.selectors' import { selectActiveDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' import { useLocationConnect } from 'routes/routes.hook' +import { VesselGroupReportVesselsSubsection } from 'features/vessel-groups/vessel-groups.types' import styles from './VesselGroupReportVesselsGraph.module.css' type ReportGraphTooltipProps = { diff --git a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraphSelector.tsx b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraphSelector.tsx index 70101cd4c6..953017c8c1 100644 --- a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraphSelector.tsx +++ b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsGraphSelector.tsx @@ -1,10 +1,10 @@ import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import { Choice, ChoiceOption } from '@globalfishingwatch/ui-components' -import { VesselGroupReportVesselsSubsection } from 'types' import { useLocationConnect } from 'routes/routes.hook' import { selectVesselGroupReportStatus } from 'features/vessel-group-report/vessel-group-report.slice' import { AsyncReducerStatus } from 'utils/async-slice' +import { VesselGroupReportVesselsSubsection } from 'features/vessel-groups/vessel-groups.types' import { selectVesselGroupReportVesselsSubsection } from '../vessel-group.config.selectors' type VesselGroupReportVesselsGraphSelectorProps = {} diff --git a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsTable.tsx b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsTable.tsx index 75fc625b31..e3bf96fe70 100644 --- a/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsTable.tsx +++ b/apps/fishing-map/features/vessel-group-report/vessels/VesselGroupReportVesselsTable.tsx @@ -14,15 +14,15 @@ import VesselLink from 'features/vessel/VesselLink' import VesselPin from 'features/vessel/VesselPin' import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' import { AsyncReducerStatus } from 'utils/async-slice' -import { - VesselGroupReportVesselsOrderDirection, - VesselGroupReportVesselsOrderProperty, -} from 'types' import { selectVesselGroupReportVesselsOrderDirection, selectVesselGroupReportVesselsOrderProperty, } from 'features/vessel-group-report/vessel-group.config.selectors' import { selectVesselGroupReportVessels } from 'features/vessel-group-report/vessel-group-report.slice' +import { + VesselGroupReportVesselsOrderProperty, + VesselGroupReportVesselsOrderDirection, +} from 'features/vessel-groups/vessel-groups.types' import styles from './VesselGroupReportVesselsTable.module.css' import { selectVesselGroupReportVesselsPaginated } from './vessel-group-report-vessels.selectors' import VesselGroupReportVesselsTableFooter from './VesselGroupReportVesselsTableFooter' diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts new file mode 100644 index 0000000000..bc074964b2 --- /dev/null +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts @@ -0,0 +1,21 @@ +export type VesselGroupReportSection = 'vessels' | 'insights' | 'activity' | 'events' +export type VesselGroupReportVesselsSubsection = 'flag' | 'shiptypes' | 'geartypes' | 'source' +export type VesselGroupReportActivitySubsection = 'fishing-effort' | 'presence' +export type VesselGroupReportEventsSubsection = 'fishing' | 'encounters' | 'port' | 'loitering' +export type VesselGroupReportVesselsOrderProperty = 'shipname' | 'flag' | 'shiptype' +export type VesselGroupReportVesselsOrderDirection = 'asc' | 'desc' + +export type VesselGroupReportState = { + viewOnlyVesselGroup: boolean + vesselGroupReportSection: VesselGroupReportSection + vesselGroupReportVesselsSubsection?: VesselGroupReportVesselsSubsection + vesselGroupReportActivitySubsection?: VesselGroupReportActivitySubsection + vesselGroupReportEventsSubsection?: VesselGroupReportEventsSubsection + vesselGroupReportVesselPage?: number + vesselGroupReportResultsPerPage?: number + vesselGroupReportVesselFilter?: string + vesselGroupReportVesselsOrderProperty?: VesselGroupReportVesselsOrderProperty + vesselGroupReportVesselsOrderDirection?: VesselGroupReportVesselsOrderDirection +} + +export type VesselGroupReportStateProperty = keyof VesselGroupReportState diff --git a/apps/fishing-map/features/vessel/Vessel.tsx b/apps/fishing-map/features/vessel/Vessel.tsx index 53fc2ad729..d13d580671 100644 --- a/apps/fishing-map/features/vessel/Vessel.tsx +++ b/apps/fishing-map/features/vessel/Vessel.tsx @@ -29,7 +29,6 @@ import { useClickedEventConnect } from 'features/map/map-interactions.hooks' import VesselAreas from 'features/vessel/areas/VesselAreas' import RelatedVessels from 'features/vessel/related-vessels/RelatedVessels' import { useLocationConnect } from 'routes/routes.hook' -import { VesselSection } from 'types' import { selectVesselHasEventsDatasets } from 'features/vessel/selectors/vessel.resources.selectors' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' import { VESSEL_PROFILE_DATAVIEWS_INSTANCES } from 'data/default-workspaces/context-layers' @@ -50,6 +49,7 @@ import Insights from 'features/vessel/insights/Insights' import VesselActivity from './activity/VesselActivity' import VesselIdentity from './identity/VesselIdentity' import styles from './Vessel.module.css' +import { VesselSection } from './vessel.types' const Vessel = () => { const { t } = useTranslation() diff --git a/apps/fishing-map/features/vessel/activity/VesselActivity.tsx b/apps/fishing-map/features/vessel/activity/VesselActivity.tsx index 48bbdade91..22c0f17b7f 100644 --- a/apps/fishing-map/features/vessel/activity/VesselActivity.tsx +++ b/apps/fishing-map/features/vessel/activity/VesselActivity.tsx @@ -8,12 +8,12 @@ import ActivityByVoyage from 'features/vessel/activity/activity-by-voyage/Activi import { VesselActivitySummary } from 'features/vessel/activity/VesselActivitySummary' import { useLocationConnect } from 'routes/routes.hook' import { selectVesselActivityMode } from 'features/vessel/vessel.config.selectors' -import { VesselProfileActivityMode } from 'types' import { selectVesselHasEventsDatasets } from 'features/vessel/selectors/vessel.resources.selectors' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectVesselProfileDataview } from 'features/dataviews/selectors/dataviews.instances.selectors' import { useVesselProfileEventsError, useVesselProfileEventsLoading } from '../vessel-events.hooks' import { useVesselProfileLayer } from '../vessel-bounds.hooks' +import { VesselProfileActivityMode } from '../vessel.types' import styles from './VesselActivity.module.css' const VesselActivity = () => { diff --git a/apps/fishing-map/features/vessel/areas/VesselAreas.tsx b/apps/fishing-map/features/vessel/areas/VesselAreas.tsx index c93e8db423..4d6e9215a1 100644 --- a/apps/fishing-map/features/vessel/areas/VesselAreas.tsx +++ b/apps/fishing-map/features/vessel/areas/VesselAreas.tsx @@ -11,7 +11,6 @@ import { selectEventsGroupedByArea, UNKNOWN_AREA, } from 'features/vessel/activity/vessels-activity.selectors' -import { VesselAreaSubsection } from 'types' import { selectVesselAreaSubsection } from 'features/vessel/vessel.config.selectors' import { useLocationConnect } from 'routes/routes.hook' import { useRegionNamesByType } from 'features/regions/regions.hooks' @@ -27,6 +26,7 @@ import { selectVesselProfileColor } from 'features/dataviews/selectors/dataviews import { useMapFitBounds } from 'features/map/map-bounds.hooks' import { useDebouncedDispatchHighlightedEvent } from 'features/map/map-interactions.hooks' import { useVesselProfileEventsLoading } from '../vessel-events.hooks' +import { VesselAreaSubsection } from '../vessel.types' import styles from './VesselAreas.module.css' type VesselAreasProps = { diff --git a/apps/fishing-map/features/vessel/related-vessels/RelatedVessels.tsx b/apps/fishing-map/features/vessel/related-vessels/RelatedVessels.tsx index 7e08e5c856..0a7e3a0d26 100644 --- a/apps/fishing-map/features/vessel/related-vessels/RelatedVessels.tsx +++ b/apps/fishing-map/features/vessel/related-vessels/RelatedVessels.tsx @@ -2,7 +2,6 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { useCallback, useMemo } from 'react' import { Choice, ChoiceOption, Spinner } from '@globalfishingwatch/ui-components' -import { VesselRelatedSubsection } from 'types' import { selectVesselRelatedSubsection } from 'features/vessel/vessel.config.selectors' import { useLocationConnect } from 'routes/routes.hook' import RelatedEncounterVessels from 'features/vessel/related-vessels/RelatedEncounterVessels' @@ -10,6 +9,7 @@ import RelatedOwnersVessels from 'features/vessel/related-vessels/RelatedOwnersV import { VesselActivitySummary } from 'features/vessel/activity/VesselActivitySummary' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { useVesselProfileEventsLoading } from '../vessel-events.hooks' +import { VesselRelatedSubsection } from '../vessel.types' import styles from './RelatedVessels.module.css' const RelatedVessels = () => { diff --git a/apps/fishing-map/features/vessel/vessel.config.selectors.ts b/apps/fishing-map/features/vessel/vessel.config.selectors.ts index d6aca4f2ac..41ce8d65ad 100644 --- a/apps/fishing-map/features/vessel/vessel.config.selectors.ts +++ b/apps/fishing-map/features/vessel/vessel.config.selectors.ts @@ -1,8 +1,8 @@ import { createSelector } from '@reduxjs/toolkit' import { VesselIdentitySourceEnum } from '@globalfishingwatch/api-types' -import { VesselProfileState, VesselProfileStateProperty } from 'types' import { selectQueryParam } from 'routes/routes.selectors' import { DEFAULT_VESSEL_STATE } from 'features/vessel/vessel.config' +import { VesselProfileStateProperty, VesselProfileState } from './vessel.types' type VesselProfileProperty

    = Required[P] export function selectVesselProfileStateProperty

    ( diff --git a/apps/fishing-map/features/vessel/vessel.config.ts b/apps/fishing-map/features/vessel/vessel.config.ts index 8871446ec5..45ce2cf80b 100644 --- a/apps/fishing-map/features/vessel/vessel.config.ts +++ b/apps/fishing-map/features/vessel/vessel.config.ts @@ -2,7 +2,7 @@ import { RegionType, SelfReportedSource } from '@globalfishingwatch/api-types' import { VesselIdentitySourceEnum } from '@globalfishingwatch/api-types' import { I18nNamespaces } from 'features/i18n/i18n.types' import { IdentityVesselData } from 'features/vessel/vessel.slice' -import { VesselProfileState } from 'types' +import { VesselProfileState } from './vessel.types' export const DEFAULT_VESSEL_IDENTITY_DATASET = 'public-global-vessel-identity' export const DEFAULT_VESSEL_IDENTITY_VERSION = 'v3.0' diff --git a/apps/fishing-map/features/vessel/vessel.types.ts b/apps/fishing-map/features/vessel/vessel.types.ts new file mode 100644 index 0000000000..317e6ed94f --- /dev/null +++ b/apps/fishing-map/features/vessel/vessel.types.ts @@ -0,0 +1,19 @@ +import { VesselIdentitySourceEnum } from '@globalfishingwatch/api-types' + +export type VesselSection = 'activity' | 'related_vessels' | 'areas' | 'insights' +export type VesselAreaSubsection = 'fao' | 'eez' | 'mpa' | 'rfmo' +export type VesselRelatedSubsection = 'encounters' | 'owners' +export type VesselProfileActivityMode = 'voyage' | 'type' +export type VesselProfileState = { + vesselDatasetId: string + vesselRegistryId?: string + vesselSelfReportedId?: string + vesselSection: VesselSection + vesselArea: VesselAreaSubsection + vesselRelated: VesselRelatedSubsection + vesselIdentitySource: VesselIdentitySourceEnum + vesselActivityMode: VesselProfileActivityMode + viewOnlyVessel: boolean +} + +export type VesselProfileStateProperty = keyof VesselProfileState diff --git a/apps/fishing-map/features/workspace/workspace.slice.ts b/apps/fishing-map/features/workspace/workspace.slice.ts index 92ac83e729..28c7fc9d46 100644 --- a/apps/fishing-map/features/workspace/workspace.slice.ts +++ b/apps/fishing-map/features/workspace/workspace.slice.ts @@ -19,7 +19,7 @@ import { UrlDataviewInstance, } from '@globalfishingwatch/dataviews-client' import { DEFAULT_TIME_RANGE, PRIVATE_SUFIX, VALID_PASSWORD } from 'data/config' -import { QueryParams, WorkspaceState } from 'types' +import { AnyWorkspaceState, QueryParams, WorkspaceState } from 'types' import { fetchDatasetsByIdsThunk } from 'features/datasets/datasets.slice' import { fetchDataviewsByIdsThunk } from 'features/dataviews/dataviews.slice' import { @@ -71,7 +71,7 @@ interface WorkspaceSliceState { // used to identify when someone saves its own version of the workspace customStatus: AsyncReducerStatus error: AsyncError - data: Workspace | null + data: Workspace | null password: string | typeof VALID_PASSWORD lastVisited: LastWorkspaceVisited | undefined } diff --git a/apps/fishing-map/routes/routes.selectors.ts b/apps/fishing-map/routes/routes.selectors.ts index eb9d317239..b78a0cb8d5 100644 --- a/apps/fishing-map/routes/routes.selectors.ts +++ b/apps/fishing-map/routes/routes.selectors.ts @@ -47,6 +47,7 @@ const selectIsReportLocation = createSelector( [selectLocationType], (locationType) => locationType === REPORT ) + const selectIsWorkspaceReportLocation = createSelector( [selectLocationType], (locationType) => locationType === WORKSPACE_REPORT diff --git a/apps/fishing-map/types/index.ts b/apps/fishing-map/types/index.ts index 00fcf31914..01a7b177f9 100644 --- a/apps/fishing-map/types/index.ts +++ b/apps/fishing-map/types/index.ts @@ -1,12 +1,5 @@ import { BaseUrlWorkspace, UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' -import { - DatasetSubCategory, - DataviewCategory, - EventType, - GearType, - VesselIdentitySourceEnum, - VesselType, -} from '@globalfishingwatch/api-types' +import { EventType } from '@globalfishingwatch/api-types' import { DrawFeatureType, FourwingsVisualizationMode, @@ -14,16 +7,14 @@ import { HEATMAP_LOW_RES_ID, RulerData, } from '@globalfishingwatch/deck-layers' -import { - REPORT_VESSELS_GRAPH_GEARTYPE, - REPORT_VESSELS_GRAPH_FLAG, - REPORT_ACTIVITY_GRAPH_EVOLUTION, - REPORT_ACTIVITY_GRAPH_BEFORE_AFTER, - REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON, - REPORT_VESSELS_GRAPH_VESSELTYPE, -} from 'data/config' -import { SearchType } from 'features/search/search.config' import { MapAnnotation } from 'features/map/overlays/annotations/annotations.types' +import { AreaReportState, AreaReportStateProperty } from 'features/area-report/reports.types' +import { VesselProfileState, VesselProfileStateProperty } from 'features/vessel/vessel.types' +import { + VesselGroupReportState, + VesselGroupReportStateProperty, +} from 'features/vessel-groups/vessel-groups.types' +import { VesselSearchState, VesselSearchStateProperty } from 'features/search/search.types' export { Locale } from '@globalfishingwatch/api-types' type WorkspaceViewportParam = 'latitude' | 'longitude' | 'zoom' @@ -31,28 +22,16 @@ type WorkspaceTimeRangeParam = 'start' | 'end' export type BufferUnit = 'nauticalmiles' | 'kilometers' export type BufferOperation = 'dissolve' | 'difference' -type ReportStateProperty = - | 'reportActivityGraph' - | 'reportAreaBounds' - | 'reportCategory' - | 'reportResultsPerPage' - | 'reportTimeComparison' - | 'reportVesselFilter' - | 'reportVesselGraph' - | 'reportVesselPage' - | 'reportBufferValue' - | 'reportBufferUnit' - | 'reportBufferOperation' - export type WorkspaceStateProperty = keyof WorkspaceState type AppStateProperty = keyof AppState -type AnyStateProperty = WorkspaceStateProperty | ReportStateProperty | AppStateProperty +type AnyStateProperty = WorkspaceStateProperty | AppStateProperty export type WorkspaceParam = | WorkspaceViewportParam | WorkspaceTimeRangeParam | AnyStateProperty + | AreaReportStateProperty | VesselProfileStateProperty | VesselGroupReportStateProperty | VesselSearchStateProperty @@ -61,29 +40,6 @@ export type WorkspaceViewport = Record type WorkspaceTimeRange = Record type BivariateDataviews = [string, string] -export type ReportActivityGraph = - | typeof REPORT_ACTIVITY_GRAPH_EVOLUTION - | typeof REPORT_ACTIVITY_GRAPH_BEFORE_AFTER - | typeof REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON - -export type ReportActivityTimeComparison = { - start: string - compareStart: string - duration: number - durationType: 'days' | 'months' -} - -export enum ReportCategory { - Fishing = DatasetSubCategory.Fishing, - Presence = DatasetSubCategory.Presence, - Detections = DataviewCategory.Detections, - Environment = DataviewCategory.Environment, -} - -export type ReportVesselGraph = - | typeof REPORT_VESSELS_GRAPH_GEARTYPE - | typeof REPORT_VESSELS_GRAPH_VESSELTYPE - | typeof REPORT_VESSELS_GRAPH_FLAG export interface WorkspaceState extends BaseUrlWorkspace { activityVisualizationMode?: FourwingsVisualizationMode @@ -97,17 +53,6 @@ export interface WorkspaceState extends BaseUrlWorkspace { mapRulersVisible?: boolean daysFromLatest?: number // use latest day as endAt minus the number of days set here readOnly?: boolean - reportActivityGraph?: ReportActivityGraph - reportAreaBounds?: Bbox - reportCategory?: ReportCategory - reportTimeComparison?: ReportActivityTimeComparison - reportVesselFilter?: string - reportVesselGraph?: ReportVesselGraph - reportVesselPage?: number - reportBufferValue?: number - reportBufferUnit?: BufferUnit - reportBufferOperation?: BufferOperation - reportResultsPerPage?: number sidebarOpen?: boolean timebarGraph?: TimebarGraphs timebarSelectedEnvId?: string @@ -115,68 +60,9 @@ export interface WorkspaceState extends BaseUrlWorkspace { visibleEvents?: VisibleEvents } -export type VesselSearchState = { - query?: string - shipname?: string - sources?: string[] - searchOption?: SearchType - infoSource?: VesselIdentitySourceEnum - ssvid?: string - imo?: string - callsign?: string - codMarinha?: string - nationalId?: string - flag?: string[] - geartypes?: GearType[] - shiptypes?: VesselType[] - targetSpecies?: string - transmissionDateFrom?: string - transmissionDateTo?: string - owner?: string - fleet?: string[] - origin?: string -} - -export type VesselSearchStateProperty = keyof VesselSearchState -export type VesselSection = 'activity' | 'related_vessels' | 'areas' | 'insights' -export type VesselAreaSubsection = 'fao' | 'eez' | 'mpa' | 'rfmo' -export type VesselRelatedSubsection = 'encounters' | 'owners' -export type VesselProfileActivityMode = 'voyage' | 'type' -export type VesselProfileState = { - vesselDatasetId: string - vesselRegistryId?: string - vesselSelfReportedId?: string - vesselSection: VesselSection - vesselArea: VesselAreaSubsection - vesselRelated: VesselRelatedSubsection - vesselIdentitySource: VesselIdentitySourceEnum - vesselActivityMode: VesselProfileActivityMode - viewOnlyVessel: boolean -} - -export type VesselProfileStateProperty = keyof VesselProfileState - -export type VesselGroupReportSection = 'vessels' | 'insights' | 'activity' | 'events' -export type VesselGroupReportVesselsSubsection = 'flag' | 'shiptypes' | 'geartypes' | 'source' -export type VesselGroupReportActivitySubsection = 'fishing-effort' | 'presence' -export type VesselGroupReportEventsSubsection = 'fishing' | 'encounters' | 'port' | 'loitering' -export type VesselGroupReportVesselsOrderProperty = 'shipname' | 'flag' | 'shiptype' -export type VesselGroupReportVesselsOrderDirection = 'asc' | 'desc' - -export type VesselGroupReportState = { - viewOnlyVesselGroup: boolean - vesselGroupReportSection: VesselGroupReportSection - vesselGroupReportVesselsSubsection?: VesselGroupReportVesselsSubsection - vesselGroupReportActivitySubsection?: VesselGroupReportActivitySubsection - vesselGroupReportEventsSubsection?: VesselGroupReportEventsSubsection - vesselGroupReportVesselPage?: number - vesselGroupReportResultsPerPage?: number - vesselGroupReportVesselFilter?: string - vesselGroupReportVesselsOrderProperty?: VesselGroupReportVesselsOrderProperty - vesselGroupReportVesselsOrderDirection?: VesselGroupReportVesselsOrderDirection -} - -export type VesselGroupReportStateProperty = keyof VesselGroupReportState +export type AnyWorkspaceState = Partial< + WorkspaceState & AreaReportState & VesselProfileState & VesselGroupReportState +> type RedirectParam = { 'access-token'?: string @@ -197,9 +83,10 @@ export type AppState = { } export type QueryParams = Partial & - Partial & WorkspaceState & + Partial & Partial & + Partial & Partial & AppState & RedirectParam & From 7f4bb76f57571240b6b8356d17b28f81ba449a38 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 15:53:03 +0200 Subject: [PATCH 15/23] clean all report params at once --- .../features/sidebar/SidebarHeader.tsx | 15 ++++++++----- .../features/workspace/workspace.slice.ts | 21 +++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/apps/fishing-map/features/sidebar/SidebarHeader.tsx b/apps/fishing-map/features/sidebar/SidebarHeader.tsx index f1e75249d1..4ac37c3172 100644 --- a/apps/fishing-map/features/sidebar/SidebarHeader.tsx +++ b/apps/fishing-map/features/sidebar/SidebarHeader.tsx @@ -40,6 +40,7 @@ import { selectLocationType, selectIsWorkspaceVesselLocation, selectIsAnyVesselLocation, + selectIsVesselGroupReportLocation, } from 'routes/routes.selectors' import { DEFAULT_WORKSPACE_ID, WorkspaceCategory } from 'data/workspaces' import { selectReadOnly } from 'features/app/selectors/app.selectors' @@ -358,7 +359,7 @@ function CloseReportButton() { dispatch(cleanCurrentWorkspaceReportState()) } - const isWorkspaceRoute = workspaceId !== DEFAULT_WORKSPACE_ID + const isWorkspaceRoute = workspaceId !== undefined && workspaceId !== DEFAULT_WORKSPACE_ID const linkTo = { type: isWorkspaceRoute ? WORKSPACE : HOME, payload: { @@ -449,6 +450,7 @@ function SidebarHeader() { const isSearchLocation = useSelector(selectIsAnySearchLocation) const isReportLocation = useSelector(selectIsAnyReportLocation) const isVesselLocation = useSelector(selectIsWorkspaceVesselLocation) + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) const isAnyVesselLocation = useSelector(selectIsAnyVesselLocation) const isSmallScreen = useSmallScreen(SMALL_PHONE_BREAKPOINT) const activeSearchOption = useSelector(selectSearchOption) @@ -504,6 +506,8 @@ function SidebarHeader() { dispatchQueryParams({ searchOption: option.id, ...EMPTY_FILTERS, ...additionalParams }) } + const showCloseReportButton = isReportLocation || isVesselGroupReportLocation + return ( } {isSmallScreen && } - {isReportLocation && } + {showCloseReportButton && } {isVesselLocation && } {isSearchLocation && !readOnly && !isSmallScreen && ( )} - {!isReportLocation && !isVesselLocation && showBackToWorkspaceButton && ( - - )} + {!isReportLocation && + !isVesselLocation && + !showCloseReportButton && + showBackToWorkspaceButton && } )}

    diff --git a/apps/fishing-map/features/workspace/workspace.slice.ts b/apps/fishing-map/features/workspace/workspace.slice.ts index 28c7fc9d46..6c54805480 100644 --- a/apps/fishing-map/features/workspace/workspace.slice.ts +++ b/apps/fishing-map/features/workspace/workspace.slice.ts @@ -52,6 +52,8 @@ import { AppDispatch } from 'store' import { LIBRARY_LAYERS } from 'data/layer-library' import { selectPrivateUserGroups } from 'features/user/selectors/user.groups.selectors' import { PRIVATE_SEARCH_DATASET_BY_GROUP } from 'features/user/user.config' +import { DEFAULT_AREA_REPORT_STATE } from 'features/area-report/reports.config' +import { DEFAULT_VESSEL_GROUP_REPORT_STATE } from 'features/vessel-group-report/vessel-group-report.config' import { selectCurrentWorkspaceId, selectDaysFromLatest, @@ -420,17 +422,14 @@ export const updatedCurrentWorkspaceThunk = createAsyncThunk< export function cleanReportQuery(query: QueryParams) { return { ...query, - reportActivityGraph: undefined, - reportAreaBounds: undefined, - reportCategory: undefined, - reportResultsPerPage: undefined, - reportTimeComparison: undefined, - reportVesselFilter: undefined, - reportVesselGraph: undefined, - reportVesselPage: undefined, - reportBufferUnit: undefined, - reportBufferValue: undefined, - reportBufferOperation: undefined, + ...Object.keys(DEFAULT_AREA_REPORT_STATE).reduce((acc, key) => { + acc[key] = undefined + return acc + }, {} as Record), + ...Object.keys(DEFAULT_VESSEL_GROUP_REPORT_STATE).reduce((acc, key) => { + acc[key] = undefined + return acc + }, {} as Record), } } From 70580683c790333365499a21570549d9b1839bd0 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 16:03:04 +0200 Subject: [PATCH 16/23] show edit icon in LayerPanel --- .../vessel-groups/VesselGroupsLayerPanel.tsx | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx index 75841515b0..c5e8da8626 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx @@ -11,6 +11,12 @@ import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' import { useLayerPanelDataviewSort } from 'features/workspace/shared/layer-panel-sort.hook' import { formatInfoField } from 'utils/info' import VesselGroupReportLink from 'features/vessel-group-report/VesselGroupReportLink' +import { useAppDispatch } from 'features/app/app.hooks' +import { + setNewVesselGroupSearchVessels, + setVesselGroupEditId, + setVesselGroupsModalOpen, +} from 'features/vessel-groups/vessel-groups.slice' import Color from '../common/Color' import LayerSwitch from '../common/LayerSwitch' import Remove from '../common/Remove' @@ -26,6 +32,7 @@ function VesselGroupLayerPanel({ vesselGroup, }: VesselGroupLayerPanelProps): React.ReactElement { const { t } = useTranslation() + const dispatch = useAppDispatch() const { upsertDataviewInstance } = useDataviewInstancesConnect() const activityLayer = useGetDeckLayer(dataview?.id) @@ -50,6 +57,14 @@ function VesselGroupLayerPanel({ setColorOpen(false) } + const onEditClick = () => { + if (vesselGroup && (vesselGroup?.id || !vesselGroup?.vessels?.length)) { + dispatch(setVesselGroupEditId(vesselGroup.id)) + dispatch(setNewVesselGroupSearchVessels(vesselGroup.vessels)) + dispatch(setVesselGroupsModalOpen(true)) + } + } + const onToggleColorOpen = () => { setColorOpen(!colorOpen) } @@ -96,14 +111,23 @@ function VesselGroupLayerPanel({ > {layerActive && ( - + + + + )} From 999767f750f24d180ae9a3add1d408dffff6d3cb Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 16:06:56 +0200 Subject: [PATCH 17/23] go to report link icon --- .../workspace/vessel-groups/VesselGroupsLayerPanel.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx index c5e8da8626..9c72a06b67 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx @@ -112,6 +112,16 @@ function VesselGroupLayerPanel({ {layerActive && ( + + + Date: Fri, 30 Aug 2024 16:31:52 +0200 Subject: [PATCH 18/23] deleted and loading VesselGroupLayerPanel state --- .../VesselGroupReportLink.tsx | 3 ++ .../vessel-groups/VesselGroupNotFound.tsx | 27 ++++++++++++++ .../vessel-groups/VesselGroupsLayerPanel.tsx | 35 +++++++++++++++++-- .../vessel-groups/VesselGroupsSection.tsx | 27 +++++++++++--- .../public/locales/source/translations.json | 2 ++ 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 apps/fishing-map/features/workspace/vessel-groups/VesselGroupNotFound.tsx diff --git a/apps/fishing-map/features/vessel-group-report/VesselGroupReportLink.tsx b/apps/fishing-map/features/vessel-group-report/VesselGroupReportLink.tsx index ce880eabf2..3568d6a353 100644 --- a/apps/fishing-map/features/vessel-group-report/VesselGroupReportLink.tsx +++ b/apps/fishing-map/features/vessel-group-report/VesselGroupReportLink.tsx @@ -15,6 +15,9 @@ type VesselGroupReportLinkProps = { function VesselGroupReportLink({ children, vesselGroupId }: VesselGroupReportLinkProps) { const workspace = useSelector(selectWorkspace) const query = useSelector(selectLocationQuery) + if (!workspace || !vesselGroupId) { + return children + } return ( +
    + +

    + {dataview.config?.filters?.['vessel-groups']?.[0] || + t('vesselGroup.notFound', 'Vessel group not found')} +

    +
    +
    + +
    +
    +
    + ) +} + +export default VesselGroupNotFound diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx index 9c72a06b67..7295a32310 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx @@ -1,6 +1,7 @@ import { Fragment, useState } from 'react' import cx from 'classnames' import { useTranslation } from 'react-i18next' +import { useSelector } from 'react-redux' import { VesselGroup } from '@globalfishingwatch/api-types' import { IconButton, ColorBarOption, Tooltip } from '@globalfishingwatch/ui-components' import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' @@ -17,26 +18,33 @@ import { setVesselGroupEditId, setVesselGroupsModalOpen, } from 'features/vessel-groups/vessel-groups.slice' +import { selectIsGFWUser } from 'features/user/selectors/user.selectors' +import { selectReadOnly } from 'features/app/selectors/app.selectors' import Color from '../common/Color' import LayerSwitch from '../common/LayerSwitch' import Remove from '../common/Remove' import Title from '../common/Title' +import VesselGroupNotFound from './VesselGroupNotFound' export type VesselGroupLayerPanelProps = { dataview: UrlDataviewInstance vesselGroup?: VesselGroup + vesselGroupLoading?: boolean } function VesselGroupLayerPanel({ dataview, vesselGroup, + vesselGroupLoading, }: VesselGroupLayerPanelProps): React.ReactElement { const { t } = useTranslation() const dispatch = useAppDispatch() + const isGFWUser = useSelector(selectIsGFWUser) + const readOnly = useSelector(selectReadOnly) const { upsertDataviewInstance } = useDataviewInstancesConnect() const activityLayer = useGetDeckLayer(dataview?.id) - const layerLoaded = activityLayer?.loaded + const layerLoaded = activityLayer?.loaded && !vesselGroupLoading const layerError = activityLayer?.instance?.getError?.() const { items, attributes, listeners, setNodeRef, setActivatorNodeRef, style } = @@ -73,6 +81,10 @@ function VesselGroupLayerPanel({ setColorOpen(false) } + if (!vesselGroup) { + return + } + return (
    {formatInfoField(vesselGroup?.name, 'name')}{' '} - ({vesselGroup?.vessels.length}) + {vesselGroup?.vessels?.length && ( + ({vesselGroup?.vessels.length}) + )} @@ -139,7 +153,22 @@ function VesselGroupLayerPanel({ /> )} - + {!readOnly && } + {!readOnly && layerActive && layerError && ( + + )}
    dataview.config?.visible === true) const readOnly = useSelector(selectReadOnly) @@ -64,14 +72,25 @@ function VesselGroupSection(): React.ReactElement { {dataviews.length > 0 ? ( dataviews?.map((dataview) => { - const vesselGroup = allVesselGroups.find((vesselGroup) => - dataview.config?.filters?.['vessel-groups'].includes(vesselGroup.id) - ) + const dataviewVesselGroups = dataview.config?.filters?.['vessel-groups'] + const vesselGroup = + workspaceVesselGroupsStatus === AsyncReducerStatus.Loading + ? ({ + id: dataviewVesselGroups[0], + name: t('vesselGroup.loadingInfo', 'Loading vessel group info'), + } as VesselGroup) + : allVesselGroups.find((vesselGroup) => + dataviewVesselGroups.includes(vesselGroup.id) + ) return ( ) }) diff --git a/apps/fishing-map/public/locales/source/translations.json b/apps/fishing-map/public/locales/source/translations.json index b4bd419ed6..efb886ba46 100644 --- a/apps/fishing-map/public/locales/source/translations.json +++ b/apps/fishing-map/public/locales/source/translations.json @@ -1011,8 +1011,10 @@ "idsPlaceholder": "Type here or paste a list of {{field}} separated by commas, spaces or line breaks", "label_one": "{{name}} ({{count}} IDs)", "label_other": "{{name}} ({{count}} IDs)", + "loadingInfo": "Loading vessel group info", "missingName": "Vessel group name is mandatory", "new": "New vessel group", + "notFound": "Vessel group not found", "remove": "Remove vessel group", "removeVessel": "Remove vessel from group", "saveAndFilter": "Save and filter workspace", From afd8173496cc45519978ab802679bcec5cfe6e30 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 16:38:00 +0200 Subject: [PATCH 19/23] fix false positive vessel group spinners --- .../vessel-groups/VesselGroupsSection.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx index 6576591ec3..1c2264c770 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx @@ -73,22 +73,22 @@ function VesselGroupSection(): React.ReactElement { {dataviews.length > 0 ? ( dataviews?.map((dataview) => { const dataviewVesselGroups = dataview.config?.filters?.['vessel-groups'] - const vesselGroup = - workspaceVesselGroupsStatus === AsyncReducerStatus.Loading - ? ({ - id: dataviewVesselGroups[0], - name: t('vesselGroup.loadingInfo', 'Loading vessel group info'), - } as VesselGroup) - : allVesselGroups.find((vesselGroup) => - dataviewVesselGroups.includes(vesselGroup.id) - ) + let vesselGroup = allVesselGroups.find((vesselGroup) => + dataviewVesselGroups.includes(vesselGroup.id) + ) + if (workspaceVesselGroupsStatus === AsyncReducerStatus.Loading && !vesselGroup) { + vesselGroup = { + id: dataviewVesselGroups[0], + name: t('vesselGroup.loadingInfo', 'Loading vessel group info'), + } as VesselGroup + } return ( From d6a34c9805bdce50578aeadeb7436699736e4f12 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 17:17:59 +0200 Subject: [PATCH 20/23] resolve vesselGroup in dataview --- .../dataviews.instances.selectors.ts | 6 +++++- .../dataviews-client/src/resolve-dataviews.ts | 21 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts index 70e941d868..9701b58e09 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts @@ -44,6 +44,7 @@ import { } from 'routes/routes.selectors' import { AsyncReducerStatus } from 'utils/async-slice' import { createDeepEqualSelector } from 'utils/selectors' +import { selectAllVesselGroups } from 'features/vessel-groups/vessel-groups.slice' const EMPTY_ARRAY: [] = [] @@ -130,6 +131,7 @@ export const selectAllDataviewInstancesResolved = createSelector( selectDataviewInstancesMergedOrdered, selectAllDataviews, selectAllDatasets, + selectAllVesselGroups, selectUserLogged, selectTrackThinningConfig, selectIsGuestUser, @@ -138,6 +140,7 @@ export const selectAllDataviewInstancesResolved = createSelector( dataviewInstances, dataviews, datasets, + vesselGroups, loggedUser, trackThinningZoomConfig, guestUser @@ -181,7 +184,8 @@ export const selectAllDataviewInstancesResolved = createSelector( const dataviewInstancesResolved = resolveDataviews( dataviewInstancesWithDatasetConfig, dataviews, - datasets + datasets, + vesselGroups ) const callbacks: GetDatasetConfigsCallbacks = { info: infoDatasetConfigsCallback(guestUser), diff --git a/libs/dataviews-client/src/resolve-dataviews.ts b/libs/dataviews-client/src/resolve-dataviews.ts index 0b1e345d69..f4f1a06af4 100644 --- a/libs/dataviews-client/src/resolve-dataviews.ts +++ b/libs/dataviews-client/src/resolve-dataviews.ts @@ -14,6 +14,7 @@ import { FilterOperator, INCLUDE_FILTER_ID, Resource, + VesselGroup, } from '@globalfishingwatch/api-types' import { removeDatasetVersion, resolveEndpoint } from '@globalfishingwatch/datasets-client' import { isNumeric } from '@globalfishingwatch/data-transforms' @@ -387,6 +388,20 @@ export function getDataviewSqlFiltersResolved(dataview: DataviewInstance | UrlDa return sqlFilters.length ? sqlFilters.join(' AND ') : '' } +export function getDataviewVesselGroupId(dataview: UrlDataviewInstance): string | undefined { + return dataview.config?.filters?.['vessel-groups']?.[0] +} + +export function getDataviewVesselGroup( + dataview: UrlDataviewInstance, + vesselGroups: VesselGroup[] +): VesselGroup | undefined { + if (!dataview || !vesselGroups?.length) return + + const dataviewVesselGroupId = getDataviewVesselGroupId(dataview) + return vesselGroups?.find(({ id }) => id === dataviewVesselGroupId) +} + /** * Gets list of dataviews and those present in the workspace, and applies any config or datasetConfig * from it (merges dataview.config and workspace's dataviewConfig and datasetConfig). @@ -396,7 +411,8 @@ export function getDataviewSqlFiltersResolved(dataview: DataviewInstance | UrlDa export function resolveDataviews( dataviewInstances: UrlDataviewInstance[], dataviews: Dataview[], - datasets: Dataset[] + datasets: Dataset[], + vesselGroups: VesselGroup[] ) { let dataviewInstancesResolved: UrlDataviewInstance[] = dataviewInstances.flatMap( (dataviewInstance) => { @@ -499,6 +515,8 @@ export function resolveDataviews( return dataset || [] }) + const vesselGroup = getDataviewVesselGroup(dataviewInstance, vesselGroups) + const resolvedDataview = { ...dataview, // Supports overriding the category so we can easily move user layers to context section @@ -507,6 +525,7 @@ export function resolveDataviews( dataviewId: dataview.slug, config, datasets: dataviewDatasets, + vesselGroup, datasetsConfig, } return resolvedDataview From 398029f7652bb8b00317055f5a47fcc1adec1d73 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 17:18:43 +0200 Subject: [PATCH 21/23] refresh Footprint layer after vessel group edit --- libs/api-types/src/dataviews.ts | 6 +++++- libs/deck-layer-composer/src/resolvers/dataviews.ts | 1 + libs/deck-layer-composer/src/resolvers/fourwings.ts | 1 + .../fourwings/footprint/FourwingsFootprintTileLayer.ts | 4 ++++ libs/deck-layers/src/layers/fourwings/fourwings.types.ts | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/api-types/src/dataviews.ts b/libs/api-types/src/dataviews.ts index 7ea71573f1..38e39db660 100644 --- a/libs/api-types/src/dataviews.ts +++ b/libs/api-types/src/dataviews.ts @@ -1,6 +1,7 @@ import { Locale } from './i18n' import { ApiAppName } from './workspaces' import { Dataset } from './datasets' +import { VesselGroup } from './vesselGroups' export type ColorCyclingType = 'fill' | 'line' export const INCLUDE_FILTER_ID = 'include' @@ -39,12 +40,14 @@ export enum DataviewType { export type DataviewSublayerConfig = { id: string datasets: Dataset[] + visible?: boolean color?: string colorRamp?: string - visible?: boolean filter?: DataviewConfig['filter'] filters?: DataviewConfig['filters'] vesselGroups?: DataviewConfig['vessel-groups'] + /** Needed to update the layer when the vessel group is edited */ + vesselGroupsLength?: number maxZoom?: number } @@ -213,6 +216,7 @@ export interface Dataview { updatedAt?: string config: DataviewConfig datasets?: Dataset[] + vesselGroup?: VesselGroup infoConfig?: DataviewInfoConfig eventsConfig?: DataviewEventsConfig filtersConfig?: DataviewFiltersConfig diff --git a/libs/deck-layer-composer/src/resolvers/dataviews.ts b/libs/deck-layer-composer/src/resolvers/dataviews.ts index 62ae32134a..5c70f8fc56 100644 --- a/libs/deck-layer-composer/src/resolvers/dataviews.ts +++ b/libs/deck-layer-composer/src/resolvers/dataviews.ts @@ -108,6 +108,7 @@ export function getFourwingsDataviewSublayers(dataview: UrlDataviewInstance) { visible: config.visible, filter: config.filter, vesselGroups: config['vessel-groups'], + vesselGroupsLength: dataview.vesselGroup?.vessels?.length, maxZoom, } diff --git a/libs/deck-layer-composer/src/resolvers/fourwings.ts b/libs/deck-layer-composer/src/resolvers/fourwings.ts index 92bf51f824..fee179ab1b 100644 --- a/libs/deck-layer-composer/src/resolvers/fourwings.ts +++ b/libs/deck-layer-composer/src/resolvers/fourwings.ts @@ -60,6 +60,7 @@ export const resolveDeckFourwingsLayerProps: DeckResolverFunction s.datasets || []).join(',') const sublayersFilters = this.props.sublayers?.flatMap((s) => s.filter || []).join(',') const sublayersVesselGroups = this.props.sublayers?.map((s) => s.vesselGroups || []).join(',') + const sublayersVesselGroupsLength = this.props.sublayers + ?.map((s) => s.vesselGroupsLength || []) + .join(',') return [ dataCache, sublayersIds, sublayersDatasets, sublayersFilters, sublayersVesselGroups, + sublayersVesselGroupsLength, ].join('-') } diff --git a/libs/deck-layers/src/layers/fourwings/fourwings.types.ts b/libs/deck-layers/src/layers/fourwings/fourwings.types.ts index ead22cb1fb..4847db8614 100644 --- a/libs/deck-layers/src/layers/fourwings/fourwings.types.ts +++ b/libs/deck-layers/src/layers/fourwings/fourwings.types.ts @@ -53,6 +53,7 @@ export type FourwingsDeckSublayer = { filter?: string positionProperties?: string[] vesselGroups?: string | string[] + vesselGroupsLength?: number extentStart?: number extentEnd?: number } From fee4cf74e5a30a905143d6f28c49e52c44df2ee4 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 17:19:13 +0200 Subject: [PATCH 22/23] use dataview vessel group --- .../vessel-groups/VesselGroupsLayerPanel.tsx | 16 ++++++++++------ .../vessel-groups/VesselGroupsSection.tsx | 19 +++---------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx index 7295a32310..66cc24c77e 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx @@ -2,7 +2,6 @@ import { Fragment, useState } from 'react' import cx from 'classnames' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' -import { VesselGroup } from '@globalfishingwatch/api-types' import { IconButton, ColorBarOption, Tooltip } from '@globalfishingwatch/ui-components' import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' import { useGetDeckLayer } from '@globalfishingwatch/deck-layer-composer' @@ -28,19 +27,18 @@ import VesselGroupNotFound from './VesselGroupNotFound' export type VesselGroupLayerPanelProps = { dataview: UrlDataviewInstance - vesselGroup?: VesselGroup vesselGroupLoading?: boolean } function VesselGroupLayerPanel({ dataview, - vesselGroup, vesselGroupLoading, }: VesselGroupLayerPanelProps): React.ReactElement { const { t } = useTranslation() const dispatch = useAppDispatch() const isGFWUser = useSelector(selectIsGFWUser) const readOnly = useSelector(selectReadOnly) + const { vesselGroup } = dataview const { upsertDataviewInstance } = useDataviewInstancesConnect() const activityLayer = useGetDeckLayer(dataview?.id) @@ -105,9 +103,15 @@ function VesselGroupLayerPanel({ content={t('vesselGroupReport.clickToSee', 'Click to see the vessel group report')} > - {formatInfoField(vesselGroup?.name, 'name')}{' '} - {vesselGroup?.vessels?.length && ( - ({vesselGroup?.vessels.length}) + {vesselGroupLoading ? ( + t('vesselGroup.loadingInfo', 'Loading vessel group info') + ) : ( + + {formatInfoField(vesselGroup?.name, 'name')}{' '} + {vesselGroup?.vessels?.length && ( + ({vesselGroup?.vessels.length}) + )} + )} diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx index 1c2264c770..3a33d29283 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsSection.tsx @@ -3,7 +3,6 @@ import { useSelector } from 'react-redux' import { SortableContext } from '@dnd-kit/sortable' import cx from 'classnames' import { useTranslation } from 'react-i18next' -import { VesselGroup } from '@globalfishingwatch/api-types' import styles from 'features/workspace/shared/Sections.module.css' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' @@ -11,7 +10,6 @@ import { selectReadOnly } from 'features/app/selectors/app.selectors' import VesselGroupListTooltip from 'features/vessel-groups/VesselGroupListTooltip' import { getVesselGroupDataviewInstance } from 'features/dataviews/dataviews.utils' import { selectVesselGroupDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' -import { selectAllVisibleVesselGroups } from 'features/user/selectors/user.permissions.selectors' import { useAppDispatch } from 'features/app/app.hooks' import { selectVesselGroupsStatusId, @@ -27,7 +25,6 @@ function VesselGroupSection(): React.ReactElement { const { t } = useTranslation() const dispatch = useAppDispatch() const dataviews = useSelector(selectVesselGroupDataviews) - const allVesselGroups = useSelector(selectAllVisibleVesselGroups) const workspaceVesselGroupsStatus = useSelector(selectWorkspaceVesselGroupsStatus) const vesselGroupsStatusId = useSelector(selectVesselGroupsStatusId) const { upsertDataviewInstance } = useDataviewInstancesConnect() @@ -72,24 +69,14 @@ function VesselGroupSection(): React.ReactElement { {dataviews.length > 0 ? ( dataviews?.map((dataview) => { - const dataviewVesselGroups = dataview.config?.filters?.['vessel-groups'] - let vesselGroup = allVesselGroups.find((vesselGroup) => - dataviewVesselGroups.includes(vesselGroup.id) - ) - if (workspaceVesselGroupsStatus === AsyncReducerStatus.Loading && !vesselGroup) { - vesselGroup = { - id: dataviewVesselGroups[0], - name: t('vesselGroup.loadingInfo', 'Loading vessel group info'), - } as VesselGroup - } return ( ) From 7bf1e4cb12a3a9cbee6a96f9a6a5423b4922c510 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Fri, 30 Aug 2024 17:32:23 +0200 Subject: [PATCH 23/23] close vesselGroupList on click --- .../vessel-groups/VesselGroupListTooltip.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx index 42203d4ab8..ad8b1169d5 100644 --- a/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx +++ b/apps/fishing-map/features/vessel-groups/VesselGroupListTooltip.tsx @@ -31,6 +31,16 @@ function VesselGroupListTooltip(props: VesselGroupListTooltipProps) { } }, [vesselGroupOptions?.length, vesselGroupsOpen]) + const handleVesselGroupClick = useCallback( + (vesselGroupId: string) => { + if (onAddToVesselGroup) { + onAddToVesselGroup(vesselGroupId) + setVesselGroupsOpen(false) + } + }, + [onAddToVesselGroup] + ) + return ( onAddToVesselGroup?.(NEW_VESSEL_GROUP_ID)} + onClick={() => handleVesselGroupClick(NEW_VESSEL_GROUP_ID)} key="new-group" > {t('vesselGroup.createNewGroup', 'Create new group')} @@ -51,11 +61,7 @@ function VesselGroupListTooltip(props: VesselGroupListTooltipProps) {
  • onAddToVesselGroup(group.id) - : undefined - } + onClick={!group.loading ? () => handleVesselGroupClick(group.id) : undefined} > {group.label} {group.loading && }