From ef1cfa80a259775dc8194c54b5966441fa2827bb Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 07:39:12 +0200 Subject: [PATCH 01/15] fix circular dependencies to use selectDataviewInstancesResolvedVisible --- apps/fishing-map/data/config.ts | 9 +- .../app/selectors/app.reports.selector.ts | 50 +--- .../features/app/selectors/app.selectors.ts | 5 +- .../app/selectors/app.workspace.selectors.ts | 2 +- .../features/datasets/upload/NewDataset.tsx | 2 +- .../dataviews.categories.selectors.ts | 4 +- .../dataviews.instances.selectors.ts | 277 ++++-------------- .../dataviews.resolvers.selectors.ts | 254 ++++++++++++++++ .../selectors/dataviews.selectors.ts | 250 +++------------- apps/fishing-map/features/debug/DebugMenu.tsx | 2 +- .../features/editor/WorkspaceEditor.tsx | 2 +- .../layer-library/LayerLibraryItem.tsx | 4 +- .../features/map/controls/MapControls.tsx | 2 +- .../features/map/map-layers.hooks.ts | 2 +- .../features/map/popups/PopupByCategory.tsx | 2 +- .../activity/ReportActivityGraphSelector.tsx | 2 +- .../areas/environment/ReportEnvironment.tsx | 2 +- .../reports/areas/reports-timeseries.hooks.ts | 6 +- .../reports/areas/reports.selectors.ts | 6 +- .../reports/areas/summary/ReportSummary.tsx | 6 +- .../reports/areas/vessels/ReportVessels.tsx | 6 +- .../vessels/ReportVesselsGraphSelector.tsx | 6 +- .../areas/vessels/ReportVesselsTable.tsx | 6 +- .../vessel-group-report.dataviews.ts | 31 ++ .../vessel-group-report.selectors.ts | 28 +- .../vessel-group.config.selectors.ts | 21 ++ .../vessels/VesselGroupReportVesselsTable.tsx | 2 +- .../features/resources/resources.hooks.ts | 2 +- apps/fishing-map/features/sidebar/Sidebar.tsx | 2 +- .../features/timebar/TimebarActivityGraph.tsx | 2 +- .../features/timebar/timebar.selectors.ts | 47 ++- .../features/workspace/Workspace.tsx | 2 +- .../legacy-activity-category.hook.ts | 2 +- .../features/workspace/workspace.hook.ts | 2 +- 34 files changed, 516 insertions(+), 532 deletions(-) create mode 100644 apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts diff --git a/apps/fishing-map/data/config.ts b/apps/fishing-map/data/config.ts index 0e947e1e6a..d475f1d764 100644 --- a/apps/fishing-map/data/config.ts +++ b/apps/fishing-map/data/config.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon' -import { DataviewCategory } from '@globalfishingwatch/api-types' +import { DataviewCategory, DataviewType } from '@globalfishingwatch/api-types' import { AppState, TimebarGraphs, TimebarVisualisations, UserTab, WorkspaceState } from '../types' import { getUTCDateTime } from '../utils/dates' @@ -136,3 +136,10 @@ export const POPUP_CATEGORY_ORDER = [ ] export const FIT_BOUNDS_REPORT_PADDING = 30 + +export const REPORT_ONLY_VISIBLE_LAYERS = [ + DataviewType.Basemap, + DataviewType.Context, + DataviewType.UserContext, + DataviewType.UserPoints, +] 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 3738837587..2d1d822126 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -1,10 +1,5 @@ import { createSelector } from '@reduxjs/toolkit' -import { - selectActiveDetectionsDataviews, - selectActiveHeatmapEnvironmentalDataviews, - selectActiveReportActivityDataviews, - selectReportActiveCategories, -} from 'features/dataviews/selectors/dataviews.selectors' +import { selectActiveDataviewsCategories } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectReportById } from 'features/reports/areas/reports.slice' import { selectLocationAreaId, @@ -15,7 +10,6 @@ import { selectUrlBufferValueQuery, } from 'routes/routes.selectors' import { BufferOperation, BufferUnit } from 'types' -import { createDeepEqualSelector } from 'utils/selectors' import { selectReportBufferOperationSelector, selectReportBufferUnitSelector, @@ -25,10 +19,6 @@ import { } from 'features/reports/areas/reports.config.selectors' import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' -export function isActivityReport(reportCategory: ReportCategory) { - return reportCategory === ReportCategory.Fishing || reportCategory === ReportCategory.Presence -} - export const selectCurrentReport = createSelector( [selectReportId, (state) => state.reports], (reportId, reports) => { @@ -51,6 +41,21 @@ export const selectReportAreaId = createSelector( } ) +export const selectReportActiveCategories = createSelector( + [selectActiveDataviewsCategories], + (activeCategories): ReportCategory[] => { + const orderedCategories = [ + ReportCategory.Fishing, + ReportCategory.Presence, + ReportCategory.Detections, + ReportCategory.Environment, + ] + return orderedCategories.flatMap((category) => + activeCategories.some((a) => a === category) ? category : [] + ) + } +) + export const selectReportCategory = createSelector( [selectReportCategorySelector, selectReportActiveCategories], (reportCategory, activeCategories): ReportCategory => { @@ -60,29 +65,6 @@ export const selectReportCategory = createSelector( } ) -export const selectActiveReportDataviews = createDeepEqualSelector( - [ - selectReportCategory, - selectActiveReportActivityDataviews, - selectActiveDetectionsDataviews, - selectActiveHeatmapEnvironmentalDataviews, - ], - ( - reportCategory, - activityDataviews = [], - detectionsDataviews = [], - environmentalDataviews = [] - ) => { - if (isActivityReport(reportCategory)) { - return activityDataviews - } - if (reportCategory === ReportCategory.Detections) { - return detectionsDataviews - } - return environmentalDataviews - } -) - export const selectReportVesselGraph = createSelector( [selectReportVesselGraphSelector, selectReportCategory], (reportVesselGraph, reportCategory): ReportVesselGraph => { diff --git a/apps/fishing-map/features/app/selectors/app.selectors.ts b/apps/fishing-map/features/app/selectors/app.selectors.ts index 40bb319909..b7616ea4b8 100644 --- a/apps/fishing-map/features/app/selectors/app.selectors.ts +++ b/apps/fishing-map/features/app/selectors/app.selectors.ts @@ -6,14 +6,13 @@ import { getActiveActivityDatasetsInDataviews, getLatestEndDateFromDatasets, } from 'features/datasets/datasets.utils' -import { selectActiveDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsAnyReportLocation } from 'routes/routes.selectors' -import { MapAnnotation } from 'features/map/overlays/annotations/annotations.types' const EMPTY_ARRAY: [] = [] export const selectLatestAvailableDataDate = createSelector( - [selectActiveDataviewInstancesResolved], + [selectDataviewInstancesResolvedVisible], (dataviews) => { const activeDatasets = dataviews.flatMap((dataview) => { if (dataview.category === DataviewCategory.Context) { 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 4dc4365928..fd6609bc26 100644 --- a/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts +++ b/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts @@ -27,7 +27,7 @@ import { import { AppWorkspace } from 'features/workspaces-list/workspaces-list.slice' import { selectLocationCategory } from 'routes/routes.selectors' import { selectViewport } from 'features/app/selectors/app.viewport.selectors' -import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectDaysFromLatest, selectWorkspace } from 'features/workspace/workspace.selectors' import { DEFAULT_WORKSPACE_CATEGORY } from 'data/workspaces' import { diff --git a/apps/fishing-map/features/datasets/upload/NewDataset.tsx b/apps/fishing-map/features/datasets/upload/NewDataset.tsx index b915e69982..3415d12342 100644 --- a/apps/fishing-map/features/datasets/upload/NewDataset.tsx +++ b/apps/fishing-map/features/datasets/upload/NewDataset.tsx @@ -17,7 +17,7 @@ import { selectIsGuestUser } from 'features/user/selectors/user.selectors' import { getFinalDatasetFromMetadata } from 'features/datasets/upload/datasets-upload.utils' import UserGuideLink from 'features/help/UserGuideLink' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' -import { selectDataviewInstancesMerged } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesMerged } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { useDatasetsAPI, useDatasetModalOpenConnect, diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts index 182d3fa00a..2e49735555 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts @@ -1,11 +1,11 @@ import { createSelector } from '@reduxjs/toolkit' import { DataviewCategory, DataviewType } from '@globalfishingwatch/api-types' import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' -import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolvedVisible } from './dataviews.instances.selectors' export const selectDataviewInstancesByCategory = (category?: DataviewCategory) => { return createSelector( - [selectDataviewInstancesResolved], + [selectDataviewInstancesResolvedVisible], (dataviews): UrlDataviewInstance[] => { return dataviews?.filter((dataview) => dataview.category === category) } 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 f5ee7cc384..fb66ef9fab 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts @@ -1,248 +1,89 @@ import { createSelector } from '@reduxjs/toolkit' -import { - DatasetTypes, - DataviewCategory, - DataviewType, - DataviewDatasetConfig, - DataviewInstance, -} from '@globalfishingwatch/api-types' -import { - extendDataviewDatasetConfig, - GetDatasetConfigsCallbacks, - getResources, - mergeWorkspaceUrlDataviewInstances, - resolveDataviews, - UrlDataviewInstance, -} from '@globalfishingwatch/dataviews-client' -import { VESSEL_PROFILE_DATAVIEWS_INSTANCES } from 'data/default-workspaces/context-layers' -import { selectAllDatasets, selectDeprecatedDatasets } from 'features/datasets/datasets.slice' -import { getRelatedDatasetByType } from 'features/datasets/datasets.utils' -import { selectAllDataviews } from 'features/dataviews/dataviews.slice' -import { - dataviewHasVesselGroupId, - getVesselDataviewInstance, - getVesselDataviewInstanceDatasetConfig, - VESSEL_DATAVIEW_INSTANCE_PREFIX, -} from 'features/dataviews/dataviews.utils' -import { selectTrackThinningConfig } from 'features/resources/resources.selectors.thinning' -import { infoDatasetConfigsCallback } from 'features/resources/resources.utils' -import { selectIsGuestUser, selectUserLogged } from 'features/user/selectors/user.selectors' +import { DatasetTypes, DataviewCategory, DataviewType } from '@globalfishingwatch/api-types' +import { selectDeprecatedDatasets } from 'features/datasets/datasets.slice' +import { VESSEL_DATAVIEW_INSTANCE_PREFIX } from 'features/dataviews/dataviews.utils' import { selectViewOnlyVessel } from 'features/vessel/vessel.config.selectors' -import { selectVesselInfoData } from 'features/vessel/selectors/vessel.selectors' -import { getRelatedIdentityVesselIds } from 'features/vessel/vessel.utils' import { - selectWorkspaceDataviewInstances, - selectWorkspaceStateProperty, - selectWorkspaceStatus, -} from 'features/workspace/workspace.selectors' -import { - selectIsWorkspaceLocation, - selectUrlDataviewInstances, selectIsAnyVesselLocation, - selectIsVesselLocation, selectVesselId, - selectUrlDataviewInstancesOrder, selectIsVesselGroupReportLocation, selectReportVesselGroupId, + selectIsAnyReportLocation, } from 'routes/routes.selectors' -import { AsyncReducerStatus } from 'utils/async-slice' import { createDeepEqualSelector } from 'utils/selectors' -import { selectAllVesselGroups } from 'features/vessel-groups/vessel-groups.slice' +import { getReportVesselGroupVisibleDataviews } from 'features/reports/vessel-groups/vessel-group-report.dataviews' +import { REPORT_ONLY_VISIBLE_LAYERS } from 'data/config' +import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' import { - getVesselGroupDataviewInstance, - getVesselGroupEventsDataviewInstances, -} from 'features/reports/vessel-groups/vessel-group-report.dataviews' - -const EMPTY_ARRAY: [] = [] + selectVGRSection, + selectVGRSubsection, + selectViewOnlyVesselGroup, +} from 'features/reports/vessel-groups/vessel-group.config.selectors' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' +import { + selectAllDataviewInstancesResolved, + selectDataviewInstancesResolved, +} from './dataviews.resolvers.selectors' -export const selectDataviewInstancesMerged = createSelector( +export const selectDataviewInstancesResolvedVisible = createSelector( [ - selectIsWorkspaceLocation, - selectWorkspaceStatus, - selectWorkspaceDataviewInstances, - selectUrlDataviewInstances, + selectDataviewInstancesResolved, + selectIsAnyReportLocation, + selectReportCategory, selectIsAnyVesselLocation, - selectIsVesselLocation, + selectViewOnlyVessel, + selectVesselId, selectIsVesselGroupReportLocation, selectReportVesselGroupId, - selectVesselId, - selectVesselInfoData, + selectVGRSection, + selectVGRSubsection, + selectViewOnlyVesselGroup, ], ( - isWorkspaceLocation, - workspaceStatus, - workspaceDataviewInstances, - urlDataviewInstances = EMPTY_ARRAY, - isAnyVesselLocation, + dataviews = [], + isReportLocation, + reportCategory, isVesselLocation, + viewOnlyVessel, + vesselId, isVesselGroupReportLocation, reportVesselGroupId, - urlVesselId, - vessel - ): UrlDataviewInstance[] | undefined => { - if (isWorkspaceLocation && workspaceStatus !== AsyncReducerStatus.Finished) { - return - } - const mergedDataviewInstances = - mergeWorkspaceUrlDataviewInstances( - workspaceDataviewInstances as DataviewInstance[], - urlDataviewInstances - ) || [] - - if (isAnyVesselLocation) { - const existingDataviewInstance = mergedDataviewInstances?.find( - ({ id }) => urlVesselId && id.includes(urlVesselId) - ) - if (!existingDataviewInstance && vessel?.identities) { - const vesselDatasets = { - info: vessel.info, - track: vessel.track, - ...(vessel?.events?.length && { - events: vessel?.events, - }), - relatedVesselIds: getRelatedIdentityVesselIds(vessel), + vGRSection, + vGRSubsection, + viewOnlyVesselGroup + ) => { + const visibleDataviews = dataviews.filter((dataview) => dataview.config?.visible) + if (isReportLocation) { + return visibleDataviews.filter((dataview) => { + if ( + dataview.category === DataviewCategory.Activity || + dataview.category === DataviewCategory.Detections + ) { + return getReportCategoryFromDataview(dataview) === reportCategory } - - const dataviewInstance = getVesselDataviewInstance({ id: urlVesselId }, vesselDatasets) - const datasetsConfig: DataviewDatasetConfig[] = getVesselDataviewInstanceDatasetConfig( - urlVesselId, - vesselDatasets - ) - mergedDataviewInstances.push({ ...dataviewInstance, datasetsConfig }) - } - if (isVesselLocation) { - VESSEL_PROFILE_DATAVIEWS_INSTANCES.forEach((dataviewInstance) => { - if (!mergedDataviewInstances.find(({ id }) => id === dataviewInstance.id)) { - mergedDataviewInstances.push({ ...dataviewInstance }) - } - }) - } + return true + }) } - if (isVesselGroupReportLocation) { - const existingDataviewInstance = mergedDataviewInstances?.find((dataview) => - dataviewHasVesselGroupId(dataview, reportVesselGroupId) - ) - if (!existingDataviewInstance) { - const dataviewInstance = getVesselGroupDataviewInstance(reportVesselGroupId) - if (dataviewInstance) { - mergedDataviewInstances.push(dataviewInstance) + if (isVesselLocation && viewOnlyVessel && vesselId !== undefined) { + return visibleDataviews.filter(({ id, config }) => { + if (REPORT_ONLY_VISIBLE_LAYERS.includes(config?.type as DataviewType)) { + return true } - } - mergedDataviewInstances.push(...getVesselGroupEventsDataviewInstances(reportVesselGroupId)) - } - return mergedDataviewInstances - } -) - -export const selectDataviewInstancesMergedOrdered = createSelector( - [selectDataviewInstancesMerged, selectUrlDataviewInstancesOrder], - (dataviewInstances = [], dataviewInstancesOrder): UrlDataviewInstance[] => { - if (!dataviewInstancesOrder || !dataviewInstancesOrder.length) { - return dataviewInstances + return config?.type === DataviewType.Track && id.includes(vesselId) + }) } - const dataviewInstancesOrdered = dataviewInstances.sort( - (a, b) => dataviewInstancesOrder.indexOf(a.id) - dataviewInstancesOrder.indexOf(b.id) - ) - return [...dataviewInstancesOrdered] - } -) - -export const selectTimebarGraphSelector = selectWorkspaceStateProperty('timebarGraph') -export const selectAllDataviewInstancesResolved = createSelector( - [ - selectDataviewInstancesMergedOrdered, - selectAllDataviews, - selectAllDatasets, - selectAllVesselGroups, - selectUserLogged, - selectTrackThinningConfig, - selectIsGuestUser, - ], - ( - dataviewInstances, - dataviews, - datasets, - vesselGroups, - loggedUser, - trackThinningZoomConfig, - guestUser - ): UrlDataviewInstance[] | undefined => { - if (!dataviews?.length || !datasets?.length || !dataviewInstances?.length) { - return EMPTY_ARRAY - } - const dataviewInstancesWithDatasetConfig = dataviewInstances.map((dataviewInstance) => { - if ( - dataviewInstance.id?.startsWith(VESSEL_DATAVIEW_INSTANCE_PREFIX) && - !dataviewInstance.datasetsConfig?.length && - dataviewInstance.config?.info - ) { - const vesselId = dataviewInstance.id.split(VESSEL_DATAVIEW_INSTANCE_PREFIX)[1] - // New way to resolve datasetConfig for vessels to avoid storing all - // the datasetConfig in the instance and save url string characters - const config = { ...dataviewInstance.config } - // Vessel pined from not logged user but is logged now and the related dataset is available - if (loggedUser && !config.track) { - const dataset = datasets.find((d) => d.id === config.info) - const trackDatasetId = getRelatedDatasetByType(dataset, DatasetTypes.Tracks)?.id - if (trackDatasetId) { - config.track = trackDatasetId - } - } - const datasetsConfig: DataviewDatasetConfig[] = getVesselDataviewInstanceDatasetConfig( - vesselId, - config - ) - return { - ...dataviewInstance, - config: { - ...dataviewInstance.config, - trackThinningZoomConfig, - }, - datasetsConfig, - } - } - return dataviewInstance - }) - const dataviewInstancesResolved = resolveDataviews( - dataviewInstancesWithDatasetConfig, - dataviews, - datasets, - vesselGroups - ) - const callbacks: GetDatasetConfigsCallbacks = { - info: infoDatasetConfigsCallback(guestUser), + if (isVesselGroupReportLocation && viewOnlyVesselGroup && reportVesselGroupId !== undefined) { + const dataviewsVisible = getReportVesselGroupVisibleDataviews({ + dataviews: visibleDataviews, + reportVesselGroupId, + vesselGroupReportSection: vGRSection, + vesselGroupReportSubSection: vGRSubsection, + }) + return dataviewsVisible } - const dataviewInstancesResolvedExtended = extendDataviewDatasetConfig( - dataviewInstancesResolved, - callbacks - ) - return dataviewInstancesResolvedExtended - } -) -/** - * Calls getResources to prepare track dataviews' datasetConfigs. - * Injects app-specific logic by using getResources's callback - */ -export const selectDataviewsResources = createSelector( - [selectAllDataviewInstancesResolved], - (dataviewInstances) => { - return getResources(dataviewInstances || []) - } -) - -const defaultDataviewResolved: UrlDataviewInstance[] = [] -export const selectDataviewInstancesResolved = createSelector( - [selectDataviewsResources], - (dataviewsResources) => { - return dataviewsResources.dataviews || defaultDataviewResolved - } -) -export const selectActiveDataviewInstancesResolved = createSelector( - [selectDataviewInstancesResolved], - (dataviewInstances) => { - return dataviewInstances.filter((d) => d.config?.visible) + return visibleDataviews } ) diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts new file mode 100644 index 0000000000..bae0f0da64 --- /dev/null +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts @@ -0,0 +1,254 @@ +import { createSelector } from '@reduxjs/toolkit' +import { uniq } from 'es-toolkit' +import { + DatasetTypes, + DataviewDatasetConfig, + DataviewInstance, +} from '@globalfishingwatch/api-types' +import { + extendDataviewDatasetConfig, + GetDatasetConfigsCallbacks, + getResources, + mergeWorkspaceUrlDataviewInstances, + resolveDataviews, + UrlDataviewInstance, +} from '@globalfishingwatch/dataviews-client' +import { ColorRampId } from '@globalfishingwatch/deck-layers' +import { VESSEL_PROFILE_DATAVIEWS_INSTANCES } from 'data/default-workspaces/context-layers' +import { selectAllDatasets } from 'features/datasets/datasets.slice' +import { getRelatedDatasetByType } from 'features/datasets/datasets.utils' +import { selectAllDataviews } from 'features/dataviews/dataviews.slice' +import { + dataviewHasVesselGroupId, + getVesselDataviewInstance, + getVesselDataviewInstanceDatasetConfig, + VESSEL_DATAVIEW_INSTANCE_PREFIX, +} from 'features/dataviews/dataviews.utils' +import { selectTrackThinningConfig } from 'features/resources/resources.selectors.thinning' +import { infoDatasetConfigsCallback } from 'features/resources/resources.utils' +import { selectIsGuestUser, selectUserLogged } from 'features/user/selectors/user.selectors' +import { selectVesselInfoData } from 'features/vessel/selectors/vessel.selectors' +import { getRelatedIdentityVesselIds } from 'features/vessel/vessel.utils' +import { + selectWorkspaceDataviewInstances, + selectWorkspaceStatus, +} from 'features/workspace/workspace.selectors' +import { + selectIsWorkspaceLocation, + selectUrlDataviewInstances, + selectIsAnyVesselLocation, + selectIsVesselLocation, + selectVesselId, + selectUrlDataviewInstancesOrder, + selectIsVesselGroupReportLocation, + selectReportVesselGroupId, +} from 'routes/routes.selectors' +import { AsyncReducerStatus } from 'utils/async-slice' +import { selectAllVesselGroups } from 'features/vessel-groups/vessel-groups.slice' +import { + getVesselGroupActivityDataviewInstance, + getVesselGroupDataviewInstance, + getVesselGroupEventsDataviewInstances, +} from 'features/reports/vessel-groups/vessel-group-report.dataviews' +import { ReportCategory } from 'features/reports/areas/reports.types' +import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' + +const EMPTY_ARRAY: [] = [] + +export const selectDataviewInstancesMerged = createSelector( + [ + selectIsWorkspaceLocation, + selectWorkspaceStatus, + selectWorkspaceDataviewInstances, + selectUrlDataviewInstances, + selectIsAnyVesselLocation, + selectIsVesselLocation, + selectIsVesselGroupReportLocation, + selectReportVesselGroupId, + selectVesselId, + selectVesselInfoData, + ], + ( + isWorkspaceLocation, + workspaceStatus, + workspaceDataviewInstances, + urlDataviewInstances = EMPTY_ARRAY, + isAnyVesselLocation, + isVesselLocation, + isVesselGroupReportLocation, + reportVesselGroupId, + urlVesselId, + vessel + ): UrlDataviewInstance[] | undefined => { + if (isWorkspaceLocation && workspaceStatus !== AsyncReducerStatus.Finished) { + return + } + const mergedDataviewInstances = + mergeWorkspaceUrlDataviewInstances( + workspaceDataviewInstances as DataviewInstance[], + urlDataviewInstances + ) || [] + + if (isAnyVesselLocation) { + const existingDataviewInstance = mergedDataviewInstances?.find( + ({ id }) => urlVesselId && id.includes(urlVesselId) + ) + if (!existingDataviewInstance && vessel?.identities) { + const vesselDatasets = { + info: vessel.info, + track: vessel.track, + ...(vessel?.events?.length && { + events: vessel?.events, + }), + relatedVesselIds: getRelatedIdentityVesselIds(vessel), + } + + const dataviewInstance = getVesselDataviewInstance({ id: urlVesselId }, vesselDatasets) + const datasetsConfig: DataviewDatasetConfig[] = getVesselDataviewInstanceDatasetConfig( + urlVesselId, + vesselDatasets + ) + mergedDataviewInstances.push({ ...dataviewInstance, datasetsConfig }) + } + if (isVesselLocation) { + VESSEL_PROFILE_DATAVIEWS_INSTANCES.forEach((dataviewInstance) => { + if (!mergedDataviewInstances.find(({ id }) => id === dataviewInstance.id)) { + mergedDataviewInstances.push({ ...dataviewInstance }) + } + }) + } + } + if (isVesselGroupReportLocation) { + let vesselGroupDataviewInstance = mergedDataviewInstances?.find((dataview) => + dataviewHasVesselGroupId(dataview, reportVesselGroupId) + ) + if (!vesselGroupDataviewInstance) { + vesselGroupDataviewInstance = getVesselGroupDataviewInstance(reportVesselGroupId) + if (vesselGroupDataviewInstance) { + mergedDataviewInstances.push(vesselGroupDataviewInstance) + } + } + const activityVGRInstance = getVesselGroupActivityDataviewInstance({ + vesselGroupId: reportVesselGroupId, + color: vesselGroupDataviewInstance?.config?.color, + colorRamp: vesselGroupDataviewInstance?.config?.colorRamp as ColorRampId, + }) + if (activityVGRInstance) { + mergedDataviewInstances.push(activityVGRInstance) + } + mergedDataviewInstances.push(...getVesselGroupEventsDataviewInstances(reportVesselGroupId)) + } + return mergedDataviewInstances + } +) + +export const selectDataviewInstancesMergedOrdered = createSelector( + [selectDataviewInstancesMerged, selectUrlDataviewInstancesOrder], + (dataviewInstances = [], dataviewInstancesOrder): UrlDataviewInstance[] => { + if (!dataviewInstancesOrder || !dataviewInstancesOrder.length) { + return dataviewInstances + } + const dataviewInstancesOrdered = dataviewInstances.sort( + (a, b) => dataviewInstancesOrder.indexOf(a.id) - dataviewInstancesOrder.indexOf(b.id) + ) + return [...dataviewInstancesOrdered] + } +) + +export const selectAllDataviewInstancesResolved = createSelector( + [ + selectDataviewInstancesMergedOrdered, + selectAllDataviews, + selectAllDatasets, + selectAllVesselGroups, + selectUserLogged, + selectTrackThinningConfig, + selectIsGuestUser, + ], + ( + dataviewInstances, + dataviews, + datasets, + vesselGroups, + loggedUser, + trackThinningZoomConfig, + guestUser + ): UrlDataviewInstance[] | undefined => { + if (!dataviews?.length || !datasets?.length || !dataviewInstances?.length) { + return EMPTY_ARRAY + } + const dataviewInstancesWithDatasetConfig = dataviewInstances.map((dataviewInstance) => { + if ( + dataviewInstance.id?.startsWith(VESSEL_DATAVIEW_INSTANCE_PREFIX) && + !dataviewInstance.datasetsConfig?.length && + dataviewInstance.config?.info + ) { + const vesselId = dataviewInstance.id.split(VESSEL_DATAVIEW_INSTANCE_PREFIX)[1] + // New way to resolve datasetConfig for vessels to avoid storing all + // the datasetConfig in the instance and save url string characters + const config = { ...dataviewInstance.config } + // Vessel pined from not logged user but is logged now and the related dataset is available + if (loggedUser && !config.track) { + const dataset = datasets.find((d) => d.id === config.info) + const trackDatasetId = getRelatedDatasetByType(dataset, DatasetTypes.Tracks)?.id + if (trackDatasetId) { + config.track = trackDatasetId + } + } + const datasetsConfig: DataviewDatasetConfig[] = getVesselDataviewInstanceDatasetConfig( + vesselId, + config + ) + return { + ...dataviewInstance, + config: { + ...dataviewInstance.config, + trackThinningZoomConfig, + }, + datasetsConfig, + } + } + return dataviewInstance + }) + const dataviewInstancesResolved = resolveDataviews( + dataviewInstancesWithDatasetConfig, + dataviews, + datasets, + vesselGroups + ) + const callbacks: GetDatasetConfigsCallbacks = { + info: infoDatasetConfigsCallback(guestUser), + } + const dataviewInstancesResolvedExtended = extendDataviewDatasetConfig( + dataviewInstancesResolved, + callbacks + ) + return dataviewInstancesResolvedExtended + } +) + +/** + * Calls getResources to prepare track dataviews' datasetConfigs. + * Injects app-specific logic by using getResources's callback + */ +export const selectDataviewsResources = createSelector( + [selectAllDataviewInstancesResolved], + (dataviewInstances) => { + return getResources(dataviewInstances || []) + } +) + +const defaultDataviewResolved: UrlDataviewInstance[] = [] +export const selectDataviewInstancesResolved = createSelector( + [selectDataviewsResources], + (dataviewsResources) => { + return dataviewsResources.dataviews || defaultDataviewResolved + } +) + +export const selectActiveDataviewsCategories = createSelector( + [selectDataviewInstancesResolved], + (dataviews): ReportCategory[] => { + return uniq(dataviews.flatMap((d) => getReportCategoryFromDataview(d) || [])) + } +) diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts index f029399ff7..018ef00ef0 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts @@ -1,12 +1,5 @@ import { createSelector } from '@reduxjs/toolkit' -import { uniq } from 'es-toolkit' -import { - DataviewCategory, - Dataset, - DatasetTypes, - Dataview, - DataviewType, -} from '@globalfishingwatch/api-types' +import { Dataset, DatasetTypes, Dataview, DataviewType } from '@globalfishingwatch/api-types' import { UrlDataviewInstance, getMergedDataviewId } from '@globalfishingwatch/dataviews-client' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { @@ -18,43 +11,23 @@ import { 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 { TimebarVisualisations } from 'types' import { createDeepEqualSelector } from 'utils/selectors' import { - selectIsAnyVesselLocation, - selectVesselId, selectIsAnyReportLocation, selectIsVesselGroupReportLocation, selectReportVesselGroupId, } from 'routes/routes.selectors' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' -import { selectViewOnlyVessel } from 'features/vessel/vessel.config.selectors' -import { - selectTimebarSelectedEnvId, - selectTimebarSelectedVGId, -} from 'features/app/selectors/app.timebar.selectors' -import { - selectActiveVesselsDataviews, - selectAllDataviewInstancesResolved, - selectDataviewInstancesMergedOrdered, - selectDataviewInstancesResolved, -} from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { isBathymetryDataview } from 'features/dataviews/dataviews.utils' import { selectDownloadActiveTabId } from 'features/download/downloadActivity.slice' import { HeatmapDownloadTab } from 'features/download/downloadActivity.config' import { selectVGRSection, - selectViewOnlyVesselGroup, + selectVGRSubsection, } from 'features/reports/vessel-groups/vessel-group.config.selectors' +import { getReportVesselGroupVisibleDataviews } from 'features/reports/vessel-groups/vessel-group-report.dataviews' import { ReportCategory } from 'features/reports/areas/reports.types' -import { selectReportCategorySelector } from 'features/reports/areas/reports.config.selectors' -import { - VGREventsSubsection, - VGRSection, - VGRSubsection, -} from 'features/vessel-groups/vessel-groups.types' -import { selectVGRSubsection } from 'features/reports/vessel-groups/vessel-group-report.selectors' -import { DATAVIEW_ID_BY_VESSEL_GROUP_EVENTS } from 'features/reports/vessel-groups/vessel-group-report.dataviews' +import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' import { selectContextAreasDataviews, selectActivityDataviews, @@ -62,138 +35,13 @@ import { selectEnvironmentalDataviews, selectEventsDataviews, selectVesselGroupDataviews, -} from './dataviews.categories.selectors' - -const REPORT_ONLY_VISIBLE_LAYERS = [ - DataviewType.Basemap, - DataviewType.Context, - DataviewType.UserContext, - DataviewType.UserPoints, -] - -const selectActiveDataviewsCategories = createSelector( - [selectDataviewInstancesResolved], - (dataviews): ReportCategory[] => { - return uniq( - dataviews.flatMap((d) => (d.config?.visible ? getReportCategoryFromDataview(d) : [])) - ) - } -) - -export const selectReportActiveCategories = createSelector( - [selectActiveDataviewsCategories], - (activeCategories): ReportCategory[] => { - const orderedCategories = [ - ReportCategory.Fishing, - ReportCategory.Presence, - ReportCategory.Detections, - ReportCategory.Environment, - ] - return orderedCategories.flatMap((category) => - activeCategories.some((a) => a === category) ? category : [] - ) - } -) - -const selectReportCategory = createSelector( - [selectReportCategorySelector, selectReportActiveCategories], - (reportCategory, activeCategories): ReportCategory => { - return activeCategories.some((category) => category === reportCategory) - ? reportCategory - : activeCategories[0] - } -) - -export const selectDataviewInstancesResolvedVisible = createSelector( - [ - selectDataviewInstancesResolved, - selectIsAnyReportLocation, - selectReportCategory, - selectIsAnyVesselLocation, - selectViewOnlyVessel, - selectVesselId, - selectIsVesselGroupReportLocation, - selectReportVesselGroupId, - selectVGRSection, - selectVGRSubsection, - selectViewOnlyVesselGroup, - ], - ( - dataviews = [], - isReportLocation, - reportCategory, - isVesselLocation, - viewOnlyVessel, - vesselId, - isVesselGroupReportLocation, - reportVesselGroupId, - vGRSection, - vGRSubsection, - viewOnlyVesselGroup - ) => { - if (isReportLocation) { - return dataviews.filter((dataview) => { - if ( - dataview.category === DataviewCategory.Activity || - dataview.category === DataviewCategory.Detections - ) { - return ( - dataview.config?.visible && getReportCategoryFromDataview(dataview) === reportCategory - ) - } - return dataview.config?.visible - }) - } - if (isVesselLocation && viewOnlyVessel && vesselId !== undefined) { - return dataviews.filter(({ id, config }) => { - if (REPORT_ONLY_VISIBLE_LAYERS.includes(config?.type as DataviewType)) { - return config?.visible - } - return config?.type === DataviewType.Track && id.includes(vesselId) - }) - } - - if (isVesselGroupReportLocation && viewOnlyVesselGroup && reportVesselGroupId !== undefined) { - const dataviewsVisible = getReportVesselGroupVisibleDataviews({ - dataviews, - reportVesselGroupId, - vesselGroupReportSection: vGRSection, - vesselGroupReportSubSection: vGRSubsection, - }) - return dataviewsVisible - } - - return dataviews.filter((dataview) => dataview.config?.visible) - } -) - -type GetReportVesselGroupVisibleDataviewsParams = { - dataviews: UrlDataviewInstance[] - reportVesselGroupId: string - vesselGroupReportSection: VGRSection - vesselGroupReportSubSection?: VGRSubsection -} -function getReportVesselGroupVisibleDataviews({ - dataviews, - reportVesselGroupId, - vesselGroupReportSection, - vesselGroupReportSubSection, -}: GetReportVesselGroupVisibleDataviewsParams) { - return dataviews.filter(({ id, category, config }) => { - if (REPORT_ONLY_VISIBLE_LAYERS.includes(config?.type as DataviewType)) { - return config?.visible - } - if (vesselGroupReportSection === 'events') { - const dataviewIdBySubSection = - DATAVIEW_ID_BY_VESSEL_GROUP_EVENTS[vesselGroupReportSubSection as VGREventsSubsection] - return id.toString() === dataviewIdBySubSection - } - return ( - category === DataviewCategory.VesselGroups && - config?.filters?.['vessel-groups'].includes(reportVesselGroupId) - ) - }) -} +} from 'features/dataviews/selectors/dataviews.categories.selectors' +import { + selectDataviewInstancesResolved, + selectAllDataviewInstancesResolved, + selectDataviewInstancesMergedOrdered, +} from 'features/dataviews/selectors/dataviews.resolvers.selectors' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' export const selectHasOtherVesselGroupDataviews = createSelector( [ @@ -293,6 +141,38 @@ export const selectActiveHeatmapEnvironmentalDataviews = createSelector( } ) +export function isActivityReport(reportCategory: ReportCategory) { + return reportCategory === ReportCategory.Fishing || reportCategory === ReportCategory.Presence +} + +export const selectActiveReportDataviews = createDeepEqualSelector( + [ + selectReportCategory, + selectActiveReportActivityDataviews, + selectActiveDetectionsDataviews, + selectActiveHeatmapEnvironmentalDataviews, + selectIsVesselGroupReportLocation, + ], + ( + reportCategory, + activityDataviews = [], + detectionsDataviews = [], + environmentalDataviews = [], + isVesselGroupReportLocation + ) => { + if (isVesselGroupReportLocation) { + return activityDataviews + } + if (isActivityReport(reportCategory)) { + return activityDataviews + } + if (reportCategory === ReportCategory.Detections) { + return detectionsDataviews + } + return environmentalDataviews + } +) + export const selectActiveHeatmapAnimatedEnvironmentalDataviews = createSelector( [selectActiveHeatmapEnvironmentalDataviews], (dataviews) => { @@ -364,48 +244,6 @@ const selectActiveEventsDataviews = createSelector([selectEventsDataviews], (dat dataviews?.filter((d) => d.config?.visible) ) -export const selectActiveActivityDataviewsByVisualisation = ( - timebarVisualisation: TimebarVisualisations -) => - createSelector( - [ - selectActiveReportActivityDataviews, - selectActiveDetectionsDataviews, - selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, - selectActiveVesselGroupDataviews, - selectTimebarSelectedEnvId, - selectTimebarSelectedVGId, - ], - ( - activityDataviews, - detectionsDataviews, - environmentDataviews, - vesselGroupDataviews, - timebarSelectedEnvId, - timebarSelectedVGId - ) => { - if (timebarVisualisation === TimebarVisualisations.HeatmapActivity) { - return activityDataviews - } - if (timebarVisualisation === TimebarVisualisations.HeatmapDetections) { - return detectionsDataviews - } - if (timebarVisualisation === TimebarVisualisations.VesselGroup) { - const selectedVGDataview = - timebarSelectedVGId && vesselGroupDataviews.find((d) => d.id === timebarSelectedVGId) - - if (selectedVGDataview) return [selectedVGDataview] - else if (vesselGroupDataviews[0]) return [vesselGroupDataviews[0]] - } - // timebarVisualisation === TimebarVisualisations.Environment - const selectedEnvDataview = - timebarSelectedEnvId && environmentDataviews.find((d) => d.id === timebarSelectedEnvId) - - if (selectedEnvDataview) return [selectedEnvDataview] - else if (environmentDataviews[0]) return [environmentDataviews[0]] - } - ) - export const selectHasReportLayersVisible = createSelector( [selectActiveHeatmapDowloadDataviews], (reportDataviews) => { diff --git a/apps/fishing-map/features/debug/DebugMenu.tsx b/apps/fishing-map/features/debug/DebugMenu.tsx index 0b25bb0c29..c590110a6c 100644 --- a/apps/fishing-map/features/debug/DebugMenu.tsx +++ b/apps/fishing-map/features/debug/DebugMenu.tsx @@ -6,7 +6,7 @@ import { selectLocationQuery } from 'routes/routes.selectors' import { useAppDispatch } from 'features/app/app.hooks' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { debugDatasetsInDataviews, debugRelatedDatasets } from 'features/datasets/datasets.debug' -import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import styles from './DebugMenu.module.css' import { DebugOption, selectDebugOptions, toggleOption } from './debug.slice' diff --git a/apps/fishing-map/features/editor/WorkspaceEditor.tsx b/apps/fishing-map/features/editor/WorkspaceEditor.tsx index 13fbcba742..be65c23436 100644 --- a/apps/fishing-map/features/editor/WorkspaceEditor.tsx +++ b/apps/fishing-map/features/editor/WorkspaceEditor.tsx @@ -11,7 +11,7 @@ import { getDataviewInstanceFromDataview } from 'features/dataviews/dataviews.ut import { useAppDispatch } from 'features/app/app.hooks' import { fetchDatasetsByIdsThunk } from 'features/datasets/datasets.slice' import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' -import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { fetchEditorDataviewsThunk, selectEditorDataviews, diff --git a/apps/fishing-map/features/layer-library/LayerLibraryItem.tsx b/apps/fishing-map/features/layer-library/LayerLibraryItem.tsx index b245f03fa6..368887df61 100644 --- a/apps/fishing-map/features/layer-library/LayerLibraryItem.tsx +++ b/apps/fishing-map/features/layer-library/LayerLibraryItem.tsx @@ -15,7 +15,7 @@ import { getDatasetSourceIcon, getDatasetTypeIcon } from 'features/datasets/data import { selectDatasetById } from 'features/datasets/datasets.slice' import { getHighlightedText } from 'features/layer-library/layer-library.utils' import { LibraryLayer } from 'data/layer-library' -import { selectActiveDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import styles from './LayerLibraryItem.module.css' type LayerLibraryItemProps = { layer: LibraryLayer; highlightedText?: string } @@ -35,7 +35,7 @@ const LayerLibraryItem = (props: LayerLibraryItemProps) => { moreInfoLink, datasetsConfig, } = layer - const dataviews = useSelector(selectActiveDataviewInstancesResolved) + const dataviews = useSelector(selectDataviewInstancesResolvedVisible) const datasetId = dataview.datasetsConfig?.[0].datasetId || '' const dataset = useSelector(selectDatasetById(datasetId)) const { upsertDataviewInstance } = useDataviewInstancesConnect() diff --git a/apps/fishing-map/features/map/controls/MapControls.tsx b/apps/fishing-map/features/map/controls/MapControls.tsx index e250fbd99d..dcc7ee51c1 100644 --- a/apps/fishing-map/features/map/controls/MapControls.tsx +++ b/apps/fishing-map/features/map/controls/MapControls.tsx @@ -31,7 +31,7 @@ import { ROOT_DOM_ELEMENT } from 'data/config' import { useMapBounds } from 'features/map/map-bounds.hooks' import { selectIsGFWUser } from 'features/user/selectors/user.selectors' import { useMapErrorNotification } from 'features/map/overlays/error-notification/error-notification.hooks' -import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { useRootElement } from 'hooks/dom.hooks' import { isPrintSupported, MAP_IMAGE_DEBOUNCE } from '../MapScreenshot' import styles from './MapControls.module.css' diff --git a/apps/fishing-map/features/map/map-layers.hooks.ts b/apps/fishing-map/features/map/map-layers.hooks.ts index 110638bb38..7bb720d828 100644 --- a/apps/fishing-map/features/map/map-layers.hooks.ts +++ b/apps/fishing-map/features/map/map-layers.hooks.ts @@ -25,9 +25,9 @@ import { } from 'routes/routes.selectors' import { selectActivityMergedDataviewId, - selectDataviewInstancesResolvedVisible, selectDetectionsMergedDataviewId, } from 'features/dataviews/selectors/dataviews.selectors' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectBivariateDataviews, selectActivityVisualizationMode, diff --git a/apps/fishing-map/features/map/popups/PopupByCategory.tsx b/apps/fishing-map/features/map/popups/PopupByCategory.tsx index 05c4a2f942..93e5b80512 100644 --- a/apps/fishing-map/features/map/popups/PopupByCategory.tsx +++ b/apps/fishing-map/features/map/popups/PopupByCategory.tsx @@ -21,7 +21,7 @@ import { POPUP_CATEGORY_ORDER } from 'data/config' import { AsyncReducerStatus } from 'utils/async-slice' import { useMapViewport } from 'features/map/map-viewport.hooks' import { getDatasetTitleByDataview } from 'features/datasets/datasets.utils' -import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import ComparisonRow from 'features/map/popups/categories/ComparisonRow' import WorkspacePointsTooltipSection from 'features/map/popups/categories/WorkspacePointsLayers' import DetectionsTooltipRow from 'features/map/popups/categories/DetectionsLayers' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx index 8bd2ffbe7a..6c443c4854 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx @@ -8,7 +8,7 @@ import { REPORT_ACTIVITY_GRAPH_BEFORE_AFTER, REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON, } from 'data/config' -import { selectActiveReportDataviews } from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' import { useSetReportTimeComparison } from 'features/reports/areas/reports-timecomparison.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' diff --git a/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx b/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx index 4edb23dbf6..803ceab8ad 100644 --- a/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx +++ b/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { DatasetTypes, DataviewType } from '@globalfishingwatch/api-types' import { getFourwingsInterval } from '@globalfishingwatch/deck-loaders' import { getAvailableIntervalsInDataviews } from '@globalfishingwatch/deck-layer-composer' -import { selectActiveReportDataviews } from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { useReportFeaturesLoading, useReportFilteredTimeSeries, diff --git a/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts b/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts index 0f317f5510..d660addc0d 100644 --- a/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts +++ b/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts @@ -18,10 +18,8 @@ import { FourwingsInterval, FourwingsStaticFeature, } from '@globalfishingwatch/deck-loaders' -import { - selectActiveReportDataviews, - selectReportCategory, -} from 'features/app/selectors/app.reports.selector' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { FilteredPolygons } from 'features/reports/areas/reports-geo.utils' import { FeaturesToTimeseriesParams, diff --git a/apps/fishing-map/features/reports/areas/reports.selectors.ts b/apps/fishing-map/features/reports/areas/reports.selectors.ts index af61f1e1f5..d53a181f15 100644 --- a/apps/fishing-map/features/reports/areas/reports.selectors.ts +++ b/apps/fishing-map/features/reports/areas/reports.selectors.ts @@ -8,12 +8,11 @@ import { getGeometryDissolved, wrapGeometryBbox } from '@globalfishingwatch/data import { selectReportAreaId, selectReportDatasetId, - selectActiveReportDataviews, selectReportBufferOperation, selectReportBufferUnit, selectReportBufferValue, - selectReportCategory, selectReportVesselGraph, + selectReportCategory, } from 'features/app/selectors/app.reports.selector' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { @@ -37,7 +36,8 @@ import { MAX_CATEGORIES, OTHERS_CATEGORY_LABEL, } from 'features/reports/areas/reports.config' -import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportVesselsData, selectReportPreviewBuffer } from './report.slice' import { selectReportVesselFilter, diff --git a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx index de5626398c..51c312672e 100644 --- a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx @@ -6,10 +6,7 @@ import parse from 'html-react-parser' import Sticky from 'react-sticky-el' import { Locale } from '@globalfishingwatch/api-types' import { formatI18nDate } from 'features/i18n/i18nDate' -import { - selectActiveReportDataviews, - selectReportCategory, -} from 'features/app/selectors/app.reports.selector' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/reports/areas/summary/ReportSummaryTags' import { FIELDS, getCommonProperties } from 'features/reports/areas/reports.utils' import { ReportActivityUnit } from 'features/reports/areas/Report' @@ -31,6 +28,7 @@ import { } from 'features/reports/areas/report.slice' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { useTimeCompareTimeDescription } from 'features/reports/areas/reports-timecomparison.hooks' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportVesselsHours, selectReportVesselsNumber } from '../reports.selectors' import { selectReportTimeComparison } from '../reports.config.selectors' import { ReportCategory } from '../reports.types' diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx b/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx index 711cf4be22..d0d760d7f6 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx +++ b/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx @@ -2,10 +2,8 @@ import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import ReportVesselsGraphSelector from 'features/reports/areas/vessels/ReportVesselsGraphSelector' -import { - selectActiveReportDataviews, - selectReportCategory, -} from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/reports/areas/summary/ReportSummaryTags' import { FIELDS, getCommonProperties } from 'features/reports/areas/reports.utils' import { PROPERTIES_EXCLUDED } from 'features/reports/areas/summary/ReportSummary' diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx b/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx index 4826d3a313..5821c195ca 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx +++ b/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx @@ -8,11 +8,9 @@ import { REPORT_VESSELS_GRAPH_FLAG, REPORT_VESSELS_GRAPH_VESSELTYPE, } from 'data/config' -import { - selectReportCategory, - selectReportVesselGraph, -} from 'features/app/selectors/app.reports.selector' +import { selectReportVesselGraph } from 'features/app/selectors/app.reports.selector' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { ReportCategory, ReportVesselGraph } from '../reports.types' export default function ReportVesselsGraphSelector() { diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx b/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx index 6e80651380..49cc1c3f54 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx +++ b/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx @@ -7,10 +7,8 @@ import I18nNumber from 'features/i18n/i18nNumber' import { useLocationConnect } from 'routes/routes.hook' import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' import ReportVesselsTableFooter from 'features/reports/areas/vessels/ReportVesselsTableFooter' -import { - selectActiveReportDataviews, - selectReportCategory, -} from 'features/app/selectors/app.reports.selector' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectUserData } from 'features/user/selectors/user.selectors' import DatasetLabel from 'features/datasets/DatasetLabel' import { EMPTY_API_VALUES } from 'features/reports/areas/reports.config' diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts index 9854dd6fca..c0b1a2ed19 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts @@ -39,6 +39,37 @@ export const DATAVIEW_ID_BY_VESSEL_GROUP_EVENTS: Record< gaps: VESSEL_GROUP_GAPS_EVENTS_ID, } +type GetReportVesselGroupVisibleDataviewsParams = { + dataviews: UrlDataviewInstance[] + reportVesselGroupId: string + vesselGroupReportSection: VGRSection + vesselGroupReportSubSection?: VGRSubsection +} +export function getReportVesselGroupVisibleDataviews({ + dataviews, + reportVesselGroupId, + vesselGroupReportSection, + vesselGroupReportSubSection, +}: GetReportVesselGroupVisibleDataviewsParams) { + return dataviews.filter(({ id, category, config }) => { + if (REPORT_ONLY_VISIBLE_LAYERS.includes(config?.type as DataviewType)) { + return config?.visible + } + if (vesselGroupReportSection === 'events') { + const dataviewIdBySubSection = + DATAVIEW_ID_BY_VESSEL_GROUP_EVENTS[vesselGroupReportSubSection as VGREventsSubsection] + return id.toString() === dataviewIdBySubSection + } + if (vesselGroupReportSection === 'activity') { + return id.toString() === VESSEL_GROUP_ACTIVITY_ID + } + return ( + category === DataviewCategory.VesselGroups && + config?.filters?.['vessel-groups'].includes(reportVesselGroupId) + ) + }) +} + export const getVesselGroupDataviewInstance = ( vesselGroupId: string ): DataviewInstance | undefined => { diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts index 15884a60dc..2e502c6641 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts @@ -6,17 +6,15 @@ import { } from 'queries/vessel-insight-api' import { RootState } from 'reducers' import { InsightType } from '@globalfishingwatch/api-types' -import { selectActiveDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectReportVesselGroupId } from 'routes/routes.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' -import { VGRSubsection } from 'features/vessel-groups/vessel-groups.types' import { dataviewHasVesselGroupId } from 'features/dataviews/dataviews.utils' import { selectEventsDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' import { selectVGRActivitySubsection, selectVGREventsSubsection, - selectVGRSection, - selectVGRVesselsSubsection, + selectVGRSubsection, } from './vessel-group.config.selectors' import { VESSEL_GROUP_EVENTS_DATAVIEW_IDS, @@ -31,32 +29,12 @@ export const FLAG_CHANGE_INSIGHT_ID = 'VESSEL-IDENTITY-FLAG-CHANGES' as InsightT export const MOU_INSIGHT_ID = 'VESSEL-IDENTITY-MOU-LIST' as InsightType export const selectVGRDataview = createSelector( - [selectActiveDataviewInstancesResolved, selectReportVesselGroupId], + [selectDataviewInstancesResolvedVisible, selectReportVesselGroupId], (dataviews, reportVesselGroupId) => { return dataviews?.find((dataview) => dataviewHasVesselGroupId(dataview, reportVesselGroupId)) } ) -export const selectVGRSubsection = createSelector( - [ - selectVGRSection, - selectVGRVesselsSubsection, - selectVGRActivitySubsection, - selectVGREventsSubsection, - ], - (section, vesselsSubsection, activitySubsection, eventsSubsection): VGRSubsection | undefined => { - if (section === 'activity') { - return activitySubsection - } - if (section === 'events') { - return eventsSubsection - } - if (section === 'vessels') { - return vesselsSubsection - } - } -) - export const selectVGREventsSubsectionDataview = createSelector( [ selectEventsDataviews, diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group.config.selectors.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group.config.selectors.ts index 7e469de0ff..e82f43a117 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group.config.selectors.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group.config.selectors.ts @@ -3,6 +3,7 @@ import { selectLocationQuery } from 'routes/routes.selectors' import { VesselGroupReportState, VesselGroupReportStateProperty, + VGRSubsection, } from 'features/vessel-groups/vessel-groups.types' import { DEFAULT_VESSEL_GROUP_REPORT_STATE } from './vessel-group-report.config' @@ -32,3 +33,23 @@ export const selectVGREventsVesselsProperty = selectVGRStateProperty('vGREventsV export const selectVGREventsVesselFilter = selectVGRStateProperty('vGREventsVesselFilter') export const selectVGREventsVesselPage = selectVGRStateProperty('vGREventsVesselPage') export const selectVGREventsResultsPerPage = selectVGRStateProperty('vGREventsResultsPerPage') + +export const selectVGRSubsection = createSelector( + [ + selectVGRSection, + selectVGRVesselsSubsection, + selectVGRActivitySubsection, + selectVGREventsSubsection, + ], + (section, vesselsSubsection, activitySubsection, eventsSubsection): VGRSubsection | undefined => { + if (section === 'activity') { + return activitySubsection + } + if (section === 'events') { + return eventsSubsection + } + if (section === 'vessels') { + return vesselsSubsection + } + } +) diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx index 479f61dbec..6de396764b 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx @@ -6,7 +6,7 @@ import { IconButton } from '@globalfishingwatch/ui-components' import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info' import { useLocationConnect } from 'routes/routes.hook' import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' -import { selectActiveReportDataviews } from 'features/app/selectors/app.reports.selector' +import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectUserData } from 'features/user/selectors/user.selectors' import DatasetLabel from 'features/datasets/DatasetLabel' import { EMPTY_API_VALUES } from 'features/reports/areas/reports.config' diff --git a/apps/fishing-map/features/resources/resources.hooks.ts b/apps/fishing-map/features/resources/resources.hooks.ts index fef2821006..863ebca074 100644 --- a/apps/fishing-map/features/resources/resources.hooks.ts +++ b/apps/fishing-map/features/resources/resources.hooks.ts @@ -3,7 +3,7 @@ import { useSelector } from 'react-redux' import { Resource } from '@globalfishingwatch/api-types' import { useAppDispatch } from 'features/app/app.hooks' import { fetchResourceThunk } from 'features/resources/resources.slice' -import { selectDataviewsResources } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewsResources } from 'features/dataviews/selectors/dataviews.resolvers.selectors' const useFetchResources = (resources: Resource[]) => { const dispatch = useAppDispatch() diff --git a/apps/fishing-map/features/sidebar/Sidebar.tsx b/apps/fishing-map/features/sidebar/Sidebar.tsx index 582d7c484d..13555de2e7 100644 --- a/apps/fishing-map/features/sidebar/Sidebar.tsx +++ b/apps/fishing-map/features/sidebar/Sidebar.tsx @@ -20,7 +20,7 @@ import { selectIsUserLogged } from 'features/user/selectors/user.selectors' import { fetchVesselGroupsThunk } from 'features/vessel-groups/vessel-groups.slice' import { fetchResourceThunk } from 'features/resources/resources.slice' import { useAppDispatch } from 'features/app/app.hooks' -import { selectDataviewsResources } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewsResources } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import styles from './Sidebar.module.css' import CategoryTabs from './CategoryTabs' import SidebarHeader from './SidebarHeader' diff --git a/apps/fishing-map/features/timebar/TimebarActivityGraph.tsx b/apps/fishing-map/features/timebar/TimebarActivityGraph.tsx index 2022074219..ab766671be 100644 --- a/apps/fishing-map/features/timebar/TimebarActivityGraph.tsx +++ b/apps/fishing-map/features/timebar/TimebarActivityGraph.tsx @@ -6,7 +6,7 @@ import { HighlighterCallbackFn, HighlighterCallbackFnArgs, } from '@globalfishingwatch/timebar' -import { selectActiveActivityDataviewsByVisualisation } from 'features/dataviews/selectors/dataviews.selectors' +import { selectActiveActivityDataviewsByVisualisation } from 'features/timebar/timebar.selectors' import { useHeatmapActivityGraph } from 'features/timebar/TimebarActivityGraph.hooks' import { formatNumber } from 'utils/info' import { TimebarVisualisations } from 'types' diff --git a/apps/fishing-map/features/timebar/timebar.selectors.ts b/apps/fishing-map/features/timebar/timebar.selectors.ts index 0ee04e127d..dd75111867 100644 --- a/apps/fishing-map/features/timebar/timebar.selectors.ts +++ b/apps/fishing-map/features/timebar/timebar.selectors.ts @@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit' import { getDatasetsExtent } from '@globalfishingwatch/datasets-client' import { DataviewCategory } from '@globalfishingwatch/api-types' import { TimebarVisualisations } from 'types' -import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectTimebarSelectedEnvId, selectTimebarSelectedVGId, @@ -15,15 +15,58 @@ import { selectActiveActivityDataviews, selectActiveDetectionsDataviews, selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, + selectActiveReportActivityDataviews, selectActiveVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.selectors' import { selectActivityVisualizationMode, selectDetectionsVisualizationMode, } from 'features/app/selectors/app.selectors' -import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' import { selectIsAnyReportLocation } from 'routes/routes.selectors' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' + +export const selectActiveActivityDataviewsByVisualisation = ( + timebarVisualisation: TimebarVisualisations +) => + createSelector( + [ + selectActiveReportActivityDataviews, + selectActiveDetectionsDataviews, + selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, + selectActiveVesselGroupDataviews, + selectTimebarSelectedEnvId, + selectTimebarSelectedVGId, + ], + ( + activityDataviews, + detectionsDataviews, + environmentDataviews, + vesselGroupDataviews, + timebarSelectedEnvId, + timebarSelectedVGId + ) => { + if (timebarVisualisation === TimebarVisualisations.HeatmapActivity) { + return activityDataviews + } + if (timebarVisualisation === TimebarVisualisations.HeatmapDetections) { + return detectionsDataviews + } + if (timebarVisualisation === TimebarVisualisations.VesselGroup) { + const selectedVGDataview = + timebarSelectedVGId && vesselGroupDataviews.find((d) => d.id === timebarSelectedVGId) + + if (selectedVGDataview) return [selectedVGDataview] + else if (vesselGroupDataviews[0]) return [vesselGroupDataviews[0]] + } + // timebarVisualisation === TimebarVisualisations.Environment + const selectedEnvDataview = + timebarSelectedEnvId && environmentDataviews.find((d) => d.id === timebarSelectedEnvId) + + if (selectedEnvDataview) return [selectedEnvDataview] + else if (environmentDataviews[0]) return [environmentDataviews[0]] + } + ) const selectDatasetsExtent = createSelector( [selectDataviewInstancesResolved, selectAllDatasets], diff --git a/apps/fishing-map/features/workspace/Workspace.tsx b/apps/fishing-map/features/workspace/Workspace.tsx index 9178ab9198..9e4f8a66a2 100644 --- a/apps/fishing-map/features/workspace/Workspace.tsx +++ b/apps/fishing-map/features/workspace/Workspace.tsx @@ -31,7 +31,7 @@ import WorkspaceError, { WorkspacePassword } from 'features/workspace/WorkspaceE import { getWorkspaceLabel, isPrivateWorkspaceNotAllowed } from 'features/workspace/workspace.utils' import { setWorkspaceProperty } from 'features/workspace/workspace.slice' import UserSection from 'features/workspace/user/UserSection' -import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesMergedOrdered } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import ActivitySection from './activity/ActivitySection' import VesselsSection from './vessels/VesselsSection' import VesselGroupSection from './vessel-groups/VesselGroupsSection' diff --git a/apps/fishing-map/features/workspace/legacy-activity-category.hook.ts b/apps/fishing-map/features/workspace/legacy-activity-category.hook.ts index 39fa5fcdea..71a77356a4 100644 --- a/apps/fishing-map/features/workspace/legacy-activity-category.hook.ts +++ b/apps/fishing-map/features/workspace/legacy-activity-category.hook.ts @@ -9,7 +9,7 @@ import { VIIRS_MATCH_DATAVIEW_SLUG, } from 'data/workspaces' import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook' -import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectAllDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' export const useHideLegacyActivityCategoryDataviews = () => { const actionDone = useRef(false) diff --git a/apps/fishing-map/features/workspace/workspace.hook.ts b/apps/fishing-map/features/workspace/workspace.hook.ts index e745896b1e..f891dc34da 100644 --- a/apps/fishing-map/features/workspace/workspace.hook.ts +++ b/apps/fishing-map/features/workspace/workspace.hook.ts @@ -14,7 +14,7 @@ import { selectUrlViewport, } from 'routes/routes.selectors' import { useLocationConnect } from 'routes/routes.hook' -import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { useSetMapCoordinates } from 'features/map/map-viewport.hooks' import { useTimerangeConnect } from 'features/timebar/timebar.hooks' import { selectWorkspaceDataviewInstances } from './workspace.selectors' From 79eec4fc8d933f1e6bc1d3235bdc0913cd07da7c Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 09:12:36 +0200 Subject: [PATCH 02/15] centralize activeDataviews selectors --- .../app/selectors/app.timebar.selectors.ts | 2 +- .../dataviews.categories.selectors.ts | 35 +++++++++++++++ .../dataviews.instances.selectors.ts | 5 --- .../selectors/dataviews.selectors.ts | 44 +++---------------- .../features/timebar/TimebarSettings.tsx | 10 ++--- .../features/timebar/timebar.hooks.ts | 2 +- .../features/timebar/timebar.selectors.ts | 8 ++-- .../workspace/vessels/VesselEventsLegend.tsx | 2 +- .../vessels/VesselsFromPositions.tsx | 8 ++-- .../workspace/vessels/VesselsSection.tsx | 6 +-- 10 files changed, 60 insertions(+), 62 deletions(-) diff --git a/apps/fishing-map/features/app/selectors/app.timebar.selectors.ts b/apps/fishing-map/features/app/selectors/app.timebar.selectors.ts index 89bea65640..def4d288a9 100644 --- a/apps/fishing-map/features/app/selectors/app.timebar.selectors.ts +++ b/apps/fishing-map/features/app/selectors/app.timebar.selectors.ts @@ -1,10 +1,10 @@ import { createSelector } from '@reduxjs/toolkit' import { DEFAULT_TIME_RANGE } from 'data/config' import { + selectActiveVesselsDataviews, selectEnvironmentalDataviews, selectVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.categories.selectors' -import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { TimeRange } from 'features/timebar/timebar.slice' import { selectWorkspaceStateProperty, diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts index 2e49735555..005cf4b8b3 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.categories.selectors.ts @@ -1,9 +1,19 @@ import { createSelector } from '@reduxjs/toolkit' import { DataviewCategory, DataviewType } from '@globalfishingwatch/api-types' import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' +import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectDataviewInstancesResolvedVisible } from './dataviews.instances.selectors' export const selectDataviewInstancesByCategory = (category?: DataviewCategory) => { + return createSelector( + [selectDataviewInstancesResolved], + (dataviews): UrlDataviewInstance[] => { + return dataviews?.filter((dataview) => dataview.category === category) + } + ) +} + +export const selectActiveDataviewInstancesByCategory = (category?: DataviewCategory) => { return createSelector( [selectDataviewInstancesResolvedVisible], (dataviews): UrlDataviewInstance[] => { @@ -15,20 +25,45 @@ export const selectDataviewInstancesByCategory = (category?: DataviewCategory) = export const selectEnvironmentalDataviews = selectDataviewInstancesByCategory( DataviewCategory.Environment ) +export const selectActiveEnvironmentalDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.Environment +) + export const selectEventsDataviews = selectDataviewInstancesByCategory(DataviewCategory.Events) +export const selectActiveEventsDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.Events +) export const selectActivityDataviews = selectDataviewInstancesByCategory(DataviewCategory.Activity) +export const selectActiveActivityDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.Activity +) + +export const selectVesselsDataviews = selectDataviewInstancesByCategory(DataviewCategory.Vessels) +export const selectActiveVesselsDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.Vessels +) export const selectDetectionsDataviews = selectDataviewInstancesByCategory( DataviewCategory.Detections ) +export const selectActiveDetectionsDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.Detections +) export const selectVesselGroupDataviews = selectDataviewInstancesByCategory( DataviewCategory.VesselGroups ) +export const selectActiveVesselGroupDataviews = selectActiveDataviewInstancesByCategory( + DataviewCategory.VesselGroups +) export const selectContextAreasDataviews = selectDataviewInstancesByCategory( DataviewCategory.Context ) +export const selectActiveContextAreasDataviews = selectDataviewInstancesByCategory( + DataviewCategory.Context +) + export const selectCustomUserDataviews = selectDataviewInstancesByCategory(DataviewCategory.User) 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 fb66ef9fab..de5dfef0b4 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts @@ -122,11 +122,6 @@ export const selectVesselsDataviews = createSelector([selectTrackDataviews], (da ) }) -export const selectActiveVesselsDataviews = createDeepEqualSelector( - [selectVesselsDataviews], - (dataviews) => dataviews?.filter((d) => d.config?.visible) -) - export const selectVesselProfileDataview = createDeepEqualSelector( [selectVesselsDataviews, selectVesselId], (dataviews, vesselId) => dataviews.find(({ id }) => vesselId && id.includes(vesselId)) diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts index 018ef00ef0..7976cee105 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts @@ -17,7 +17,6 @@ import { selectIsVesselGroupReportLocation, selectReportVesselGroupId, } from 'routes/routes.selectors' -import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { isBathymetryDataview } from 'features/dataviews/dataviews.utils' import { selectDownloadActiveTabId } from 'features/download/downloadActivity.slice' import { HeatmapDownloadTab } from 'features/download/downloadActivity.config' @@ -29,12 +28,12 @@ import { getReportVesselGroupVisibleDataviews } from 'features/reports/vessel-gr import { ReportCategory } from 'features/reports/areas/reports.types' import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' import { - selectContextAreasDataviews, - selectActivityDataviews, - selectDetectionsDataviews, - selectEnvironmentalDataviews, - selectEventsDataviews, - selectVesselGroupDataviews, + selectActiveActivityDataviews, + selectActiveContextAreasDataviews, + selectActiveEventsDataviews, + selectActiveVesselsDataviews, + selectActiveEnvironmentalDataviews, + selectActiveDetectionsDataviews, } from 'features/dataviews/selectors/dataviews.categories.selectors' import { selectDataviewInstancesResolved, @@ -73,18 +72,6 @@ export const selectBasemapLabelsDataviewInstance = createSelector( } ) -const selectActiveContextAreasDataviews = createSelector( - [selectContextAreasDataviews], - (dataviews) => dataviews?.filter((d) => d.config?.visible) -) - -export const selectActiveActivityDataviews = createSelector( - [selectActivityDataviews], - (dataviews): UrlDataviewInstance[] => { - return dataviews?.filter((d) => d.config?.visible) - } -) - export const selectActivityMergedDataviewId = createSelector( [selectActiveActivityDataviews], (dataviews): string => { @@ -104,16 +91,6 @@ export const selectActiveReportActivityDataviews = createSelector( } ) -export const selectActiveDetectionsDataviews = createSelector( - [selectDetectionsDataviews], - (dataviews): UrlDataviewInstance[] => dataviews?.filter((d) => d.config?.visible) -) - -export const selectActiveVesselGroupDataviews = createSelector( - [selectVesselGroupDataviews], - (dataviews): UrlDataviewInstance[] => dataviews?.filter((d) => d.config?.visible) -) - export const selectDetectionsMergedDataviewId = createSelector( [selectActiveDetectionsDataviews], (dataviews): string => { @@ -129,11 +106,6 @@ export const selectActiveActivityAndDetectionsDataviews = createSelector( ] ) -export const selectActiveEnvironmentalDataviews = createSelector( - [selectEnvironmentalDataviews], - (dataviews): UrlDataviewInstance[] => dataviews?.filter((d) => d.config?.visible) -) - export const selectActiveHeatmapEnvironmentalDataviews = createSelector( [selectActiveEnvironmentalDataviews], (dataviews) => { @@ -240,10 +212,6 @@ export const selectActiveTemporalgridDataviews: ( } ) -const selectActiveEventsDataviews = createSelector([selectEventsDataviews], (dataviews) => - dataviews?.filter((d) => d.config?.visible) -) - export const selectHasReportLayersVisible = createSelector( [selectActiveHeatmapDowloadDataviews], (reportDataviews) => { diff --git a/apps/fishing-map/features/timebar/TimebarSettings.tsx b/apps/fishing-map/features/timebar/TimebarSettings.tsx index 5c32999fe3..046b49dcee 100644 --- a/apps/fishing-map/features/timebar/TimebarSettings.tsx +++ b/apps/fishing-map/features/timebar/TimebarSettings.tsx @@ -10,9 +10,7 @@ import useClickedOutside from 'hooks/use-clicked-outside' import { TimebarGraphs, TimebarVisualisations } from 'types' import { selectActiveReportActivityDataviews, - selectActiveDetectionsDataviews, selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, - selectActiveVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.selectors' import { getEventLabel } from 'utils/analytics' import { ReactComponent as AreaIcon } from 'assets/icons/timebar-area.svg' @@ -22,11 +20,13 @@ import { ReactComponent as TrackDepthIcon } from 'assets/icons/timebar-track-dep import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectIsVesselLocation } from 'routes/routes.selectors' +import { selectActiveTrackDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { formatInfoField } from 'utils/info' import { - selectActiveTrackDataviews, + selectActiveDetectionsDataviews, + selectActiveVesselGroupDataviews, selectActiveVesselsDataviews, -} from 'features/dataviews/selectors/dataviews.instances.selectors' -import { formatInfoField } from 'utils/info' +} from 'features/dataviews/selectors/dataviews.categories.selectors' import { useTimebarVisualisationConnect, useTimebarGraphConnect, diff --git a/apps/fishing-map/features/timebar/timebar.hooks.ts b/apps/fishing-map/features/timebar/timebar.hooks.ts index 467c6a5a55..3bc0575806 100644 --- a/apps/fishing-map/features/timebar/timebar.hooks.ts +++ b/apps/fishing-map/features/timebar/timebar.hooks.ts @@ -14,7 +14,6 @@ import { import { useLocationConnect } from 'routes/routes.hook' import { selectActiveReportActivityDataviews, - selectActiveDetectionsDataviews, selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, selectActiveVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.selectors' @@ -26,6 +25,7 @@ import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' import { DEFAULT_TIME_RANGE } from 'data/config' import { selectActiveTrackDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsWorkspaceMapReady } from 'features/workspace/workspace.selectors' +import { selectActiveDetectionsDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' import { changeSettings, setHighlightedEvents, diff --git a/apps/fishing-map/features/timebar/timebar.selectors.ts b/apps/fishing-map/features/timebar/timebar.selectors.ts index dd75111867..5e80400504 100644 --- a/apps/fishing-map/features/timebar/timebar.selectors.ts +++ b/apps/fishing-map/features/timebar/timebar.selectors.ts @@ -12,11 +12,8 @@ import { AVAILABLE_END, AVAILABLE_START } from 'data/config' import { getDatasetsInDataviews } from 'features/datasets/datasets.utils' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { - selectActiveActivityDataviews, - selectActiveDetectionsDataviews, selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, selectActiveReportActivityDataviews, - selectActiveVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.selectors' import { selectActivityVisualizationMode, @@ -25,6 +22,11 @@ import { import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' import { selectIsAnyReportLocation } from 'routes/routes.selectors' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' +import { + selectActiveActivityDataviews, + selectActiveDetectionsDataviews, + selectActiveVesselGroupDataviews, +} from 'features/dataviews/selectors/dataviews.categories.selectors' export const selectActiveActivityDataviewsByVisualisation = ( timebarVisualisation: TimebarVisualisations diff --git a/apps/fishing-map/features/workspace/vessels/VesselEventsLegend.tsx b/apps/fishing-map/features/workspace/vessels/VesselEventsLegend.tsx index 83be415cf8..05bfe5a758 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselEventsLegend.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselEventsLegend.tsx @@ -12,7 +12,7 @@ import styles from 'features/workspace/shared/Sections.module.css' import { getEventsDatasetsInDataview } from 'features/datasets/datasets.utils' import { upperFirst } from 'utils/info' import { useVisibleVesselEvents } from 'features/workspace/vessels/vessel-events.hooks' -import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' import EncounterIcon from '../../../assets/icons/event-encounter.svg' import LoiteringIcon from '../../../assets/icons/event-loitering.svg' import PortIcon from '../../../assets/icons/event-port.svg' diff --git a/apps/fishing-map/features/workspace/vessels/VesselsFromPositions.tsx b/apps/fishing-map/features/workspace/vessels/VesselsFromPositions.tsx index 75bcf823d9..0ac032c4ab 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsFromPositions.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsFromPositions.tsx @@ -9,10 +9,6 @@ import { FourwingsLayer } from '@globalfishingwatch/deck-layers' import { FourwingsPositionFeature } from '@globalfishingwatch/deck-loaders' import { Collapsable } from '@globalfishingwatch/ui-components' import { DatasetTypes } from '@globalfishingwatch/api-types' -import { - selectActiveActivityDataviews, - selectActiveDetectionsDataviews, -} from 'features/dataviews/selectors/dataviews.selectors' import styles from 'features/workspace/shared/Sections.module.css' import VesselPin from 'features/vessel/VesselPin' import { formatInfoField } from 'utils/info' @@ -21,6 +17,10 @@ import { VESSEL_LAYER_PREFIX } from 'features/dataviews/dataviews.utils' import I18nNumber from 'features/i18n/i18nNumber' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { getRelatedDatasetByType } from 'features/datasets/datasets.utils' +import { + selectActiveActivityDataviews, + selectActiveDetectionsDataviews, +} from 'features/dataviews/selectors/dataviews.categories.selectors' const MAX_VESSLES_TO_DISPLAY = 10 diff --git a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx index 7813d2acd1..a7df1e4709 100644 --- a/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx +++ b/apps/fishing-map/features/workspace/vessels/VesselsSection.tsx @@ -15,10 +15,7 @@ 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 { - selectActiveVesselsDataviews, - selectVesselsDataviews, -} from 'features/dataviews/selectors/dataviews.instances.selectors' +import { selectVesselsDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsGuestUser } from 'features/user/selectors/user.selectors' import { hasTracksWithNoData, @@ -39,6 +36,7 @@ import { import { AsyncReducerStatus } from 'utils/async-slice' import { useAppDispatch } from 'features/app/app.hooks' import { getVesselGroupDataviewInstance } from 'features/reports/vessel-groups/vessel-group-report.dataviews' +import { selectActiveVesselsDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' import VesselEventsLegend from './VesselEventsLegend' import VesselLayerPanel from './VesselLayerPanel' import VesselsFromPositions from './VesselsFromPositions' From 86f7892ac91717c70550032dbcc78559eb234142 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 10:42:05 +0200 Subject: [PATCH 03/15] activity for vessel group reports --- apps/fishing-map/features/app/App.tsx | 3 +- .../reports/activity/ReportActivity.tsx | 28 ++++++++++++- .../features/reports/areas/Report.tsx | 31 ++++----------- .../reports/areas/reports-timeseries.hooks.ts | 27 ++++++------- .../features/reports/areas/reports.config.ts | 22 +++++++++++ .../features/reports/areas/reports.hooks.ts | 14 ++++++- .../reports/areas/reports.selectors.ts | 21 +++++++--- .../vessel-groups/VesselGroupReport.tsx | 11 ++++-- .../vessel-group-report.dataviews.ts | 39 ++++++++++++++++++- .../vessels/VesselGroupReportVesselsGraph.tsx | 2 +- 10 files changed, 146 insertions(+), 52 deletions(-) diff --git a/apps/fishing-map/features/app/App.tsx b/apps/fishing-map/features/app/App.tsx index aa79ec7596..633e899679 100644 --- a/apps/fishing-map/features/app/App.tsx +++ b/apps/fishing-map/features/app/App.tsx @@ -137,6 +137,7 @@ function App() { const isReportLocation = useSelector(selectIsAnyReportLocation) const reportAreaBounds = useSelector(selectReportAreaBounds) const isAnySearchLocation = useSelector(selectIsAnySearchLocation) + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) const onMenuClick = useCallback(() => { setMenuOpen(true) @@ -177,7 +178,7 @@ function App() { const resolvedAction = await action if (fetchWorkspaceThunk.fulfilled.match(resolvedAction)) { const workspace = resolvedAction.payload as Workspace - if (!isWorkspacePasswordProtected(workspace)) { + if (!isVesselGroupReportLocation && !isWorkspacePasswordProtected(workspace)) { fitWorkspaceBounds(workspace) } } diff --git a/apps/fishing-map/features/reports/activity/ReportActivity.tsx b/apps/fishing-map/features/reports/activity/ReportActivity.tsx index db0eb4bae3..277f084622 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivity.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivity.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, useMemo } from 'react' +import React, { Fragment, useEffect, useMemo } from 'react' import { useSelector } from 'react-redux' import { useTranslation } from 'react-i18next' import ReportActivityGraphSelector from 'features/reports/activity/ReportActivityGraphSelector' @@ -6,16 +6,22 @@ import { useTimerangeConnect } from 'features/timebar/timebar.hooks' import { getReportGraphMode, ReportGraphProps, + useComputeReportTimeSeries, useReportFeaturesLoading, useReportFilteredTimeSeries, } from 'features/reports/areas/reports-timeseries.hooks' -import { selectTimeComparisonValues } from 'features/reports/areas/reports.selectors' +import { + selectReportArea, + selectTimeComparisonValues, +} from 'features/reports/areas/reports.selectors' import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison' import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph' import UserGuideLink from 'features/help/UserGuideLink' +import { AsyncReducerStatus } from 'utils/async-slice' import { selectReportActivityGraph } from '../areas/reports.config.selectors' import { ReportActivityGraph } from '../areas/reports.types' +import { useFetchReportArea, useFitAreaInViewport } from '../areas/reports.hooks' import ReportActivityEvolution from './ReportActivityEvolution' import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' @@ -39,7 +45,25 @@ const GRAPH_BY_TYPE: Record | } const emptyGraphData = {} as ReportGraphProps + export default function ReportActivity() { + useComputeReportTimeSeries() + const reportArea = useSelector(selectReportArea) + const { status } = useFetchReportArea() + + const fitAreaInViewport = useFitAreaInViewport() + + // This ensures that the area is in viewport when then area load finishes + useEffect(() => { + if (status === AsyncReducerStatus.Finished && reportArea?.bounds) { + requestAnimationFrame(() => { + fitAreaInViewport() + }) + } + // Reacting only to the area status and fitting bounds after load + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [status, reportArea]) + const { t } = useTranslation() const { start, end } = useTimerangeConnect() const reportActivityGraph = useSelector(selectReportActivityGraph) diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/Report.tsx index aef5d74539..29788b8a60 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/Report.tsx @@ -15,7 +15,11 @@ import { ContextFeature } from '@globalfishingwatch/deck-layers' import { AsyncReducerStatus } from 'utils/async-slice' import { useLocationConnect } from 'routes/routes.hook' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' -import { selectActiveTemporalgridDataviews } from 'features/dataviews/selectors/dataviews.selectors' +import { + isActivityReport, + selectActiveReportDataviews, + selectActiveTemporalgridDataviews, +} from 'features/dataviews/selectors/dataviews.selectors' import WorkspaceError, { ErrorPlaceHolder, WorkspaceLoginError, @@ -47,17 +51,12 @@ import { } from 'features/reports/areas/report.slice' import { useAppDispatch } from 'features/app/app.hooks' import { - isActivityReport, - selectActiveReportDataviews, selectReportAreaId, selectReportCategory, selectReportDatasetId, } from 'features/app/selectors/app.reports.selector' import { formatI18nDate } from 'features/i18n/i18nDate' -import { - useComputeReportTimeSeries, - useSetTimeseries, -} from 'features/reports/areas/reports-timeseries.hooks' +import { useSetTimeseries } from 'features/reports/areas/reports-timeseries.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' import DatasetLabel from 'features/datasets/DatasetLabel' @@ -66,12 +65,7 @@ import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/ar import { selectIsGuestUser, selectUserData } from 'features/user/selectors/user.selectors' import { useFetchDataviewResources } from 'features/resources/resources.hooks' import ReportActivity from '../activity/ReportActivity' -import { - useFetchReportArea, - useFetchReportVessel, - useFitAreaInViewport, - useHighlightReportArea, -} from './reports.hooks' +import { useFetchReportVessel, useFitAreaInViewport, useHighlightReportArea } from './reports.hooks' import ReportSummary from './summary/ReportSummary' import ReportTitle from './title/ReportTitle' import ReportVessels from './vessels/ReportVessels' @@ -367,7 +361,6 @@ function ActivityReport({ reportName }: { reportName: string }) { } export default function Report() { - useComputeReportTimeSeries() const { t } = useTranslation() const dispatch = useAppDispatch() const setTimeseries = useSetTimeseries() @@ -406,7 +399,6 @@ export default function Report() { }) const workspaceStatus = useSelector(selectWorkspaceStatus) const reportAreaError = useSelector(selectReportAreaStatus) === AsyncReducerStatus.Error - const { status } = useFetchReportArea() const { dispatchTimebarVisualisation } = useTimebarVisualisationConnect() const { dispatchTimebarSelectedEnvId } = useTimebarEnvironmentConnect() const workspaceVesselGroupsStatus = useSelector(selectWorkspaceVesselGroupsStatus) @@ -415,15 +407,6 @@ export default function Report() { const fitAreaInViewport = useFitAreaInViewport() - // This ensures that the area is in viewport when then area load finishes - useEffect(() => { - if (status === AsyncReducerStatus.Finished && reportArea?.bounds) { - fitAreaInViewport() - } - // Reacting only to the area status and fitting bounds after load - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [status, reportArea]) - useEffect(() => { if (reportArea && !hasReportBuffer) { highlightArea(reportArea as ContextFeature) diff --git a/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts b/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts index d660addc0d..e4b539b0cf 100644 --- a/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts +++ b/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts @@ -33,11 +33,12 @@ import { selectShowTimeComparison, } from 'features/reports/areas/reports.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' -import { AreaGeometry } from 'features/areas/areas.slice' +import { Area, AreaGeometry } from 'features/areas/areas.slice' import { useFilterCellsByPolygonWorker } from 'features/reports/areas/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' +import { ENTIRE_WORLD_REPORT_AREA_ID } from './reports.config' interface EvolutionGraphData { date: string @@ -158,15 +159,19 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { ]) const updateFeaturesFiltered = useCallback( - async (instances: FourwingsLayer[], polygon: AreaGeometry, mode?: 'point' | 'cell') => { + async (instances: FourwingsLayer[], area: Area, mode?: 'point' | 'cell') => { setFeaturesFiltered([]) + for (const instance of instances) { const features = instance.getData() as FourwingsFeature[] - const filteredInstanceFeatures = await filterCellsByPolygon({ - layersCells: [features], - polygon, - mode, - }) + const filteredInstanceFeatures = + area.id === ENTIRE_WORLD_REPORT_AREA_ID + ? ([{ contained: features, overlapping: [] }] as FilteredPolygons[]) + : await filterCellsByPolygon({ + layersCells: [features], + polygon: area.geometry!, + mode, + }) setFeaturesFiltered((prev) => [...prev, filteredInstanceFeatures]) } }, @@ -175,15 +180,11 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { useEffect(() => { if (area?.geometry && layersLoaded && featuresFilteredDirtyRef.current && instances.length) { - updateFeaturesFiltered( - instances, - area.geometry, - reportCategory === 'environment' ? 'point' : 'cell' - ) + updateFeaturesFiltered(instances, area, reportCategory === 'environment' ? 'point' : 'cell') featuresFilteredDirtyRef.current = false } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [area?.geometry, reportCategory, layersLoaded, reportBufferHash]) + }, [area, reportCategory, layersLoaded, reportBufferHash]) const computeTimeseries = useCallback( ( diff --git a/apps/fishing-map/features/reports/areas/reports.config.ts b/apps/fishing-map/features/reports/areas/reports.config.ts index 8d188c3f81..59e4e47085 100644 --- a/apps/fishing-map/features/reports/areas/reports.config.ts +++ b/apps/fishing-map/features/reports/areas/reports.config.ts @@ -4,6 +4,7 @@ import { REPORT_VESSELS_PER_PAGE, } from 'data/config' import { BufferUnit, BufferOperation } from 'types' +import { Area, AreaGeometry } from 'features/areas/areas.slice' import { AreaReportState } from './reports.types' export const REPORT_BUFFER_FEATURE_ID: string = 'buffer' @@ -40,3 +41,24 @@ export const DEFAULT_AREA_REPORT_STATE: AreaReportState = { reportBufferUnit: undefined, reportBufferOperation: undefined, } + +export const ENTIRE_WORLD_REPORT_AREA_ID = 'world' +export const ENTIRE_WORLD_REPORT_AREA: Area = { + id: ENTIRE_WORLD_REPORT_AREA_ID, + name: 'Entire World', + type: 'Feature', + bounds: [-180, -90, 180, 90], + geometry: { + type: 'Polygon', + coordinates: [ + [ + [-180, -90], + [180, -90], + [180, 90], + [-180, 90], + [-180, -90], + ], + ], + }, + properties: {}, +} diff --git a/apps/fishing-map/features/reports/areas/reports.hooks.ts b/apps/fishing-map/features/reports/areas/reports.hooks.ts index 3c331ff311..393c77d49a 100644 --- a/apps/fishing-map/features/reports/areas/reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/reports.hooks.ts @@ -30,6 +30,8 @@ import { FIT_BOUNDS_REPORT_PADDING } from 'data/config' import { RFMO_DATAVIEW_SLUG } from 'data/workspaces' import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' +import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' +import { AsyncReducerStatus } from 'utils/async-slice' import { fetchReportVesselsThunk, getReportQuery, @@ -68,7 +70,11 @@ function useReportAreaCenter(bounds?: Bbox) { const { latitude, longitude, zoom } = getMapCoordinatesFromBounds(map, bounds, { padding: FIT_BOUNDS_REPORT_PADDING, }) - return { latitude, longitude, zoom } + return { + latitude: parseFloat(latitude.toFixed(8)), + longitude: parseFloat(longitude.toFixed(8)), + zoom: parseFloat(zoom.toFixed(8)), + } }, [bounds, map]) } @@ -109,6 +115,7 @@ function getSimplificationByDataview(dataview: UrlDataviewInstance | Dataview) { export function useFetchReportArea() { const dispatch = useAppDispatch() + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) const { datasetId, areaId } = useSelector(selectReportAreaIds) const status = useSelector(selectDatasetAreaStatus({ datasetId, areaId })) const data = useSelector(selectDatasetAreaDetail({ datasetId, areaId })) @@ -129,7 +136,10 @@ export function useFetchReportArea() { } }, [areaId, datasetId, dispatch, areaDataviews]) - return useMemo(() => ({ status, data }), [status, data]) + return useMemo( + () => ({ status: isVesselGroupReportLocation ? AsyncReducerStatus.Finished : status, data }), + [isVesselGroupReportLocation, status, data] + ) } export function useFetchReportVessel() { diff --git a/apps/fishing-map/features/reports/areas/reports.selectors.ts b/apps/fishing-map/features/reports/areas/reports.selectors.ts index d53a181f15..aeb6b00f6e 100644 --- a/apps/fishing-map/features/reports/areas/reports.selectors.ts +++ b/apps/fishing-map/features/reports/areas/reports.selectors.ts @@ -33,11 +33,13 @@ import { sortStrings } from 'utils/shared' import { Area, AreaGeometry, selectAreas } from 'features/areas/areas.slice' import { EMPTY_API_VALUES, + ENTIRE_WORLD_REPORT_AREA, MAX_CATEGORIES, OTHERS_CATEGORY_LABEL, } from 'features/reports/areas/reports.config' import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' +import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' import { selectReportVesselsData, selectReportPreviewBuffer } from './report.slice' import { selectReportVesselFilter, @@ -506,10 +508,19 @@ export const selectHasReportBuffer = createSelector( ) export const selectReportArea = createSelector( - [selectReportAreaData, selectHasReportBuffer, selectReportBufferArea], - (area, hasReportBuffer, bufferedArea) => { - if (!area) return null - if (!hasReportBuffer) return area - return bufferedArea + [ + selectReportAreaData, + selectHasReportBuffer, + selectReportBufferArea, + selectIsVesselGroupReportLocation, + ], + (area, hasReportBuffer, bufferedArea, isVesselGroupReportLocation) => { + if (isVesselGroupReportLocation) { + return ENTIRE_WORLD_REPORT_AREA + } + if (hasReportBuffer) { + return bufferedArea + } + return area } ) diff --git a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx index 384e830c29..a83bb79e6b 100644 --- a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx @@ -13,6 +13,8 @@ import { useTimebarVisualisationConnect, } from 'features/timebar/timebar.hooks' import VGREvents from 'features/reports/events/VGREvents' +import ReportActivity from 'features/reports/activity/ReportActivity' +import { useFitAreaInViewport } from '../areas/reports.hooks' import { useFetchVesselGroupReport } from './vessel-group-report.hooks' import { selectVGRData, selectVGRStatus } from './vessel-group-report.slice' import VesselGroupReportTitle from './VesselGroupReportTitle' @@ -32,6 +34,7 @@ function VesselGroupReport() { const reportDataview = useSelector(selectVGRDataview) const { dispatchTimebarVisualisation } = useTimebarVisualisationConnect() const { dispatchTimebarSelectedVGId } = useTimebarVesselGroupConnect() + const fitAreaInViewport = useFitAreaInViewport() useEffect(() => { fetchVesselGroupReport(vesselGroupId) @@ -54,8 +57,11 @@ function VesselGroupReport() { category: TrackCategory.VesselGroupReport, action: `click_${tab.id}_tab`, }) + if (tab.id === 'activity') { + fitAreaInViewport() + } }, - [dispatchQueryParams] + [dispatchQueryParams, fitAreaInViewport] ) const sectionTabs: Tab[] = useMemo( @@ -73,8 +79,7 @@ function VesselGroupReport() { { id: 'activity', title: t('common.activity', 'Activity'), - disabled: true, - content:

Coming soon

, + content: , }, { id: 'events', diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts index c0b1a2ed19..7d1596e125 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts @@ -4,19 +4,30 @@ import { DataviewInstance, DataviewType, } from '@globalfishingwatch/api-types' +import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' +import { ColorRampId } from '@globalfishingwatch/deck-layers' +import { REPORT_ONLY_VISIBLE_LAYERS } from 'data/config' import { CLUSTER_ENCOUNTER_EVENTS_DATAVIEW_SLUG, CLUSTER_LOITERING_EVENTS_DATAVIEW_SLUG, CLUSTER_PORT_VISIT_EVENTS_DATAVIEW_SLUG, DEFAULT_PRESENCE_VESSEL_GROUP_DATASETS, + FISHING_DATAVIEW_SLUG, PRESENCE_DATAVIEW_SLUG, } from 'data/workspaces' -import { VGREventsSubsection } from 'features/vessel-groups/vessel-groups.types' +import { + VGREventsSubsection, + VGRSection, + VGRSubsection, +} from 'features/vessel-groups/vessel-groups.types' export const VESSEL_GROUP_DATAVIEW_PREFIX = `vessel-group-` export type VesselGroupEventsDataviewId = `${typeof VESSEL_GROUP_DATAVIEW_PREFIX}${VGREventsSubsection}` + +export const VESSEL_GROUP_ACTIVITY_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}activity` + export const VESSEL_GROUP_ENCOUNTER_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}encounter` export const VESSEL_GROUP_LOITERING_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}loitering` export const VESSEL_GROUP_PORT_VISITS_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}port_visits` @@ -90,6 +101,32 @@ export const getVesselGroupDataviewInstance = ( } } +export const getVesselGroupActivityDataviewInstance = ({ + vesselGroupId, + color, + colorRamp, +}: { + vesselGroupId: string + color?: string + colorRamp?: ColorRampId +}): DataviewInstance | undefined => { + if (vesselGroupId) { + return { + id: VESSEL_GROUP_ACTIVITY_ID, + category: DataviewCategory.Activity, + config: { + visible: true, + ...(color && { color }), + ...(colorRamp && { colorRamp }), + filters: { + 'vessel-groups': [vesselGroupId], + }, + }, + dataviewId: FISHING_DATAVIEW_SLUG, + } + } +} + export const getVesselGroupEventDataviewInstance = ({ vesselGroupId, instanceId, diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx index 240e454582..c5d66cd70d 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx @@ -149,7 +149,7 @@ export default function VesselGroupReportVesselsGraph({ return (
- {data && ( + {data && data.length > 0 && ( Date: Tue, 24 Sep 2024 16:25:11 +0200 Subject: [PATCH 04/15] vessels activity in vessel group report component --- .../reports/activity/ReportActivity.tsx | 421 +++++++++++++----- .../activity/ReportActivityEvolution.tsx | 3 +- .../reports/activity/ReportActivityGraph.tsx | 125 ++++++ .../download/ReportDownload.module.css | 0 .../download/ReportDownload.tsx | 0 .../vessels/ReportVessels.module.css | 0 .../vessels/ReportVessels.tsx | 10 +- .../vessels/ReportVesselsFilter.module.css | 0 .../vessels/ReportVesselsFilter.tsx | 0 .../vessels/ReportVesselsGraph.module.css | 0 .../vessels/ReportVesselsGraph.tsx | 8 +- .../vessels/ReportVesselsGraphSelector.tsx | 2 +- .../vessels/ReportVesselsTable.module.css | 0 .../vessels/ReportVesselsTable.tsx | 12 +- .../ReportVesselsTableFooter.module.css | 0 .../vessels/ReportVesselsTableFooter.tsx | 23 +- .../report-activity-vessels.selectors.ts | 241 ++++++++++ .../vessels/report-activity-vessels.utils.ts | 23 + .../features/reports/areas/Report.tsx | 353 +-------------- .../reports/areas/reports.selectors.ts | 300 ++----------- .../reports/areas/summary/ReportSummary.tsx | 5 +- .../features/reports/events/VGREvents.tsx | 2 +- .../vessels/VesselGroupReportVessels.tsx | 2 +- .../vessel-group-report-vessels.selectors.ts | 2 +- apps/fishing-map/next-env.d.ts | 2 +- 25 files changed, 790 insertions(+), 744 deletions(-) create mode 100644 apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx rename apps/fishing-map/features/reports/{areas => activity}/download/ReportDownload.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/download/ReportDownload.tsx (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVessels.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVessels.tsx (87%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsFilter.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsFilter.tsx (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsGraph.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsGraph.tsx (95%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsGraphSelector.tsx (95%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsTable.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsTable.tsx (93%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsTableFooter.module.css (100%) rename apps/fishing-map/features/reports/{areas => activity}/vessels/ReportVesselsTableFooter.tsx (95%) create mode 100644 apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts create mode 100644 apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.utils.ts diff --git a/apps/fishing-map/features/reports/activity/ReportActivity.tsx b/apps/fishing-map/features/reports/activity/ReportActivity.tsx index 277f084622..93adb79733 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivity.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivity.tsx @@ -1,125 +1,336 @@ -import React, { Fragment, useEffect, useMemo } from 'react' -import { useSelector } from 'react-redux' +import { Fragment, useEffect, useMemo, useRef } from 'react' +import cx from 'classnames' import { useTranslation } from 'react-i18next' -import ReportActivityGraphSelector from 'features/reports/activity/ReportActivityGraphSelector' -import { useTimerangeConnect } from 'features/timebar/timebar.hooks' -import { - getReportGraphMode, - ReportGraphProps, - useComputeReportTimeSeries, - useReportFeaturesLoading, - useReportFilteredTimeSeries, -} from 'features/reports/areas/reports-timeseries.hooks' +import { useSelector } from 'react-redux' +import isEqual from 'lodash/isEqual' +import { Button } from '@globalfishingwatch/ui-components' import { - selectReportArea, - selectTimeComparisonValues, -} from 'features/reports/areas/reports.selectors' -import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' -import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison' -import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph' -import UserGuideLink from 'features/help/UserGuideLink' + getIsConcurrentError, + getIsTimeoutError, + isAuthError, +} from '@globalfishingwatch/api-client' +import { useLocalStorage } from '@globalfishingwatch/react-hooks' import { AsyncReducerStatus } from 'utils/async-slice' -import { selectReportActivityGraph } from '../areas/reports.config.selectors' -import { ReportActivityGraph } from '../areas/reports.types' -import { useFetchReportArea, useFitAreaInViewport } from '../areas/reports.hooks' -import ReportActivityEvolution from './ReportActivityEvolution' -import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' -import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' -import styles from './ReportActivity.module.css' +import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' +import { + isActivityReport, + selectActiveReportDataviews, +} from 'features/dataviews/selectors/dataviews.selectors' +import { WorkspaceLoginError } from 'features/workspace/WorkspaceError' +import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' +import { selectReportDataviewsWithPermissions } from 'features/reports/areas/reports.selectors' +import { selectHasReportVessels } from 'features/reports/activity/vessels/report-activity-vessels.selectors' +import ReportVesselsPlaceholder from 'features/reports/areas/placeholders/ReportVesselsPlaceholder' +import { getDownloadReportSupported } from 'features/download/download.utils' +import { SUPPORT_EMAIL } from 'data/config' +import { parseReportUrl } from 'features/reports/areas/reports.utils' +import { + getDateRangeHash, + selectReportVesselsDateRangeHash, + setDateRangeHash, +} from 'features/reports/areas/report.slice' +import { useAppDispatch } from 'features/app/app.hooks' +import { + selectReportAreaId, + selectReportCategory, + selectReportDatasetId, +} from 'features/app/selectors/app.reports.selector' +import { formatI18nDate } from 'features/i18n/i18nDate' +import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' +import DatasetLabel from 'features/datasets/DatasetLabel' +import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' +// import { REPORT_BUFFER_GENERATOR_ID } from 'features/map/map.config' +import { selectIsGuestUser, selectUserData } from 'features/user/selectors/user.selectors' +import { useFetchDataviewResources } from 'features/resources/resources.hooks' +import ReportActivityGraph from 'features/reports/activity/ReportActivityGraph' +import { useFetchReportVessel } from 'features/reports/areas/reports.hooks' +import ReportVessels from 'features/reports/activity/vessels/ReportVessels' +import ReportDownload from 'features/reports/activity/download/ReportDownload' +import styles from 'features/reports/areas/Report.module.css' -export type ReportActivityProps = { - data: ReportGraphProps - start: string - end: string -} +export type ReportActivityUnit = 'hour' | 'detection' -const SELECTORS_BY_TYPE: Record = { - evolution: null, - beforeAfter: ReportActivityBeforeAfter, - periodComparison: ReportActivityPeriodComparison, -} -const GRAPH_BY_TYPE: Record | null> = { - evolution: ReportActivityEvolution, - beforeAfter: ReportActivityBeforeAfterGraph, - periodComparison: ReportActivityPeriodComparisonGraph, -} +function ActivityReport({ reportName }: { reportName?: string }) { + useFetchDataviewResources() + const { t } = useTranslation() + const dispatch = useAppDispatch() + const [lastReports] = useLocalStorage(LAST_REPORTS_STORAGE_KEY, []) + const reportCategory = useSelector(selectReportCategory) + const timerange = useSelector(selectTimeRange) + const reportDataviews = useSelector(selectReportDataviewsWithPermissions) + const guestUser = useSelector(selectIsGuestUser) + const datasetId = useSelector(selectReportDatasetId) + const areaId = useSelector(selectReportAreaId) + const reportDateRangeHash = useSelector(selectReportVesselsDateRangeHash) + const userData = useSelector(selectUserData) + const workspaceStatus = useSelector(selectWorkspaceStatus) + const dataviews = useSelector(selectActiveReportDataviews) + const datasetsDownloadNotSupported = getDatasetsReportNotSupported( + dataviews, + userData?.permissions || [] + ) + const timerangeTooLong = !getDownloadReportSupported(timerange.start, timerange.end) + const { status: reportStatus, error: statusError, dispatchFetchReport } = useFetchReportVessel() + const dispatchTimeoutRef = useRef() + const hasVessels = useSelector(selectHasReportVessels) -const emptyGraphData = {} as ReportGraphProps + // TODO get this from datasets config + const activityUnit = isActivityReport(reportCategory) ? 'hour' : 'detection' -export default function ReportActivity() { - useComputeReportTimeSeries() - const reportArea = useSelector(selectReportArea) - const { status } = useFetchReportArea() + const reportLoading = reportStatus === AsyncReducerStatus.Loading + const reportError = reportStatus === AsyncReducerStatus.Error + const reportLoaded = reportStatus === AsyncReducerStatus.Finished + const reportOutdated = + reportDateRangeHash !== '' && reportDateRangeHash !== getDateRangeHash(timerange) + const hasAuthError = reportError && isAuthError(statusError) - const fitAreaInViewport = useFitAreaInViewport() + const { currentReportUrl } = statusError?.metadata || ({} as { currentReportUrl: string }) + const lastReport = currentReportUrl + ? lastReports.find((report) => { + const currentReportParams = parseReportUrl(currentReportUrl) + const reportParams = parseReportUrl(report.reportUrl) + return isEqual(currentReportParams, reportParams) + }) + : undefined + const concurrentReportError = getIsConcurrentError(statusError!) + const isSameWorkspaceReport = + concurrentReportError && window?.location.href === lastReport?.workspaceUrl - // This ensures that the area is in viewport when then area load finishes + const isTimeoutError = getIsTimeoutError(statusError!) useEffect(() => { - if (status === AsyncReducerStatus.Finished && reportArea?.bounds) { - requestAnimationFrame(() => { - fitAreaInViewport() - }) + if (isTimeoutError) { + dispatchTimeoutRef.current = setTimeout(() => { + dispatchFetchReport() + }, 1000 * 30) // retrying each 30 secs + } + return () => { + if (dispatchTimeoutRef.current) { + clearTimeout(dispatchTimeoutRef.current) + } } - // Reacting only to the area status and fitting bounds after load - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [status, reportArea]) + }, [dispatchFetchReport, isTimeoutError]) - const { t } = useTranslation() - const { start, end } = useTimerangeConnect() - const reportActivityGraph = useSelector(selectReportActivityGraph) - const timeComparisonValues = useSelector(selectTimeComparisonValues) + const ReportVesselError = useMemo(() => { + if (hasAuthError || guestUser) { + return ( + +
+ +
+
+ ) + } + if (statusError) { + if (concurrentReportError) { + if (isSameWorkspaceReport) { + return + } + return ( + +
+

+ {t('analysis.errorConcurrentReport', 'There is already a report running')} +

+ {lastReport && ( + + {t('analysis.errorConcurrentReportLink', 'See it')} + + )} +

+

+
+
+ ) + } - const SelectorsComponent = useMemo( - () => SELECTORS_BY_TYPE[reportActivityGraph], - [reportActivityGraph] - ) - const GraphComponent = useMemo( - () => GRAPH_BY_TYPE[reportActivityGraph] as any, - [reportActivityGraph] - ) - const loading = useReportFeaturesLoading() - const layersTimeseriesFiltered = useReportFilteredTimeSeries() - const reportGraphMode = getReportGraphMode(reportActivityGraph) - const isSameTimeseriesMode = layersTimeseriesFiltered?.[0]?.mode === reportGraphMode - const showSelectors = layersTimeseriesFiltered !== undefined - const showPlaceholder = loading || !isSameTimeseriesMode - const isEmptyData = - layersTimeseriesFiltered && - layersTimeseriesFiltered.every(({ timeseries }) => timeseries.length === 0) && - !loading + if ( + statusError.status === 413 || + (statusError.status === 422 && statusError.message === 'Geometry too large') + ) { + return ( + +
+

+ {t( + 'analysis.errorTooComplex', + 'The geometry of the area is too complex to perform a report, try to simplify and upload again.' + )} +

+
+
+ ) + } + if (isTimeoutError) { + return ( + +
+

+ {t('analysis.timeoutError', 'This is taking more than expected, please wait')} +

+
+
+ ) + } + return ( + +
+

{statusError.message}

+
+
+ ) + } - return ( -
- {showSelectors && ( -
- - -
- )} - {showPlaceholder ? ( - - ) : isEmptyData ? ( - - {t('analysis.noDataByArea', 'No data available for the selected area')} - - ) : ( - - )} - {showSelectors && SelectorsComponent && } - {!showPlaceholder && ( - -
- + if (!reportDataviews?.length) { + return ( +

+ {t( + 'analysis.datasetsNotAllowedAll', + 'None of your datasets are allowed to be used in reports' + )}{' '} +

+ ) + } + return ( +

+ + {t('errors.generic', 'Something went wrong, try again or contact:')}{' '} + {SUPPORT_EMAIL} + +

+ ) + }, [ + areaId, + concurrentReportError, + datasetId, + guestUser, + hasAuthError, + isSameWorkspaceReport, + isTimeoutError, + lastReport, + reportDataviews?.length, + statusError, + t, + ]) + + const ReportComponent = useMemo(() => { + if (workspaceStatus === AsyncReducerStatus.Loading) { + return + } + if (timerangeTooLong) { + return ( + +

- {t('analysis.disclaimer', 'The data shown above should be taken as an estimate.')} + {t( + 'analysis.timeRangeTooLong', + 'The selected time range is too long, please select a shorter time range' + )}

+
+ ) + } + + if (reportError || (!reportLoading && !reportDataviews?.length)) { + return ReportVesselError + } + + if ( + (reportOutdated || reportStatus === AsyncReducerStatus.Idle) && + !reportLoading && + !hasAuthError + ) { + return ( + +
+

between {{start}} and {{end}}', + start: formatI18nDate(timerange?.start), + end: formatI18nDate(timerange?.end), + }), + }} + /> + +

+
+ ) + } + if (reportLoaded) { + return hasVessels ? ( + + + - )} -
+ ) : ( +
+ {datasetsDownloadNotSupported.length > 0 && ( +

+ {t( + 'analysis.datasetsNotAllowed', + 'Vessels are not included from the following sources:' + )}{' '} + {datasetsDownloadNotSupported.map((dataset, index) => ( + + + {index < datasetsDownloadNotSupported.length - 1 && ', '} + + ))} +

+ )} +

{t('analysis.noDataByArea', 'No data available for the selected area')}

+
+ ) + } + + return + }, [ + workspaceStatus, + timerangeTooLong, + reportError, + reportLoading, + reportDataviews?.length, + reportOutdated, + reportStatus, + hasAuthError, + reportLoaded, + t, + ReportVesselError, + timerange?.start, + timerange?.end, + dispatch, + dispatchFetchReport, + hasVessels, + activityUnit, + reportName, + datasetsDownloadNotSupported, + ]) + + return ( + + + {ReportComponent} + ) } + +export default ActivityReport diff --git a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx index a7ece2c959..e2701aa701 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx @@ -15,10 +15,9 @@ import { DateTime } from 'luxon' import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' import i18n from 'features/i18n/i18n' import { formatDateForInterval, getUTCDateTime } from 'utils/dates' -import { toFixed } from 'utils/shared' import { formatI18nNumber } from 'features/i18n/i18nNumber' -import { ReportActivityProps } from 'features/reports/activity/ReportActivity' import { formatEvolutionData } from 'features/reports/areas/reports-timeseries.utils' +import { ReportActivityProps } from 'features/reports/activity/ReportActivityGraph' import { tickFormatter } from '../areas/reports.utils' import styles from './ReportActivityEvolution.module.css' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx new file mode 100644 index 0000000000..277f084622 --- /dev/null +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx @@ -0,0 +1,125 @@ +import React, { Fragment, useEffect, useMemo } from 'react' +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' +import ReportActivityGraphSelector from 'features/reports/activity/ReportActivityGraphSelector' +import { useTimerangeConnect } from 'features/timebar/timebar.hooks' +import { + getReportGraphMode, + ReportGraphProps, + useComputeReportTimeSeries, + useReportFeaturesLoading, + useReportFilteredTimeSeries, +} from 'features/reports/areas/reports-timeseries.hooks' +import { + selectReportArea, + selectTimeComparisonValues, +} from 'features/reports/areas/reports.selectors' +import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' +import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison' +import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph' +import UserGuideLink from 'features/help/UserGuideLink' +import { AsyncReducerStatus } from 'utils/async-slice' +import { selectReportActivityGraph } from '../areas/reports.config.selectors' +import { ReportActivityGraph } from '../areas/reports.types' +import { useFetchReportArea, useFitAreaInViewport } from '../areas/reports.hooks' +import ReportActivityEvolution from './ReportActivityEvolution' +import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' +import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' +import styles from './ReportActivity.module.css' + +export type ReportActivityProps = { + data: ReportGraphProps + start: string + end: string +} + +const SELECTORS_BY_TYPE: Record = { + evolution: null, + beforeAfter: ReportActivityBeforeAfter, + periodComparison: ReportActivityPeriodComparison, +} +const GRAPH_BY_TYPE: Record | null> = { + evolution: ReportActivityEvolution, + beforeAfter: ReportActivityBeforeAfterGraph, + periodComparison: ReportActivityPeriodComparisonGraph, +} + +const emptyGraphData = {} as ReportGraphProps + +export default function ReportActivity() { + useComputeReportTimeSeries() + const reportArea = useSelector(selectReportArea) + const { status } = useFetchReportArea() + + const fitAreaInViewport = useFitAreaInViewport() + + // This ensures that the area is in viewport when then area load finishes + useEffect(() => { + if (status === AsyncReducerStatus.Finished && reportArea?.bounds) { + requestAnimationFrame(() => { + fitAreaInViewport() + }) + } + // Reacting only to the area status and fitting bounds after load + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [status, reportArea]) + + const { t } = useTranslation() + const { start, end } = useTimerangeConnect() + const reportActivityGraph = useSelector(selectReportActivityGraph) + const timeComparisonValues = useSelector(selectTimeComparisonValues) + + const SelectorsComponent = useMemo( + () => SELECTORS_BY_TYPE[reportActivityGraph], + [reportActivityGraph] + ) + const GraphComponent = useMemo( + () => GRAPH_BY_TYPE[reportActivityGraph] as any, + [reportActivityGraph] + ) + const loading = useReportFeaturesLoading() + const layersTimeseriesFiltered = useReportFilteredTimeSeries() + const reportGraphMode = getReportGraphMode(reportActivityGraph) + const isSameTimeseriesMode = layersTimeseriesFiltered?.[0]?.mode === reportGraphMode + const showSelectors = layersTimeseriesFiltered !== undefined + const showPlaceholder = loading || !isSameTimeseriesMode + const isEmptyData = + layersTimeseriesFiltered && + layersTimeseriesFiltered.every(({ timeseries }) => timeseries.length === 0) && + !loading + + return ( +
+ {showSelectors && ( +
+ + +
+ )} + {showPlaceholder ? ( + + ) : isEmptyData ? ( + + {t('analysis.noDataByArea', 'No data available for the selected area')} + + ) : ( + + )} + {showSelectors && SelectorsComponent && } + {!showPlaceholder && ( + +
+ +

+ {t('analysis.disclaimer', 'The data shown above should be taken as an estimate.')} +

+
+
+ )} +
+ ) +} diff --git a/apps/fishing-map/features/reports/areas/download/ReportDownload.module.css b/apps/fishing-map/features/reports/activity/download/ReportDownload.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/download/ReportDownload.module.css rename to apps/fishing-map/features/reports/activity/download/ReportDownload.module.css diff --git a/apps/fishing-map/features/reports/areas/download/ReportDownload.tsx b/apps/fishing-map/features/reports/activity/download/ReportDownload.tsx similarity index 100% rename from apps/fishing-map/features/reports/areas/download/ReportDownload.tsx rename to apps/fishing-map/features/reports/activity/download/ReportDownload.tsx diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVessels.module.css b/apps/fishing-map/features/reports/activity/vessels/ReportVessels.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVessels.module.css rename to apps/fishing-map/features/reports/activity/vessels/ReportVessels.module.css diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx similarity index 87% rename from apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx index d0d760d7f6..016b0b301b 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVessels.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx @@ -1,15 +1,15 @@ import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' -import ReportVesselsGraphSelector from 'features/reports/areas/vessels/ReportVesselsGraphSelector' +import ReportVesselsGraphSelector from 'features/reports/activity/vessels/ReportVesselsGraphSelector' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/reports/areas/summary/ReportSummaryTags' import { FIELDS, getCommonProperties } from 'features/reports/areas/reports.utils' import { PROPERTIES_EXCLUDED } from 'features/reports/areas/summary/ReportSummary' -import { ReportActivityUnit } from '../Report' -import { selectReportVesselFilter } from '../reports.config.selectors' -import { ReportCategory } from '../reports.types' +import { ReportActivityUnit } from 'features/reports/areas/Report' +import { selectReportVesselFilter } from 'features/reports/areas/reports.config.selectors' +import { ReportCategory } from 'features/reports/areas/reports.types' import ReportVesselsGraph from './ReportVesselsGraph' import ReportVesselsFilter from './ReportVesselsFilter' import ReportVesselsTable from './ReportVesselsTable' @@ -17,7 +17,7 @@ import styles from './ReportVessels.module.css' type ReportVesselTableProps = { activityUnit: ReportActivityUnit - reportName: string + reportName?: string } export default function ReportVessels({ activityUnit, reportName }: ReportVesselTableProps) { diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsFilter.module.css b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsFilter.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsFilter.module.css rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsFilter.module.css diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsFilter.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsFilter.tsx similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsFilter.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsFilter.tsx diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraph.module.css b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraph.module.css rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.module.css diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraph.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx similarity index 95% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraph.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx index e0c6762938..7521d54adb 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraph.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx @@ -15,13 +15,13 @@ import { } from 'data/config' import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/reports.config' import { getVesselGearTypeLabel } from 'utils/info' +import { ReportVesselGraph } from 'features/reports/areas/reports.types' import { - cleanFlagState, - selectReportDataviewsWithPermissions, selectReportVesselsGraphDataGrouped, selectReportVesselsGraphDataOthers, -} from '../reports.selectors' -import { ReportVesselGraph } from '../reports.types' +} from 'features/reports/activity/vessels/report-activity-vessels.selectors' +import { cleanFlagState } from 'features/reports/activity/vessels/report-activity-vessels.utils' +import { selectReportDataviewsWithPermissions } from 'features/reports/areas/reports.selectors' import styles from './ReportVesselsGraph.module.css' const MAX_OTHER_TOOLTIP_ITEMS = 10 diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx similarity index 95% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx index 5821c195ca..f1eb4eb8bc 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsGraphSelector.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx @@ -11,7 +11,7 @@ import { import { selectReportVesselGraph } from 'features/app/selectors/app.reports.selector' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' -import { ReportCategory, ReportVesselGraph } from '../reports.types' +import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' export default function ReportVesselsGraphSelector() { const { dispatchQueryParams } = useLocationConnect() diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.module.css b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.module.css rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.module.css diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx similarity index 93% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx index 49cc1c3f54..82a85df3db 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTable.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx @@ -6,7 +6,6 @@ import { EMPTY_FIELD_PLACEHOLDER, formatInfoField } from 'utils/info' import I18nNumber from 'features/i18n/i18nNumber' import { useLocationConnect } from 'routes/routes.hook' import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' -import ReportVesselsTableFooter from 'features/reports/areas/vessels/ReportVesselsTableFooter' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectUserData } from 'features/user/selectors/user.selectors' @@ -15,14 +14,15 @@ import { EMPTY_API_VALUES } from 'features/reports/areas/reports.config' import VesselLink from 'features/vessel/VesselLink' 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' +import { ReportActivityUnit } from 'features/reports/areas/Report' +import { selectReportVesselsPaginated } from 'features/reports/activity/vessels/report-activity-vessels.selectors' +import { ReportCategory } from 'features/reports/areas/reports.types' +import ReportVesselsTableFooter from 'features/reports/activity/vessels/ReportVesselsTableFooter' +import styles from 'features/reports/activity/vessels/ReportVesselsTable.module.css' type ReportVesselTableProps = { activityUnit: ReportActivityUnit - reportName: string + reportName?: string } export default function ReportVesselsTable({ activityUnit, reportName }: ReportVesselTableProps) { diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTableFooter.module.css b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsTableFooter.module.css rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.module.css diff --git a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTableFooter.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx similarity index 95% rename from apps/fishing-map/features/reports/areas/vessels/ReportVesselsTableFooter.tsx rename to apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx index f6407b075c..15be5c32cd 100644 --- a/apps/fishing-map/features/reports/areas/vessels/ReportVesselsTableFooter.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx @@ -17,20 +17,25 @@ import { } from 'features/vessel-groups/vessel-groups.slice' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' +import { selectReportVesselFilter } from 'features/reports/areas/reports.config.selectors' import { - selectReportVesselsFiltered, - selectReportVesselsList, - selectReportVesselsListWithAllInfo, - selectReportVesselsPagination, - ReportVesselWithDatasets, selectReportAreaName, -} from '../reports.selectors' -import { getVesselsFiltered, parseReportVesselsToIdentity } from '../reports.utils' -import { selectReportVesselFilter } from '../reports.config.selectors' + ReportVesselWithDatasets, +} from 'features/reports/areas/reports.selectors' +import { + parseReportVesselsToIdentity, + getVesselsFiltered, +} from 'features/reports/areas/reports.utils' import styles from './ReportVesselsTableFooter.module.css' +import { + selectReportVesselsListWithAllInfo, + selectReportVesselsList, + selectReportVesselsFiltered, + selectReportVesselsPagination, +} from './report-activity-vessels.selectors' type ReportVesselsTableFooterProps = { - reportName: string + reportName?: string } export default function ReportVesselsTableFooter({ reportName }: ReportVesselsTableFooterProps) { diff --git a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts new file mode 100644 index 0000000000..41a8d5bb88 --- /dev/null +++ b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts @@ -0,0 +1,241 @@ +import { createSelector } from '@reduxjs/toolkit' +import { groupBy, sum, sumBy, uniq, uniqBy } from 'es-toolkit' +import { t } from 'i18next' +import { DatasetTypes } from '@globalfishingwatch/api-types' +import { + selectReportVesselGraph, + selectReportCategory, +} from 'features/app/selectors/app.reports.selector' +import { selectAllDatasets } from 'features/datasets/datasets.slice' +import { getRelatedDatasetByType } from 'features/datasets/datasets.utils' +import { getVesselsFiltered } from 'features/reports/areas/reports.utils' +import { + EMPTY_API_VALUES, + MAX_CATEGORIES, + OTHERS_CATEGORY_LABEL, +} from 'features/reports/areas/reports.config' +import { + ReportVesselWithDatasets, + selectReportActivityFlatten, + selectReportDataviewsWithPermissions, +} from 'features/reports/areas/reports.selectors' +import { + selectReportResultsPerPage, + selectReportVesselFilter, + selectReportVesselPage, +} from 'features/reports/areas/reports.config.selectors' +import { + cleanFlagState, + cleanVesselOrGearType, +} from 'features/reports/activity/vessels/report-activity-vessels.utils' +import { getVesselGearTypeLabel } from 'utils/info' + +const EMPTY_ARRAY: [] = [] + +export const selectReportVesselsList = createSelector( + [selectReportActivityFlatten, selectAllDatasets, selectReportCategory], + (vessels, datasets, reportCategory) => { + if (!vessels?.length) return null + return Object.values(groupBy(vessels, (v) => v.vesselId)) + .flatMap((vesselActivity) => { + if (vesselActivity[0]?.category !== reportCategory) return EMPTY_ARRAY + const activityDataset = datasets.find((d) => vesselActivity[0].activityDatasetId === d.id) + const infoDatasetId = getRelatedDatasetByType(activityDataset, DatasetTypes.Vessels)?.id + const infoDataset = datasets.find((d) => d.id === infoDatasetId) + const trackDatasetId = getRelatedDatasetByType( + infoDataset || activityDataset, + DatasetTypes.Tracks + )?.id + const trackDataset = datasets.find((d) => d.id === trackDatasetId) + return { + dataviewId: vesselActivity[0]?.dataviewId, + vesselId: vesselActivity[0]?.vesselId, + shipName: vesselActivity[0]?.shipName, + mmsi: vesselActivity[0]?.mmsi, + flag: vesselActivity[0]?.flag, + flagTranslated: t( + `flags:${vesselActivity[0]?.flag as string}` as any, + vesselActivity[0]?.flag + ), + flagTranslatedClean: cleanFlagState( + t(`flags:${vesselActivity[0]?.flag as string}` as any, vesselActivity[0]?.flag) + ), + geartype: cleanVesselOrGearType({ + value: vesselActivity[0]?.geartype, + property: 'geartype', + }), + vesselType: cleanVesselOrGearType({ + value: vesselActivity[0]?.vesselType, + property: 'vesselType', + }), + value: vesselActivity[0]?.value, + infoDataset, + trackDataset, + sourceColor: vesselActivity[0]?.sourceColor, + } as ReportVesselWithDatasets + }) + .sort((a, b) => (b.value as number) - (a.value as number)) + } +) + +export const selectHasReportVessels = createSelector([selectReportVesselsList], (vessels) => { + return vessels!?.length > 0 +}) + +export const selectReportVesselsFiltered = createSelector( + [selectReportVesselsList, selectReportVesselFilter], + (vessels, filter) => { + if (!vessels?.length) return null + return getVesselsFiltered(vessels, filter).sort( + (a, b) => b.value - a.value + ) + } +) + +const defaultVesselsList: ReportVesselWithDatasets[] = [] +export const selectReportVesselsPaginated = createSelector( + [selectReportVesselsFiltered, selectReportVesselPage, selectReportResultsPerPage], + (vessels, page = 0, resultsPerPage) => { + if (!vessels?.length) return defaultVesselsList + return vessels.slice(resultsPerPage * page, resultsPerPage * (page + 1)) + } +) + +export const selectReportVesselsPagination = createSelector( + [ + selectReportVesselsPaginated, + selectReportVesselsFiltered, + selectReportVesselsList, + selectReportVesselPage, + selectReportResultsPerPage, + ], + (vessels, allVesselsFiltered, allVessels, page = 0, resultsPerPage) => { + return { + page, + offset: resultsPerPage * page, + resultsPerPage, + resultsNumber: vessels!?.length, + totalFiltered: allVesselsFiltered!?.length, + total: allVessels!?.length, + } + } +) + +const selectReportVesselsGraphData = createSelector( + [selectReportVesselGraph, selectReportVesselsFiltered, selectReportDataviewsWithPermissions], + (reportGraph, vesselsFiltered, dataviews) => { + if (!vesselsFiltered?.length) return null + + const reportData = groupBy(vesselsFiltered, (v) => v.dataviewId || '') + + const dataByDataview = dataviews.map((dataview) => { + const dataviewData = reportData[dataview.id] + ? Object.values(reportData[dataview.id]).flatMap((v) => v || []) + : [] + const dataByKey = groupBy(dataviewData, (d) => d[reportGraph] || '') + return { id: dataview.id, data: dataByKey } + }) + + const allDistributionKeys = uniq(dataByDataview.flatMap(({ data }) => Object.keys(data))) + + const dataviewIds = dataviews.map((d) => d.id) + const data = allDistributionKeys + .flatMap((key) => { + const distributionData: Record = { name: key } + dataByDataview.forEach(({ id, data }) => { + distributionData[id] = (data?.[key] || []).length + }) + if (sum(dataviewIds.map((d) => distributionData[d])) === 0) return EMPTY_ARRAY + return distributionData + }) + .sort((a, b) => { + if (EMPTY_API_VALUES.includes(a.name)) return 1 + if (EMPTY_API_VALUES.includes(b.name)) return -1 + return sum(dataviewIds.map((d: any) => b[d])) - sum(dataviewIds.map((d: any) => a[d])) + }) + + return { distributionKeys: data.map((d) => d.name), data } + } +) + +export const selectReportVesselsGraphDataGrouped = createSelector( + [selectReportVesselsGraphData, selectReportDataviewsWithPermissions], + (reportGraph, dataviews) => { + if (!reportGraph?.data?.length) return null + if (reportGraph?.distributionKeys.length <= MAX_CATEGORIES) return reportGraph.data + const dataviewIds = dataviews.map((d) => d.id) + const top = reportGraph.data.slice(0, MAX_CATEGORIES) + const rest = reportGraph.data.slice(MAX_CATEGORIES) + const others = { + name: OTHERS_CATEGORY_LABEL, + ...Object.fromEntries( + dataviewIds.map((dataview) => [dataview, sum(rest.map((key: any) => key[dataview]))]) + ), + } + return [...top, others] + } +) + +const defaultOthersLabel: any[] = [] +export const selectReportVesselsGraphDataOthers = createSelector( + [selectReportVesselsGraphData], + (reportGraph) => { + if (!reportGraph?.data?.length) return null + if (reportGraph?.distributionKeys.length <= MAX_CATEGORIES) return defaultOthersLabel + const others = reportGraph.data.slice(MAX_CATEGORIES) + return reportGraph.distributionKeys + .flatMap((key) => { + const other = others.find((o) => o.name === key) + if (!other) return EMPTY_ARRAY + const { name, ...rest } = other + return { name, value: sum(Object.values(rest)) } + }) + .sort((a, b) => { + if (EMPTY_API_VALUES.includes(a.name)) return 1 + if (EMPTY_API_VALUES.includes(b.name)) return -1 + return b.value - a.value + }) + } +) + +export const selectReportVesselsNumber = createSelector( + [selectReportActivityFlatten], + (vessels) => { + if (!vessels?.length) return null + + return uniqBy(vessels, (v) => v.vesselId).length + } +) + +export const selectReportVesselsHours = createSelector([selectReportActivityFlatten], (vessels) => { + if (!vessels?.length) return null + return vessels.map((vessel) => vessel?.value || 0).reduce((acc, value) => acc + value, 0) +}) + +export const selectReportVesselsListWithAllInfo = createSelector( + [selectReportActivityFlatten], + (vessels) => { + if (!vessels?.length) return null + + return Object.values(groupBy(vessels, (v) => v.vesselId)) + .map((vesselActivity) => { + return { + ...vesselActivity[0], + value: sumBy(vesselActivity, (a) => a.value), + flagTranslated: t( + `flags:${vesselActivity[0]?.flag as string}` as any, + vesselActivity[0]?.flag + ), + flagTranslatedClean: cleanFlagState( + t(`flags:${vesselActivity[0]?.flag as string}` as any, vesselActivity[0]?.flag) + ), + geartype: getVesselGearTypeLabel({ geartypes: vesselActivity[0]?.geartype }), + vesselType: t( + `vessel.veeselTypes.${vesselActivity[0]?.vesselType}` as any, + vesselActivity[0]?.vesselType + ), + } + }) + .sort((a, b) => b.value - a.value) + } +) diff --git a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.utils.ts b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.utils.ts new file mode 100644 index 0000000000..b5628e9d2f --- /dev/null +++ b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.utils.ts @@ -0,0 +1,23 @@ +import { t } from 'i18next' +import { EMPTY_FIELD_PLACEHOLDER, getVesselGearTypeLabel } from 'utils/info' +import { sortStrings } from 'utils/shared' + +type CleanVesselOrGearTypeParams = { value: string; property: 'geartype' | 'vesselType' } +export function cleanVesselOrGearType({ value, property }: CleanVesselOrGearTypeParams) { + const valuesClean = value ? value?.split(',').filter(Boolean) : [EMPTY_FIELD_PLACEHOLDER] + const valuesCleanTranslated = valuesClean + .map((value) => { + if (property === 'geartype') { + return getVesselGearTypeLabel({ geartypes: value }) + } + return t(`vessel.vesselTypes.${value?.toLowerCase()}` as any, value) + }) + .sort(sortStrings) + return valuesCleanTranslated.length > 1 + ? valuesCleanTranslated.join('|') + : valuesCleanTranslated[0] +} + +export function cleanFlagState(flagState: string) { + return flagState.replace(/,/g, '') +} diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/Report.tsx index 29788b8a60..25e2661860 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/Report.tsx @@ -1,365 +1,44 @@ -import { Fragment, useCallback, useEffect, useMemo, useRef } from 'react' -import cx from 'classnames' +import { Fragment, useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { uniq } from 'es-toolkit' -import isEqual from 'lodash/isEqual' -import { Button, Tab, Tabs } from '@globalfishingwatch/ui-components' -import { - getIsConcurrentError, - getIsTimeoutError, - isAuthError, -} from '@globalfishingwatch/api-client' -import { useLocalStorage } from '@globalfishingwatch/react-hooks' +import { Tab, Tabs } from '@globalfishingwatch/ui-components' import { ContextFeature } from '@globalfishingwatch/deck-layers' import { AsyncReducerStatus } from 'utils/async-slice' import { useLocationConnect } from 'routes/routes.hook' -import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { isActivityReport, - selectActiveReportDataviews, selectActiveTemporalgridDataviews, } from 'features/dataviews/selectors/dataviews.selectors' -import WorkspaceError, { - ErrorPlaceHolder, - WorkspaceLoginError, -} from 'features/workspace/WorkspaceError' +import WorkspaceError, { ErrorPlaceHolder } from 'features/workspace/WorkspaceError' import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' import { selectWorkspaceVesselGroupsStatus } from 'features/vessel-groups/vessel-groups.slice' import { selectHasReportBuffer, - selectHasReportVessels, selectReportArea, selectReportAreaStatus, - selectReportDataviewsWithPermissions, } from 'features/reports/areas/reports.selectors' -import ReportVesselsPlaceholder from 'features/reports/areas/placeholders/ReportVesselsPlaceholder' import { TimebarVisualisations } from 'types' -import { getDownloadReportSupported } from 'features/download/download.utils' -import { SUPPORT_EMAIL } from 'data/config' import { useTimebarEnvironmentConnect, useTimebarVisualisationConnect, } from 'features/timebar/timebar.hooks' -import { getReportCategoryFromDataview, parseReportUrl } from 'features/reports/areas/reports.utils' -import { - getDateRangeHash, - resetReportData, - selectReportVesselsDateRangeHash, - selectReportVesselsStatus, - setDateRangeHash, -} from 'features/reports/areas/report.slice' +import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { resetReportData, selectReportVesselsStatus } from 'features/reports/areas/report.slice' import { useAppDispatch } from 'features/app/app.hooks' -import { - selectReportAreaId, - selectReportCategory, - selectReportDatasetId, -} from 'features/app/selectors/app.reports.selector' -import { formatI18nDate } from 'features/i18n/i18nDate' +import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { useSetTimeseries } from 'features/reports/areas/reports-timeseries.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' -import DatasetLabel from 'features/datasets/DatasetLabel' -import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' -// import { REPORT_BUFFER_GENERATOR_ID } from 'features/map/map.config' -import { selectIsGuestUser, selectUserData } from 'features/user/selectors/user.selectors' -import { useFetchDataviewResources } from 'features/resources/resources.hooks' -import ReportActivity from '../activity/ReportActivity' -import { useFetchReportVessel, useFitAreaInViewport, useHighlightReportArea } from './reports.hooks' -import ReportSummary from './summary/ReportSummary' -import ReportTitle from './title/ReportTitle' -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' +import ActivityReport from 'features/reports/activity/ReportActivity' +import ReportTitle from 'features/reports/areas/title/ReportTitle' +import { ReportCategory } from 'features/reports/areas/reports.types' +import ReportSummary from 'features/reports/areas/summary/ReportSummary' +import ReportEnvironment from 'features/reports/areas/environment/ReportEnvironment' +import { useFitAreaInViewport, useHighlightReportArea } from 'features/reports/areas/reports.hooks' +import styles from 'features/reports/areas/Report.module.css' export type ReportActivityUnit = 'hour' | 'detection' -function ActivityReport({ reportName }: { reportName: string }) { - useFetchDataviewResources() - const { t } = useTranslation() - const dispatch = useAppDispatch() - const [lastReports] = useLocalStorage(LAST_REPORTS_STORAGE_KEY, []) - const reportCategory = useSelector(selectReportCategory) - const timerange = useSelector(selectTimeRange) - const reportDataviews = useSelector(selectReportDataviewsWithPermissions) - const guestUser = useSelector(selectIsGuestUser) - const datasetId = useSelector(selectReportDatasetId) - const areaId = useSelector(selectReportAreaId) - const reportDateRangeHash = useSelector(selectReportVesselsDateRangeHash) - const userData = useSelector(selectUserData) - const workspaceStatus = useSelector(selectWorkspaceStatus) - const dataviews = useSelector(selectActiveReportDataviews) - const datasetsDownloadNotSupported = getDatasetsReportNotSupported( - dataviews, - userData?.permissions || [] - ) - const timerangeTooLong = !getDownloadReportSupported(timerange.start, timerange.end) - const { status: reportStatus, error: statusError, dispatchFetchReport } = useFetchReportVessel() - const dispatchTimeoutRef = useRef() - const hasVessels = useSelector(selectHasReportVessels) - - // TODO get this from datasets config - const activityUnit = isActivityReport(reportCategory) ? 'hour' : 'detection' - - const reportLoading = reportStatus === AsyncReducerStatus.Loading - const reportError = reportStatus === AsyncReducerStatus.Error - const reportLoaded = reportStatus === AsyncReducerStatus.Finished - const reportOutdated = - reportDateRangeHash !== '' && reportDateRangeHash !== getDateRangeHash(timerange) - const hasAuthError = reportError && isAuthError(statusError) - - const { currentReportUrl } = statusError?.metadata || ({} as { currentReportUrl: string }) - const lastReport = currentReportUrl - ? lastReports.find((report) => { - const currentReportParams = parseReportUrl(currentReportUrl) - const reportParams = parseReportUrl(report.reportUrl) - return isEqual(currentReportParams, reportParams) - }) - : undefined - const concurrentReportError = getIsConcurrentError(statusError!) - const isSameWorkspaceReport = - concurrentReportError && window?.location.href === lastReport?.workspaceUrl - - const isTimeoutError = getIsTimeoutError(statusError!) - useEffect(() => { - if (isTimeoutError) { - dispatchTimeoutRef.current = setTimeout(() => { - dispatchFetchReport() - }, 1000 * 30) // retrying each 30 secs - } - return () => { - if (dispatchTimeoutRef.current) { - clearTimeout(dispatchTimeoutRef.current) - } - } - }, [dispatchFetchReport, isTimeoutError]) - - const ReportVesselError = useMemo(() => { - if (hasAuthError || guestUser) { - return ( - -
- -
-
- ) - } - if (statusError) { - if (concurrentReportError) { - if (isSameWorkspaceReport) { - return - } - return ( - -
-

- {t('analysis.errorConcurrentReport', 'There is already a report running')} -

- {lastReport && ( - - {t('analysis.errorConcurrentReportLink', 'See it')} - - )} -

-

-
-
- ) - } - - if ( - statusError.status === 413 || - (statusError.status === 422 && statusError.message === 'Geometry too large') - ) { - return ( - -
-

- {t( - 'analysis.errorTooComplex', - 'The geometry of the area is too complex to perform a report, try to simplify and upload again.' - )} -

-
-
- ) - } - if (isTimeoutError) { - return ( - -
-

- {t('analysis.timeoutError', 'This is taking more than expected, please wait')} -

-
-
- ) - } - return ( - -
-

{statusError.message}

-
-
- ) - } - - if (!reportDataviews?.length) { - return ( -

- {t( - 'analysis.datasetsNotAllowedAll', - 'None of your datasets are allowed to be used in reports' - )}{' '} -

- ) - } - return ( -

- - {t('errors.generic', 'Something went wrong, try again or contact:')}{' '} - {SUPPORT_EMAIL} - -

- ) - }, [ - areaId, - concurrentReportError, - datasetId, - guestUser, - hasAuthError, - isSameWorkspaceReport, - isTimeoutError, - lastReport, - reportDataviews?.length, - statusError, - t, - ]) - - const ReportComponent = useMemo(() => { - if (workspaceStatus === AsyncReducerStatus.Loading) { - return - } - if (timerangeTooLong) { - return ( - -
-

- {t( - 'analysis.timeRangeTooLong', - 'The selected time range is too long, please select a shorter time range' - )} -

-
-
- ) - } - - if (reportError || (!reportLoading && !reportDataviews?.length)) { - return ReportVesselError - } - - if ( - (reportOutdated || reportStatus === AsyncReducerStatus.Idle) && - !reportLoading && - !hasAuthError - ) { - return ( - -
-

between {{start}} and {{end}}', - start: formatI18nDate(timerange?.start), - end: formatI18nDate(timerange?.end), - }), - }} - /> - -

-
- ) - } - if (reportLoaded) { - return hasVessels ? ( - - - - - ) : ( -
- {datasetsDownloadNotSupported.length > 0 && ( -

- {t( - 'analysis.datasetsNotAllowed', - 'Vessels are not included from the following sources:' - )}{' '} - {datasetsDownloadNotSupported.map((dataset, index) => ( - - - {index < datasetsDownloadNotSupported.length - 1 && ', '} - - ))} -

- )} -

{t('analysis.noDataByArea', 'No data available for the selected area')}

-
- ) - } - - return - }, [ - workspaceStatus, - timerangeTooLong, - reportError, - reportLoading, - reportDataviews?.length, - reportOutdated, - reportStatus, - hasAuthError, - reportLoaded, - t, - ReportVesselError, - timerange?.start, - timerange?.end, - dispatch, - dispatchFetchReport, - hasVessels, - activityUnit, - reportName, - datasetsDownloadNotSupported, - ]) - - return ( - - - - {ReportComponent} - - ) -} - export default function Report() { const { t } = useTranslation() const dispatch = useAppDispatch() @@ -404,6 +83,7 @@ export default function Report() { const workspaceVesselGroupsStatus = useSelector(selectWorkspaceVesselGroupsStatus) const reportArea = useSelector(selectReportArea) const hasReportBuffer = useSelector(selectHasReportBuffer) + const activityUnit = isActivityReport(reportCategory) ? 'hour' : 'detection' const fitAreaInViewport = useFitAreaInViewport() @@ -477,7 +157,10 @@ export default function Report() { {reportCategory === ReportCategory.Environment ? ( ) : ( - + + + + )}
) diff --git a/apps/fishing-map/features/reports/areas/reports.selectors.ts b/apps/fishing-map/features/reports/areas/reports.selectors.ts index aeb6b00f6e..0feb14acc9 100644 --- a/apps/fishing-map/features/reports/areas/reports.selectors.ts +++ b/apps/fishing-map/features/reports/areas/reports.selectors.ts @@ -1,9 +1,9 @@ import { createSelector } from '@reduxjs/toolkit' -import { groupBy, sum, uniq, uniqBy } from 'es-toolkit' +import { groupBy, uniqBy } from 'es-toolkit' import sumBy from 'lodash/sumBy' import { t } from 'i18next' import { FeatureCollection, MultiPolygon } from 'geojson' -import { Dataset, DatasetTypes, ReportVessel } from '@globalfishingwatch/api-types' +import { Dataset, ReportVessel } from '@globalfishingwatch/api-types' import { getGeometryDissolved, wrapGeometryBbox } from '@globalfishingwatch/data-transforms' import { selectReportAreaId, @@ -11,43 +11,26 @@ import { selectReportBufferOperation, selectReportBufferUnit, selectReportBufferValue, - selectReportVesselGraph, - selectReportCategory, } from 'features/app/selectors/app.reports.selector' import { selectAllDatasets } from 'features/datasets/datasets.slice' -import { - getDatasetsReportSupported, - getRelatedDatasetByType, -} from 'features/datasets/datasets.utils' +import { getDatasetsReportSupported } from 'features/datasets/datasets.utils' import { selectUserData } from 'features/user/selectors/user.selectors' import { getUTCDateTime } from 'utils/dates' import { getBufferedArea, getBufferedFeature, getReportCategoryFromDataview, - getVesselsFiltered, } from 'features/reports/areas/reports.utils' import { createDeepEqualSelector } from 'utils/selectors' import { EMPTY_FIELD_PLACEHOLDER, getVesselGearTypeLabel } from 'utils/info' import { sortStrings } from 'utils/shared' import { Area, AreaGeometry, selectAreas } from 'features/areas/areas.slice' -import { - EMPTY_API_VALUES, - ENTIRE_WORLD_REPORT_AREA, - MAX_CATEGORIES, - OTHERS_CATEGORY_LABEL, -} from 'features/reports/areas/reports.config' +import { EMPTY_API_VALUES, ENTIRE_WORLD_REPORT_AREA } from 'features/reports/areas/reports.config' import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' import { selectReportVesselsData, selectReportPreviewBuffer } from './report.slice' -import { - selectReportVesselFilter, - selectReportVesselPage, - selectReportResultsPerPage, - selectReportActivityGraph, - selectReportTimeComparison, -} from './reports.config.selectors' +import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' import { ReportCategory } from './reports.types' const EMPTY_ARRAY: [] = [] @@ -73,28 +56,6 @@ export type ReportVesselWithDatasets = Pick { - return reportDataviews - .map((dataview) => { - const supportedDatasets = getDatasetsReportSupported( - [dataview], - userData?.permissions || [] - ) - return { - ...dataview, - datasets: dataview.datasets?.filter((d) => supportedDatasets.includes(d.id)) as Dataset[], - filter: dataview.config?.filter || '', - ...(dataview.config?.['vessel-groups']?.length && { - vesselGroups: dataview.config?.['vessel-groups'], - }), - } - }) - .filter((dataview) => dataview.datasets!?.length > 0) - } -) - export const selectReportAreaDataviews = createSelector( [selectDataviewInstancesResolved, selectReportDatasetId], (dataviewsInstances, datasetId) => { @@ -117,7 +78,29 @@ export const selectReportAreaIds = createSelector( } ) -const selectReportActivityFlatten = createSelector( +export const selectReportDataviewsWithPermissions = createDeepEqualSelector( + [selectActiveReportDataviews, selectUserData], + (reportDataviews, userData) => { + return reportDataviews + .map((dataview) => { + const supportedDatasets = getDatasetsReportSupported( + [dataview], + userData?.permissions || [] + ) + return { + ...dataview, + datasets: dataview.datasets?.filter((d) => supportedDatasets.includes(d.id)) as Dataset[], + filter: dataview.config?.filter || '', + ...(dataview.config?.['vessel-groups']?.length && { + vesselGroups: dataview.config?.['vessel-groups'], + }), + } + }) + .filter((dataview) => dataview.datasets!?.length > 0) + } +) + +export const selectReportActivityFlatten = createSelector( [selectReportVesselsData, selectAllDatasets, selectReportDataviewsWithPermissions], (reportDatasets, datasets, dataviews) => { if (!dataviews?.length || !reportDatasets?.length) return null @@ -149,157 +132,6 @@ const selectReportActivityFlatten = createSelector( } ) -export const selectReportVesselsNumber = createSelector( - [selectReportActivityFlatten], - (vessels) => { - if (!vessels?.length) return null - - return uniqBy(vessels, (v) => v.vesselId).length - } -) - -export const selectReportVesselsHours = createSelector([selectReportActivityFlatten], (vessels) => { - if (!vessels?.length) return null - return vessels.map((vessel) => vessel?.value || 0).reduce((acc, value) => acc + value, 0) -}) - -export const selectReportVesselsList = createSelector( - [selectReportActivityFlatten, selectAllDatasets, selectReportCategory], - (vessels, datasets, reportCategory) => { - if (!vessels?.length) return null - return Object.values(groupBy(vessels, (v) => v.vesselId)) - .flatMap((vesselActivity) => { - if (vesselActivity[0]?.category !== reportCategory) return EMPTY_ARRAY - const activityDataset = datasets.find((d) => vesselActivity[0].activityDatasetId === d.id) - const infoDatasetId = getRelatedDatasetByType(activityDataset, DatasetTypes.Vessels)?.id - const infoDataset = datasets.find((d) => d.id === infoDatasetId) - const trackDatasetId = getRelatedDatasetByType( - infoDataset || activityDataset, - DatasetTypes.Tracks - )?.id - const trackDataset = datasets.find((d) => d.id === trackDatasetId) - return { - dataviewId: vesselActivity[0]?.dataviewId, - vesselId: vesselActivity[0]?.vesselId, - shipName: vesselActivity[0]?.shipName, - mmsi: vesselActivity[0]?.mmsi, - flag: vesselActivity[0]?.flag, - flagTranslated: t( - `flags:${vesselActivity[0]?.flag as string}` as any, - vesselActivity[0]?.flag - ), - flagTranslatedClean: cleanFlagState( - t(`flags:${vesselActivity[0]?.flag as string}` as any, vesselActivity[0]?.flag) - ), - geartype: cleanVesselOrGearType({ - value: vesselActivity[0]?.geartype, - property: 'geartype', - }), - vesselType: cleanVesselOrGearType({ - value: vesselActivity[0]?.vesselType, - property: 'vesselType', - }), - value: vesselActivity[0]?.value, - infoDataset, - trackDataset, - sourceColor: vesselActivity[0]?.sourceColor, - } as ReportVesselWithDatasets - }) - .sort((a, b) => (b.value as number) - (a.value as number)) - } -) - -export const selectHasReportVessels = createSelector([selectReportVesselsList], (vessels) => { - return vessels!?.length > 0 -}) - -export const selectReportVesselsListWithAllInfo = createSelector( - [selectReportActivityFlatten], - (vessels) => { - if (!vessels?.length) return null - - return Object.values(groupBy(vessels, (v) => v.vesselId)) - .map((vesselActivity) => { - return { - ...vesselActivity[0], - value: sumBy(vesselActivity, 'value'), - flagTranslated: t( - `flags:${vesselActivity[0]?.flag as string}` as any, - vesselActivity[0]?.flag - ), - flagTranslatedClean: cleanFlagState( - t(`flags:${vesselActivity[0]?.flag as string}` as any, vesselActivity[0]?.flag) - ), - geartype: getVesselGearTypeLabel({ geartypes: vesselActivity[0]?.geartype }), - vesselType: t( - `vessel.veeselTypes.${vesselActivity[0]?.vesselType}` as any, - vesselActivity[0]?.vesselType - ), - } - }) - .sort((a, b) => b.value - a.value) - } -) - -type CleanVesselOrGearTypeParams = { value: string; property: 'geartype' | 'vesselType' } -function cleanVesselOrGearType({ value, property }: CleanVesselOrGearTypeParams) { - const valuesClean = value ? value?.split(',').filter(Boolean) : [EMPTY_FIELD_PLACEHOLDER] - const valuesCleanTranslated = valuesClean - .map((value) => { - if (property === 'geartype') { - return getVesselGearTypeLabel({ geartypes: value }) - } - return t(`vessel.vesselTypes.${value?.toLowerCase()}` as any, value) - }) - .sort(sortStrings) - return valuesCleanTranslated.length > 1 - ? valuesCleanTranslated.join('|') - : valuesCleanTranslated[0] -} - -export function cleanFlagState(flagState: string) { - return flagState.replace(/,/g, '') -} - -export const selectReportVesselsFiltered = createSelector( - [selectReportVesselsList, selectReportVesselFilter], - (vessels, filter) => { - if (!vessels?.length) return null - return getVesselsFiltered(vessels, filter).sort( - (a, b) => b.value - a.value - ) - } -) - -const defaultVesselsList: ReportVesselWithDatasets[] = [] -export const selectReportVesselsPaginated = createSelector( - [selectReportVesselsFiltered, selectReportVesselPage, selectReportResultsPerPage], - (vessels, page = 0, resultsPerPage) => { - if (!vessels?.length) return defaultVesselsList - return vessels.slice(resultsPerPage * page, resultsPerPage * (page + 1)) - } -) - -export const selectReportVesselsPagination = createSelector( - [ - selectReportVesselsPaginated, - selectReportVesselsFiltered, - selectReportVesselsList, - selectReportVesselPage, - selectReportResultsPerPage, - ], - (vessels, allVesselsFiltered, allVessels, page = 0, resultsPerPage) => { - return { - page, - offset: resultsPerPage * page, - resultsPerPage, - resultsNumber: vessels!?.length, - totalFiltered: allVesselsFiltered!?.length, - total: allVessels!?.length, - } - } -) - export const selectShowTimeComparison = createSelector( [selectReportActivityGraph], (reportActivityGraph) => { @@ -328,82 +160,6 @@ export const selectTimeComparisonValues = createSelector( } ) -const selectReportVesselsGraphData = createSelector( - [selectReportVesselGraph, selectReportVesselsFiltered, selectReportDataviewsWithPermissions], - (reportGraph, vesselsFiltered, dataviews) => { - if (!vesselsFiltered?.length) return null - - const reportData = groupBy(vesselsFiltered, (v) => v.dataviewId || '') - - const dataByDataview = dataviews.map((dataview) => { - const dataviewData = reportData[dataview.id] - ? Object.values(reportData[dataview.id]).flatMap((v) => v || []) - : [] - const dataByKey = groupBy(dataviewData, (d) => d[reportGraph] || '') - return { id: dataview.id, data: dataByKey } - }) - - const allDistributionKeys = uniq(dataByDataview.flatMap(({ data }) => Object.keys(data))) - - const dataviewIds = dataviews.map((d) => d.id) - const data = allDistributionKeys - .flatMap((key) => { - const distributionData: Record = { name: key } - dataByDataview.forEach(({ id, data }) => { - distributionData[id] = (data?.[key] || []).length - }) - if (sum(dataviewIds.map((d) => distributionData[d])) === 0) return EMPTY_ARRAY - return distributionData - }) - .sort((a, b) => { - if (EMPTY_API_VALUES.includes(a.name)) return 1 - if (EMPTY_API_VALUES.includes(b.name)) return -1 - return sum(dataviewIds.map((d: any) => b[d])) - sum(dataviewIds.map((d: any) => a[d])) - }) - - return { distributionKeys: data.map((d) => d.name), data } - } -) - -export const selectReportVesselsGraphDataGrouped = createSelector( - [selectReportVesselsGraphData, selectReportDataviewsWithPermissions], - (reportGraph, dataviews) => { - if (!reportGraph?.data?.length) return null - if (reportGraph?.distributionKeys.length <= MAX_CATEGORIES) return reportGraph.data - const dataviewIds = dataviews.map((d) => d.id) - const top = reportGraph.data.slice(0, MAX_CATEGORIES) - const rest = reportGraph.data.slice(MAX_CATEGORIES) - const others = { - name: OTHERS_CATEGORY_LABEL, - ...Object.fromEntries( - dataviewIds.map((dataview) => [dataview, sum(rest.map((key: any) => key[dataview]))]) - ), - } - return [...top, others] - } -) -const defaultOthersLabel: any[] = [] -export const selectReportVesselsGraphDataOthers = createSelector( - [selectReportVesselsGraphData], - (reportGraph) => { - if (!reportGraph?.data?.length) return null - if (reportGraph?.distributionKeys.length <= MAX_CATEGORIES) return defaultOthersLabel - const others = reportGraph.data.slice(MAX_CATEGORIES) - return reportGraph.distributionKeys - .flatMap((key) => { - const other = others.find((o) => o.name === key) - if (!other) return EMPTY_ARRAY - const { name, ...rest } = other - return { name, value: sum(Object.values(rest)) } - }) - .sort((a, b) => { - if (EMPTY_API_VALUES.includes(a.name)) return 1 - if (EMPTY_API_VALUES.includes(b.name)) return -1 - return b.value - a.value - }) - } -) - const selectCurrentReportArea = createSelector( [selectReportAreaIds, selectAreas], (areaIds, areas) => { diff --git a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx index 51c312672e..0d1e0efe79 100644 --- a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx @@ -29,7 +29,10 @@ import { import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { useTimeCompareTimeDescription } from 'features/reports/areas/reports-timecomparison.hooks' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' -import { selectReportVesselsHours, selectReportVesselsNumber } from '../reports.selectors' +import { + selectReportVesselsHours, + selectReportVesselsNumber, +} from 'features/reports/activity/vessels/report-activity-vessels.selectors' import { selectReportTimeComparison } from '../reports.config.selectors' import { ReportCategory } from '../reports.types' import styles from './ReportSummary.module.css' diff --git a/apps/fishing-map/features/reports/events/VGREvents.tsx b/apps/fishing-map/features/reports/events/VGREvents.tsx index 2e7404493f..555e34f95c 100644 --- a/apps/fishing-map/features/reports/events/VGREvents.tsx +++ b/apps/fishing-map/features/reports/events/VGREvents.tsx @@ -21,7 +21,7 @@ import { selectVGREventsSubsectionDataview } from 'features/reports/vessel-group import { formatI18nDate } from 'features/i18n/i18nDate' import VGREventsVesselPropertySelector from 'features/reports/events/VGREventsVesselPropertySelector' import VGREventsVesselsTable from 'features/reports/events/VGREventsVesselsTable' -import ReportVesselsFilter from 'features/reports/areas/vessels/ReportVesselsFilter' +import ReportVesselsFilter from 'features/reports/activity/vessels/ReportVesselsFilter' import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' import { VESSEL_GROUP_ENCOUNTER_EVENTS_ID } from 'features/reports/vessel-groups/vessel-group-report.dataviews' import styles from './VGREvents.module.css' diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVessels.tsx b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVessels.tsx index 559570a26e..71c38b18d3 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVessels.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVessels.tsx @@ -2,7 +2,7 @@ import { useSelector } from 'react-redux' import { DateTime } from 'luxon' import { useTranslation } from 'react-i18next' import parse from 'html-react-parser' -import ReportVesselsFilter from 'features/reports/areas/vessels/ReportVesselsFilter' +import ReportVesselsFilter from 'features/reports/activity/vessels/ReportVesselsFilter' import { selectVGRVessels } from 'features/reports/vessel-groups/vessel-group-report.slice' import { selectVGRVesselsFlags, diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts b/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts index ab9fb84149..16b8c66ff5 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts @@ -9,7 +9,6 @@ import { selectVGRVesselPage, } from 'features/reports/vessel-groups/vessel-group.config.selectors' import { formatInfoField, getVesselGearTypeLabel, getVesselShipTypeLabel } from 'utils/info' -import { cleanFlagState } from 'features/reports/areas/reports.selectors' import { t } from 'features/i18n/i18n' import { FILTER_PROPERTIES, @@ -21,6 +20,7 @@ import { selectVGRVesselsOrderProperty, selectVGRVesselsSubsection, } from 'features/reports/vessel-groups/vessel-group.config.selectors' +import { cleanFlagState } from 'features/reports/activity/vessels/report-activity-vessels.utils' import { selectVGRVessels } from '../vessel-group-report.slice' import { VesselGroupReportVesselParsed } from './vessel-group-report-vessels.types' diff --git a/apps/fishing-map/next-env.d.ts b/apps/fishing-map/next-env.d.ts index 4f11a03dc6..a4a7b3f5cf 100644 --- a/apps/fishing-map/next-env.d.ts +++ b/apps/fishing-map/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. From 6be8c23e15b4e6aa2cde6b1d7f0cdba0e075adbd Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 16:25:43 +0200 Subject: [PATCH 05/15] use region-world for activity vessel group report --- .../features/app/selectors/app.reports.selector.ts | 9 +++++++-- .../fishing-map/features/reports/areas/report.slice.ts | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) 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 2d1d822126..a190447b69 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit' import { selectActiveDataviewsCategories } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectReportById } from 'features/reports/areas/reports.slice' import { + selectIsVesselGroupReportLocation, selectLocationAreaId, selectLocationDatasetId, selectReportId, @@ -18,6 +19,7 @@ import { selectReportVesselGraphSelector, } from 'features/reports/areas/reports.config.selectors' import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' +import { WORLD_REGION_ID } from 'features/reports/areas/report.slice' export const selectCurrentReport = createSelector( [selectReportId, (state) => state.reports], @@ -35,8 +37,11 @@ export const selectReportDatasetId = createSelector( ) export const selectReportAreaId = createSelector( - [selectLocationAreaId, selectCurrentReport], - (locationAreaId, report) => { + [selectLocationAreaId, selectCurrentReport, selectIsVesselGroupReportLocation], + (locationAreaId, report, isVesselGroupReportLocation) => { + if (isVesselGroupReportLocation) { + return WORLD_REGION_ID + } return locationAreaId || report?.areaId || '' } ) diff --git a/apps/fishing-map/features/reports/areas/report.slice.ts b/apps/fishing-map/features/reports/areas/report.slice.ts index 1194f55c4a..a33f960640 100644 --- a/apps/fishing-map/features/reports/areas/report.slice.ts +++ b/apps/fishing-map/features/reports/areas/report.slice.ts @@ -68,6 +68,8 @@ const REPORT_FIELDS_TO_INCLUDE = [ 'vesselType', ] +export const WORLD_REGION_ID = 'region-world' + export const getReportQuery = (params: FetchReportVesselsThunkParams) => { const { region, @@ -101,8 +103,12 @@ export const getReportQuery = (params: FetchReportVesselsThunkParams) => { 'spatial-resolution': spatialResolution, 'spatial-aggregation': spatialAggregation, format: format, - 'region-id': region.id, - 'region-dataset': region.dataset, + ...(region.id === WORLD_REGION_ID + ? { 'region-world': true } + : { + 'region-id': region.id, + 'region-dataset': region.dataset, + }), 'buffer-unit': reportBufferUnit?.toUpperCase(), 'buffer-value': reportBufferValue, 'buffer-operation': reportBufferOperation?.toUpperCase(), From 2708e8d4dcfe3648cde3bf6f64b64f6981a1df90 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 16:59:30 +0200 Subject: [PATCH 06/15] activity presence subsection in VGR --- .../dataviews.resolvers.selectors.ts | 6 +++ .../features/reports/areas/Report.module.css | 4 ++ .../features/reports/areas/Report.tsx | 4 +- .../vessel-groups/VesselGroupReport.tsx | 4 +- .../activity/VGRActivity.module.css | 3 ++ .../vessel-groups/activity/VGRActivity.tsx | 17 +++++++ .../VGRActivitySubsectionSelector.tsx.tsx | 49 +++++++++++++++++++ .../vessel-group-report.dataviews.ts | 4 +- .../public/locales/source/translations.json | 1 + 9 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.module.css create mode 100644 apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.tsx create mode 100644 apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts index bae0f0da64..722bae3494 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts @@ -52,6 +52,8 @@ import { } from 'features/reports/vessel-groups/vessel-group-report.dataviews' import { ReportCategory } from 'features/reports/areas/reports.types' import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' +import { FISHING_DATAVIEW_SLUG, PRESENCE_DATAVIEW_SLUG } from 'data/workspaces' const EMPTY_ARRAY: [] = [] @@ -64,6 +66,7 @@ export const selectDataviewInstancesMerged = createSelector( selectIsAnyVesselLocation, selectIsVesselLocation, selectIsVesselGroupReportLocation, + selectVGRActivitySubsection, selectReportVesselGroupId, selectVesselId, selectVesselInfoData, @@ -76,6 +79,7 @@ export const selectDataviewInstancesMerged = createSelector( isAnyVesselLocation, isVesselLocation, isVesselGroupReportLocation, + vGRActivitySubsection, reportVesselGroupId, urlVesselId, vessel @@ -132,6 +136,8 @@ export const selectDataviewInstancesMerged = createSelector( vesselGroupId: reportVesselGroupId, color: vesselGroupDataviewInstance?.config?.color, colorRamp: vesselGroupDataviewInstance?.config?.colorRamp as ColorRampId, + dataviewId: + vGRActivitySubsection === 'presence' ? PRESENCE_DATAVIEW_SLUG : FISHING_DATAVIEW_SLUG, }) if (activityVGRInstance) { mergedDataviewInstances.push(activityVGRInstance) diff --git a/apps/fishing-map/features/reports/areas/Report.module.css b/apps/fishing-map/features/reports/areas/Report.module.css index 593d0242f2..c732fb7ce4 100644 --- a/apps/fishing-map/features/reports/areas/Report.module.css +++ b/apps/fishing-map/features/reports/areas/Report.module.css @@ -1,3 +1,7 @@ +.container { + padding: var(--space-M); +} + .error { width: 100%; text-align: center; diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/Report.tsx index 25e2661860..8896a568ec 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/Report.tsx @@ -157,10 +157,10 @@ export default function Report() { {reportCategory === ReportCategory.Environment ? ( ) : ( - +
- +
)}
) diff --git a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx index a83bb79e6b..3e3f088619 100644 --- a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx @@ -13,7 +13,7 @@ import { useTimebarVisualisationConnect, } from 'features/timebar/timebar.hooks' import VGREvents from 'features/reports/events/VGREvents' -import ReportActivity from 'features/reports/activity/ReportActivity' +import VGRActivity from 'features/reports/vessel-groups/activity/VGRActivity' import { useFitAreaInViewport } from '../areas/reports.hooks' import { useFetchVesselGroupReport } from './vessel-group-report.hooks' import { selectVGRData, selectVGRStatus } from './vessel-group-report.slice' @@ -79,7 +79,7 @@ function VesselGroupReport() { { id: 'activity', title: t('common.activity', 'Activity'), - content: , + content: , }, { id: 'events', diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.module.css b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.module.css new file mode 100644 index 0000000000..3a30d691c6 --- /dev/null +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.module.css @@ -0,0 +1,3 @@ +.container { + padding: var(--space-M); +} diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.tsx new file mode 100644 index 0000000000..e457365f2e --- /dev/null +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivity.tsx @@ -0,0 +1,17 @@ +import { Fragment } from 'react' +import ReportActivity from 'features/reports/activity/ReportActivity' +import VGRActivitySubsectionSelector from 'features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx' +import styles from 'features/reports/vessel-groups/activity/VGRActivity.module.css' + +function VGRActivity() { + return ( + +
+ +
+ +
+ ) +} + +export default VGRActivity diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx new file mode 100644 index 0000000000..95a03ecd88 --- /dev/null +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx @@ -0,0 +1,49 @@ +import { useSelector } from 'react-redux' +import { useTranslation } from 'react-i18next' +import { Choice, ChoiceOption } from '@globalfishingwatch/ui-components' +import { useLocationConnect } from 'routes/routes.hook' +import { VGRActivitySubsection } from 'features/vessel-groups/vessel-groups.types' +import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' +import { useReportFeaturesLoading } from 'features/reports/areas/reports-timeseries.hooks' + +function VGRActivitySubsectionSelector() { + const { t } = useTranslation() + const { dispatchQueryParams } = useLocationConnect() + const subsection = useSelector(selectVGRActivitySubsection) + const loading = useReportFeaturesLoading() + const options: ChoiceOption[] = [ + { + id: 'fishing-effort', + label: t('common.apparentFishing', 'Apparent fishing effort'), + disabled: loading, + }, + { + id: 'presence', + label: t('common.vesselPresence', 'Vessel presence'), + disabled: loading, + }, + ] + + const onSelectSubsection = (option: ChoiceOption) => { + if (subsection !== option.id) { + // trackEvent({ + // category: TrackCategory.Analysis, + // action: `Click on ${option.id} activity graph`, + // }) + dispatchQueryParams({ vGRActivitySubsection: option.id }) + } + } + + const selectedOption = subsection ? options.find((o) => o.id === subsection) : options[0] + + return ( + + ) +} + +export default VGRActivitySubsectionSelector diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts index 7d1596e125..4165b12eab 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts @@ -105,10 +105,12 @@ export const getVesselGroupActivityDataviewInstance = ({ vesselGroupId, color, colorRamp, + dataviewId, }: { vesselGroupId: string color?: string colorRamp?: ColorRampId + dataviewId: typeof FISHING_DATAVIEW_SLUG | typeof PRESENCE_DATAVIEW_SLUG }): DataviewInstance | undefined => { if (vesselGroupId) { return { @@ -122,7 +124,7 @@ export const getVesselGroupActivityDataviewInstance = ({ 'vessel-groups': [vesselGroupId], }, }, - dataviewId: FISHING_DATAVIEW_SLUG, + dataviewId, } } } diff --git a/apps/fishing-map/public/locales/source/translations.json b/apps/fishing-map/public/locales/source/translations.json index 9d5b1cb494..9a39ddcd3d 100644 --- a/apps/fishing-map/public/locales/source/translations.json +++ b/apps/fishing-map/public/locales/source/translations.json @@ -234,6 +234,7 @@ "vessel_one": "Vessel", "vessel_many": "common.vessel", "vessel_other": "Vessels", + "vesselPresence": "Vessel presence", "vesselId": "Vessel id", "vessels": "Vessels", "view_layer": "View the layer", From 221a2e3185471b8fbc6b5d57bfb3738c125caa6f Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 17:37:03 +0200 Subject: [PATCH 07/15] move report hooks utils --- apps/fishing-map/features/map/Map.tsx | 2 +- .../features/map/TimeComparisonLegend.tsx | 2 +- .../map/popups/categories/ComparisonRow.tsx | 2 +- .../activity/ReportActivityBeforeAfter.tsx | 2 +- .../reports/activity/ReportActivityEvolution.tsx | 2 +- .../reports/activity/ReportActivityGraph.tsx | 2 +- .../activity/ReportActivityGraphSelector.tsx | 2 +- .../activity/ReportActivityPeriodComparison.tsx | 2 +- .../reports-activity-geo.utils.ts} | 0 .../reports-activity-geo.utils.workers.hooks.ts} | 6 ++++-- .../reports-activity-geo.utils.workers.ts} | 2 +- .../reports-activity-timecomparison.hooks.ts} | 7 +++++-- .../reports-activity-timeseries.hooks.ts} | 15 +++++++++------ .../reports-activity-timeseries.utils.ts} | 7 +++++-- .../fishing-map/features/reports/areas/Report.tsx | 2 +- .../areas/environment/ReportEnvironment.tsx | 2 +- .../features/reports/areas/reports.config.ts | 1 + .../reports/areas/summary/ReportSummary.tsx | 6 +++--- .../features/reports/areas/title/ReportTitle.tsx | 2 +- .../VGRActivitySubsectionSelector.tsx.tsx | 2 +- .../fishing-map/features/timebar/timebar.utils.ts | 2 +- 21 files changed, 41 insertions(+), 29 deletions(-) rename apps/fishing-map/features/reports/{areas/reports-geo.utils.ts => activity/reports-activity-geo.utils.ts} (100%) rename apps/fishing-map/features/reports/{areas/reports-geo.utils.workers.hooks.ts => activity/reports-activity-geo.utils.workers.hooks.ts} (86%) rename apps/fishing-map/features/reports/{areas/reports-geo.utils.workers.ts => activity/reports-activity-geo.utils.workers.ts} (91%) rename apps/fishing-map/features/reports/{areas/reports-timecomparison.hooks.ts => activity/reports-activity-timecomparison.hooks.ts} (98%) rename apps/fishing-map/features/reports/{areas/reports-timeseries.hooks.ts => activity/reports-activity-timeseries.hooks.ts} (96%) rename apps/fishing-map/features/reports/{areas/reports-timeseries.utils.ts => activity/reports-activity-timeseries.utils.ts} (96%) diff --git a/apps/fishing-map/features/map/Map.tsx b/apps/fishing-map/features/map/Map.tsx index 4ba46adb8c..924904c01a 100644 --- a/apps/fishing-map/features/map/Map.tsx +++ b/apps/fishing-map/features/map/Map.tsx @@ -29,7 +29,7 @@ import { useMapLayers } from 'features/map/map-layers.hooks' import MapPopups from 'features/map/popups/MapPopups' import { MapCoordinates } from 'types' import { useAppDispatch } from 'features/app/app.hooks' -import { useHasReportTimeseries } from 'features/reports/areas/reports-timeseries.hooks' +import { useHasReportTimeseries } from 'features/reports/activity/reports-activity-timeseries.hooks' import { selectReportAreaStatus } from 'features/reports/areas/reports.selectors' import { AsyncReducerStatus } from 'utils/async-slice' import { diff --git a/apps/fishing-map/features/map/TimeComparisonLegend.tsx b/apps/fishing-map/features/map/TimeComparisonLegend.tsx index d6a084f38d..254aef0c9a 100644 --- a/apps/fishing-map/features/map/TimeComparisonLegend.tsx +++ b/apps/fishing-map/features/map/TimeComparisonLegend.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { MapLegend, Tooltip, UILegend } from '@globalfishingwatch/ui-components' import { useGetDeckLayerLegend } from '@globalfishingwatch/deck-layer-composer' -import { useTimeCompareTimeDescription } from 'features/reports/areas/reports-timecomparison.hooks' +import { useTimeCompareTimeDescription } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActivityMergedDataviewId } from 'features/dataviews/selectors/dataviews.selectors' import styles from './MapLegends.module.css' diff --git a/apps/fishing-map/features/map/popups/categories/ComparisonRow.tsx b/apps/fishing-map/features/map/popups/categories/ComparisonRow.tsx index 4d3d823dab..bcf12ab744 100644 --- a/apps/fishing-map/features/map/popups/categories/ComparisonRow.tsx +++ b/apps/fishing-map/features/map/popups/categories/ComparisonRow.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import cx from 'classnames' import { FourwingsHeatmapPickingObject } from '@globalfishingwatch/deck-layers' import I18nNumber from 'features/i18n/i18nNumber' -import { useTimeCompareTimeDescription } from 'features/reports/areas/reports-timecomparison.hooks' +import { useTimeCompareTimeDescription } from 'features/reports/activity/reports-activity-timecomparison.hooks' import styles from '../Popup.module.css' type ComparisonRowProps = { diff --git a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx index ce85fb750c..21c81635e5 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx @@ -2,7 +2,7 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { InputDate, InputText, Select, SelectOption } from '@globalfishingwatch/ui-components' -import { useReportTimeCompareConnect } from 'features/reports/areas/reports-timecomparison.hooks' +import { useReportTimeCompareConnect } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx index e2701aa701..ed52caf526 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx @@ -16,7 +16,7 @@ import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' import i18n from 'features/i18n/i18n' import { formatDateForInterval, getUTCDateTime } from 'utils/dates' import { formatI18nNumber } from 'features/i18n/i18nNumber' -import { formatEvolutionData } from 'features/reports/areas/reports-timeseries.utils' +import { formatEvolutionData } from 'features/reports/activity/reports-activity-timeseries.utils' import { ReportActivityProps } from 'features/reports/activity/ReportActivityGraph' import { tickFormatter } from '../areas/reports.utils' import styles from './ReportActivityEvolution.module.css' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx index 277f084622..be9114d64b 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx @@ -9,7 +9,7 @@ import { useComputeReportTimeSeries, useReportFeaturesLoading, useReportFilteredTimeSeries, -} from 'features/reports/areas/reports-timeseries.hooks' +} from 'features/reports/activity/reports-activity-timeseries.hooks' import { selectReportArea, selectTimeComparisonValues, diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx index 6c443c4854..3bb281b95b 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx @@ -10,7 +10,7 @@ import { } from 'data/config' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' -import { useSetReportTimeComparison } from 'features/reports/areas/reports-timecomparison.hooks' +import { useSetReportTimeComparison } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectReportActivityGraph } from '../areas/reports.config.selectors' import { ReportActivityGraph } from '../areas/reports.types' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx index 6400b5a9fd..4135b8fc11 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx @@ -2,7 +2,7 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { InputDate, InputText, Select } from '@globalfishingwatch/ui-components' -import { useReportTimeCompareConnect } from 'features/reports/areas/reports-timecomparison.hooks' +import { useReportTimeCompareConnect } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' diff --git a/apps/fishing-map/features/reports/areas/reports-geo.utils.ts b/apps/fishing-map/features/reports/activity/reports-activity-geo.utils.ts similarity index 100% rename from apps/fishing-map/features/reports/areas/reports-geo.utils.ts rename to apps/fishing-map/features/reports/activity/reports-activity-geo.utils.ts diff --git a/apps/fishing-map/features/reports/areas/reports-geo.utils.workers.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.hooks.ts similarity index 86% rename from apps/fishing-map/features/reports/areas/reports-geo.utils.workers.hooks.ts rename to apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.hooks.ts index db8f3d1331..4fedf91d3f 100644 --- a/apps/fishing-map/features/reports/areas/reports-geo.utils.workers.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.hooks.ts @@ -1,11 +1,13 @@ import { useCallback, useEffect, useRef } from 'react' -import { FilterByPolygomParams, FilteredPolygons } from './reports-geo.utils' +import { FilterByPolygomParams, FilteredPolygons } from './reports-activity-geo.utils' export function useFilterCellsByPolygonWorker() { const workerRef = useRef() useEffect(() => { - workerRef.current = new Worker(new URL('./reports-geo.utils.workers.ts', import.meta.url)) + workerRef.current = new Worker( + new URL('./reports-activity-geo.utils.workers.ts', import.meta.url) + ) return () => { workerRef.current?.terminate() } diff --git a/apps/fishing-map/features/reports/areas/reports-geo.utils.workers.ts b/apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.ts similarity index 91% rename from apps/fishing-map/features/reports/areas/reports-geo.utils.workers.ts rename to apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.ts index 1bca809c56..2e138aa2ae 100644 --- a/apps/fishing-map/features/reports/areas/reports-geo.utils.workers.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-geo.utils.workers.ts @@ -1,4 +1,4 @@ -import { filterByPolygon, FilterByPolygomParams } from './reports-geo.utils' +import { filterByPolygon, FilterByPolygomParams } from './reports-activity-geo.utils' // eslint-disable-next-line no-restricted-globals addEventListener('message', (event: MessageEvent) => { diff --git a/apps/fishing-map/features/reports/areas/reports-timecomparison.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts similarity index 98% rename from apps/fishing-map/features/reports/areas/reports-timecomparison.hooks.ts rename to apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts index 1452529842..30204563f0 100644 --- a/apps/fishing-map/features/reports/areas/reports-timecomparison.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts @@ -11,8 +11,11 @@ import { getUTCDateTime } from 'utils/dates' import { formatI18nDate } from 'features/i18n/i18nDate' import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' import { MAX_DAYS_TO_COMPARE, MAX_MONTHS_TO_COMPARE } from 'features/reports/areas/reports.config' -import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' -import { ReportActivityGraph } from './reports.types' +import { + selectReportActivityGraph, + selectReportTimeComparison, +} from '../areas/reports.config.selectors' +import { ReportActivityGraph } from '../areas/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/reports/areas/reports-timeseries.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts similarity index 96% rename from apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts rename to apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts index e4b539b0cf..e078b94a1d 100644 --- a/apps/fishing-map/features/reports/areas/reports-timeseries.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts @@ -20,12 +20,12 @@ import { } from '@globalfishingwatch/deck-loaders' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' -import { FilteredPolygons } from 'features/reports/areas/reports-geo.utils' +import { FilteredPolygons } from 'features/reports/activity/reports-activity-geo.utils' import { FeaturesToTimeseriesParams, featuresToTimeseries, filterTimeseriesByTimerange, -} from 'features/reports/areas/reports-timeseries.utils' +} from 'features/reports/activity/reports-activity-timeseries.utils' import { useReportAreaInViewport } from 'features/reports/areas/reports.hooks' import { selectReportArea, @@ -34,11 +34,14 @@ import { } from 'features/reports/areas/reports.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { Area, AreaGeometry } from 'features/areas/areas.slice' -import { useFilterCellsByPolygonWorker } from 'features/reports/areas/reports-geo.utils.workers.hooks' +import { useFilterCellsByPolygonWorker } from 'features/reports/activity/reports-activity-geo.utils.workers.hooks' import { TimeRange } from 'features/timebar/timebar.slice' -import { ReportActivityGraph, ReportCategory } from './reports.types' -import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' -import { ENTIRE_WORLD_REPORT_AREA_ID } from './reports.config' +import { ReportActivityGraph, ReportCategory } from '../areas/reports.types' +import { + selectReportActivityGraph, + selectReportTimeComparison, +} from '../areas/reports.config.selectors' +import { ENTIRE_WORLD_REPORT_AREA_ID } from '../areas/reports.config' interface EvolutionGraphData { date: string diff --git a/apps/fishing-map/features/reports/areas/reports-timeseries.utils.ts b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts similarity index 96% rename from apps/fishing-map/features/reports/areas/reports-timeseries.utils.ts rename to apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts index 9cccbf3776..e99f0c65c9 100644 --- a/apps/fishing-map/features/reports/areas/reports-timeseries.utils.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts @@ -5,8 +5,11 @@ import { } from '@globalfishingwatch/deck-layers' import { FourwingsFeature, FourwingsInterval } from '@globalfishingwatch/deck-loaders' import { getUTCDate } from '@globalfishingwatch/data-transforms' -import { ReportGraphMode, ReportGraphProps } from 'features/reports/areas/reports-timeseries.hooks' -import { FilteredPolygons } from 'features/reports/areas/reports-geo.utils' +import { + ReportGraphMode, + ReportGraphProps, +} from 'features/reports/activity/reports-activity-timeseries.hooks' +import { FilteredPolygons } from 'features/reports/activity/reports-activity-geo.utils' import { DateTimeSeries } from 'features/reports/areas/reports.hooks' import { getUTCDateTime } from 'utils/dates' import { ComparisonGraphData } from 'features/reports/activity/ReportActivityPeriodComparisonGraph' diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/Report.tsx index 8896a568ec..d23d61d4f9 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/Report.tsx @@ -27,7 +27,7 @@ import { getReportCategoryFromDataview } from 'features/reports/areas/reports.ut import { resetReportData, selectReportVesselsStatus } from 'features/reports/areas/report.slice' import { useAppDispatch } from 'features/app/app.hooks' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' -import { useSetTimeseries } from 'features/reports/areas/reports-timeseries.hooks' +import { useSetTimeseries } from 'features/reports/activity/reports-activity-timeseries.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import ActivityReport from 'features/reports/activity/ReportActivity' import ReportTitle from 'features/reports/areas/title/ReportTitle' diff --git a/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx b/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx index 803ceab8ad..279683ad6e 100644 --- a/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx +++ b/apps/fishing-map/features/reports/areas/environment/ReportEnvironment.tsx @@ -11,7 +11,7 @@ import { useReportFeaturesLoading, useReportFilteredTimeSeries, useTimeseriesStats, -} from 'features/reports/areas/reports-timeseries.hooks' +} from 'features/reports/activity/reports-activity-timeseries.hooks' import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' import { getDatasetNameTranslated } from 'features/i18n/utils.datasets' import { formatI18nDate } from 'features/i18n/i18nDate' diff --git a/apps/fishing-map/features/reports/areas/reports.config.ts b/apps/fishing-map/features/reports/areas/reports.config.ts index 59e4e47085..1d4c621e90 100644 --- a/apps/fishing-map/features/reports/areas/reports.config.ts +++ b/apps/fishing-map/features/reports/areas/reports.config.ts @@ -43,6 +43,7 @@ export const DEFAULT_AREA_REPORT_STATE: AreaReportState = { } export const ENTIRE_WORLD_REPORT_AREA_ID = 'world' + export const ENTIRE_WORLD_REPORT_AREA: Area = { id: ENTIRE_WORLD_REPORT_AREA_ID, name: 'Entire World', diff --git a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx index 0d1e0efe79..356b2e93cc 100644 --- a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx @@ -14,8 +14,8 @@ import { getDatasetTitleByDataview } from 'features/datasets/datasets.utils' import { useReportFilteredTimeSeries, useReportFeaturesLoading, -} from 'features/reports/areas/reports-timeseries.hooks' -import { formatEvolutionData } from 'features/reports/areas/reports-timeseries.utils' +} from 'features/reports/activity/reports-activity-timeseries.hooks' +import { formatEvolutionData } from 'features/reports/activity/reports-activity-timeseries.utils' import { AsyncReducerStatus } from 'utils/async-slice' import { formatI18nNumber } from 'features/i18n/i18nNumber' import ReportSummaryPlaceholder from 'features/reports/areas/placeholders/ReportSummaryPlaceholder' @@ -27,7 +27,7 @@ import { selectReportVesselsDateRangeHash, } from 'features/reports/areas/report.slice' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' -import { useTimeCompareTimeDescription } from 'features/reports/areas/reports-timecomparison.hooks' +import { useTimeCompareTimeDescription } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportVesselsHours, diff --git a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx index 0b38ba518b..c70e9aac49 100644 --- a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx +++ b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx @@ -38,7 +38,7 @@ import { BufferOperation, BufferUnit } from 'types' import { cleanCurrentWorkspaceStateBufferParams } from 'features/workspace/workspace.slice' import { AsyncReducerStatus } from 'utils/async-slice' import { formatI18nNumber } from 'features/i18n/i18nNumber' -import { useReportFeaturesLoading } from 'features/reports/areas/reports-timeseries.hooks' +import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks' import { useHighlightReportArea } from '../reports.hooks' import { BufferButtonTooltip } from './BufferButonTooltip' import styles from './ReportTitle.module.css' diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx index 95a03ecd88..0d299588e0 100644 --- a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx @@ -4,7 +4,7 @@ import { Choice, ChoiceOption } from '@globalfishingwatch/ui-components' import { useLocationConnect } from 'routes/routes.hook' import { VGRActivitySubsection } from 'features/vessel-groups/vessel-groups.types' import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' -import { useReportFeaturesLoading } from 'features/reports/areas/reports-timeseries.hooks' +import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks' function VGRActivitySubsectionSelector() { const { t } = useTranslation() diff --git a/apps/fishing-map/features/timebar/timebar.utils.ts b/apps/fishing-map/features/timebar/timebar.utils.ts index e31b913d3f..9d76aba5a8 100644 --- a/apps/fishing-map/features/timebar/timebar.utils.ts +++ b/apps/fishing-map/features/timebar/timebar.utils.ts @@ -9,7 +9,7 @@ import { } from '@globalfishingwatch/deck-loaders' import { getDateInIntervalResolution } from '@globalfishingwatch/deck-layers' import { getUTCDateTime } from 'utils/dates' -import { FeaturesToTimeseriesParams } from 'features/reports/areas/reports-timeseries.utils' +import { FeaturesToTimeseriesParams } from 'features/reports/activity/reports-activity-timeseries.utils' type GetGraphDataFromFourwingsFeaturesParams = Pick< FeaturesToTimeseriesParams, From 8697e64f2366fd3a82ab8c7177c27464e05c3a08 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 18:17:20 +0200 Subject: [PATCH 08/15] move reports slice to activity section --- .../features/app/selectors/app.reports.selector.ts | 2 +- .../features/map/popups/categories/ContextLayersRow.tsx | 2 +- .../features/reports/activity/ReportActivity.tsx | 2 +- .../reports-activity.slice.ts} | 0 apps/fishing-map/features/reports/areas/Report.tsx | 5 ++++- apps/fishing-map/features/reports/areas/reports.hooks.ts | 3 ++- .../features/reports/areas/reports.selectors.ts | 9 ++++----- .../features/reports/areas/summary/ReportSummary.tsx | 2 +- .../features/reports/areas/title/ReportTitle.tsx | 2 +- apps/fishing-map/features/sidebar/SidebarHeader.tsx | 2 +- apps/fishing-map/features/user/UserReports.tsx | 2 +- apps/fishing-map/reducers.ts | 2 +- 12 files changed, 18 insertions(+), 15 deletions(-) rename apps/fishing-map/features/reports/{areas/report.slice.ts => activity/reports-activity.slice.ts} (100%) 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 a190447b69..ce9b77434c 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -19,7 +19,7 @@ import { selectReportVesselGraphSelector, } from 'features/reports/areas/reports.config.selectors' import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' -import { WORLD_REGION_ID } from 'features/reports/areas/report.slice' +import { WORLD_REGION_ID } from 'features/reports/activity/reports-activity.slice' export const selectCurrentReport = createSelector( [selectReportId, (state) => state.reports], diff --git a/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx b/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx index eecf5cac06..78bf2ef540 100644 --- a/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx +++ b/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx @@ -23,7 +23,7 @@ import { import { selectSidebarOpen } from 'features/app/selectors/app.selectors' import { getAreaIdFromFeature } from 'features/map/popups/categories/ContextLayers.hooks' import { resetSidebarScroll } from 'features/sidebar/sidebar.utils' -import { resetReportData } from 'features/reports/areas/report.slice' +import { resetReportData } from 'features/reports/activity/reports-activity.slice' import { useAppDispatch } from 'features/app/app.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { diff --git a/apps/fishing-map/features/reports/activity/ReportActivity.tsx b/apps/fishing-map/features/reports/activity/ReportActivity.tsx index 93adb79733..cb1b4500c6 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivity.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivity.tsx @@ -28,7 +28,7 @@ import { getDateRangeHash, selectReportVesselsDateRangeHash, setDateRangeHash, -} from 'features/reports/areas/report.slice' +} from 'features/reports/activity/reports-activity.slice' import { useAppDispatch } from 'features/app/app.hooks' import { selectReportAreaId, diff --git a/apps/fishing-map/features/reports/areas/report.slice.ts b/apps/fishing-map/features/reports/activity/reports-activity.slice.ts similarity index 100% rename from apps/fishing-map/features/reports/areas/report.slice.ts rename to apps/fishing-map/features/reports/activity/reports-activity.slice.ts diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/Report.tsx index d23d61d4f9..053f8b2862 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/Report.tsx @@ -24,7 +24,10 @@ import { useTimebarVisualisationConnect, } from 'features/timebar/timebar.hooks' import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' -import { resetReportData, selectReportVesselsStatus } from 'features/reports/areas/report.slice' +import { + resetReportData, + selectReportVesselsStatus, +} from 'features/reports/activity/reports-activity.slice' import { useAppDispatch } from 'features/app/app.hooks' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { useSetTimeseries } from 'features/reports/activity/reports-activity-timeseries.hooks' diff --git a/apps/fishing-map/features/reports/areas/reports.hooks.ts b/apps/fishing-map/features/reports/areas/reports.hooks.ts index 393c77d49a..1ebc934b5c 100644 --- a/apps/fishing-map/features/reports/areas/reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/reports.hooks.ts @@ -38,7 +38,8 @@ import { selectReportVesselsData, selectReportVesselsError, selectReportVesselsStatus, -} from './report.slice' +} from 'features/reports/activity/reports-activity.slice' +import { selectVGRActivityDataview } from '../vessel-groups/vessel-group-report.selectors' export type DateTimeSeries = { date: string diff --git a/apps/fishing-map/features/reports/areas/reports.selectors.ts b/apps/fishing-map/features/reports/areas/reports.selectors.ts index 0feb14acc9..96f228fc9d 100644 --- a/apps/fishing-map/features/reports/areas/reports.selectors.ts +++ b/apps/fishing-map/features/reports/areas/reports.selectors.ts @@ -1,6 +1,4 @@ import { createSelector } from '@reduxjs/toolkit' -import { groupBy, uniqBy } from 'es-toolkit' -import sumBy from 'lodash/sumBy' import { t } from 'i18next' import { FeatureCollection, MultiPolygon } from 'geojson' import { Dataset, ReportVessel } from '@globalfishingwatch/api-types' @@ -22,14 +20,15 @@ import { getReportCategoryFromDataview, } from 'features/reports/areas/reports.utils' import { createDeepEqualSelector } from 'utils/selectors' -import { EMPTY_FIELD_PLACEHOLDER, getVesselGearTypeLabel } from 'utils/info' -import { sortStrings } from 'utils/shared' import { Area, AreaGeometry, selectAreas } from 'features/areas/areas.slice' import { EMPTY_API_VALUES, ENTIRE_WORLD_REPORT_AREA } from 'features/reports/areas/reports.config' import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' -import { selectReportVesselsData, selectReportPreviewBuffer } from './report.slice' +import { + selectReportVesselsData, + selectReportPreviewBuffer, +} from '../activity/reports-activity.slice' import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' import { ReportCategory } from './reports.types' diff --git a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx index 356b2e93cc..09123b98eb 100644 --- a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx @@ -25,7 +25,7 @@ import { listAsSentence } from 'utils/shared' import { getDateRangeHash, selectReportVesselsDateRangeHash, -} from 'features/reports/areas/report.slice' +} from 'features/reports/activity/reports-activity.slice' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { useTimeCompareTimeDescription } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' diff --git a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx index c70e9aac49..c71d34591f 100644 --- a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx +++ b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx @@ -19,7 +19,7 @@ import { resetReportData, selectReportPreviewBuffer, setPreviewBuffer, -} from 'features/reports/areas/report.slice' +} from 'features/reports/activity/reports-activity.slice' import { selectReportArea, selectReportAreaDataviews, diff --git a/apps/fishing-map/features/sidebar/SidebarHeader.tsx b/apps/fishing-map/features/sidebar/SidebarHeader.tsx index 29193feb0c..605d345c5c 100644 --- a/apps/fishing-map/features/sidebar/SidebarHeader.tsx +++ b/apps/fishing-map/features/sidebar/SidebarHeader.tsx @@ -48,7 +48,7 @@ import { selectSearchOption, selectSearchQuery } from 'features/search/search.co import LoginButtonWrapper from 'routes/LoginButtonWrapper' import { resetSidebarScroll } from 'features/sidebar/sidebar.utils' import { useAppDispatch } from 'features/app/app.hooks' -import { resetReportData } from 'features/reports/areas/report.slice' +import { resetReportData } from 'features/reports/activity/reports-activity.slice' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectReportsStatus } from 'features/reports/areas/reports.slice' import { selectCurrentReport } from 'features/app/selectors/app.reports.selector' diff --git a/apps/fishing-map/features/user/UserReports.tsx b/apps/fishing-map/features/user/UserReports.tsx index 1b90739e69..92aa8d145a 100644 --- a/apps/fishing-map/features/user/UserReports.tsx +++ b/apps/fishing-map/features/user/UserReports.tsx @@ -15,7 +15,7 @@ import { } from 'features/reports/areas/reports.slice' import { REPORT } from 'routes/routes' import { selectUserReports } from 'features/user/selectors/user.permissions.selectors' -import { resetReportData } from 'features/reports/areas/report.slice' +import { resetReportData } from 'features/reports/activity/reports-activity.slice' import { resetWorkspaceSlice } from 'features/workspace/workspace.slice' import styles from './User.module.css' diff --git a/apps/fishing-map/reducers.ts b/apps/fishing-map/reducers.ts index 695e1b01b3..3409029c1c 100644 --- a/apps/fishing-map/reducers.ts +++ b/apps/fishing-map/reducers.ts @@ -19,7 +19,7 @@ import mapControlsReducer from 'features/map/controls/map-controls.slice' import mapReducer from 'features/map/map.slice' import modalsReducer from 'features/modals/modals.slice' import regionsReducer from 'features/regions/regions.slice' -import reportReducer from 'features/reports/areas/report.slice' +import reportReducer from 'features/reports/activity/reports-activity.slice' import reportsReducer from 'features/reports/areas/reports.slice' import resourcesReducer from 'features/resources/resources.slice' import searchReducer from 'features/search/search.slice' From 1f85f8b3b6a97d763ba95cfad8181ae5ff63d4e1 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 18:29:36 +0200 Subject: [PATCH 09/15] fitBounds vessel group extent in profile --- .../reports/activity/ReportActivityGraph.tsx | 20 +++----- .../features/reports/areas/reports.hooks.ts | 48 +++++++++++++++++-- .../VGRActivitySubsectionSelector.tsx.tsx | 7 +++ .../vessel-group-report.selectors.ts | 14 ++++-- apps/fishing-map/queries/stats-api.ts | 21 +++++--- 5 files changed, 81 insertions(+), 29 deletions(-) diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx index be9114d64b..45c5f260c3 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx @@ -10,18 +10,14 @@ import { useReportFeaturesLoading, useReportFilteredTimeSeries, } from 'features/reports/activity/reports-activity-timeseries.hooks' -import { - selectReportArea, - selectTimeComparisonValues, -} from 'features/reports/areas/reports.selectors' +import { selectTimeComparisonValues } from 'features/reports/areas/reports.selectors' import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison' import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph' import UserGuideLink from 'features/help/UserGuideLink' -import { AsyncReducerStatus } from 'utils/async-slice' +import { useFitAreaInViewport, useReportAreaBounds } from 'features/reports/areas/reports.hooks' import { selectReportActivityGraph } from '../areas/reports.config.selectors' import { ReportActivityGraph } from '../areas/reports.types' -import { useFetchReportArea, useFitAreaInViewport } from '../areas/reports.hooks' import ReportActivityEvolution from './ReportActivityEvolution' import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' @@ -48,21 +44,17 @@ const emptyGraphData = {} as ReportGraphProps export default function ReportActivity() { useComputeReportTimeSeries() - const reportArea = useSelector(selectReportArea) - const { status } = useFetchReportArea() const fitAreaInViewport = useFitAreaInViewport() + const { loaded, bbox } = useReportAreaBounds() // This ensures that the area is in viewport when then area load finishes useEffect(() => { - if (status === AsyncReducerStatus.Finished && reportArea?.bounds) { - requestAnimationFrame(() => { - fitAreaInViewport() - }) + if (loaded && bbox?.length) { + fitAreaInViewport() } - // Reacting only to the area status and fitting bounds after load // eslint-disable-next-line react-hooks/exhaustive-deps - }, [status, reportArea]) + }, [loaded]) const { t } = useTranslation() const { start, end } = useTimerangeConnect() diff --git a/apps/fishing-map/features/reports/areas/reports.hooks.ts b/apps/fishing-map/features/reports/areas/reports.hooks.ts index 1ebc934b5c..ea1b9a477b 100644 --- a/apps/fishing-map/features/reports/areas/reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/reports.hooks.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo } from 'react' import { useSelector } from 'react-redux' +import { useGetStatsByDataviewQuery } from 'queries/stats-api' import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' import { Dataset, Dataview } from '@globalfishingwatch/api-types' import { useLocalStorage } from '@globalfishingwatch/react-hooks' @@ -21,6 +22,7 @@ import { selectReportArea, selectReportAreaDataviews, selectReportAreaIds, + selectReportAreaStatus, selectReportDataviewsWithPermissions, } from 'features/reports/areas/reports.selectors' import { useDeckMap } from 'features/map/map-context.hooks' @@ -30,7 +32,7 @@ import { FIT_BOUNDS_REPORT_PADDING } from 'data/config' import { RFMO_DATAVIEW_SLUG } from 'data/workspaces' import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' -import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' +import { selectIsVesselGroupReportLocation, selectUrlTimeRange } from 'routes/routes.selectors' import { AsyncReducerStatus } from 'utils/async-slice' import { fetchReportVesselsThunk, @@ -79,10 +81,47 @@ function useReportAreaCenter(bounds?: Bbox) { }, [bounds, map]) } +function useVesselGroupReportBounds() { + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) + const dataview = useSelector(selectVGRActivityDataview)! + const urlTimeRange = useSelector(selectUrlTimeRange) + const { + data: stats, + isFetching, + isSuccess, + } = useGetStatsByDataviewQuery( + { + dataview, + timerange: urlTimeRange as any, + fields: [], + }, + { + skip: !isVesselGroupReportLocation || !dataview || !urlTimeRange, + } + ) + + const statsBbox = stats && ([stats.minLon, stats.minLat, stats.maxLon, stats.maxLat] as Bbox) + return { + loaded: !isFetching && isSuccess, + bbox: statsBbox?.some((v) => v === null || v === undefined) ? null : statsBbox!, + } +} + +export function useReportAreaBounds() { + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) + const { loaded, bbox } = useVesselGroupReportBounds() + const area = useSelector(selectReportArea) + const areaStatus = useSelector(selectReportAreaStatus) + const areaBbox = isVesselGroupReportLocation ? bbox : area?.geometry?.bbox || area!?.bounds + return { + loaded: isVesselGroupReportLocation ? loaded : areaStatus === AsyncReducerStatus.Finished, + bbox: areaBbox, + } +} + export function useReportAreaInViewport() { const viewState = useMapViewState() - const area = useSelector(selectReportArea) - const bbox = area?.geometry?.bbox || area!?.bounds + const { bbox } = useReportAreaBounds() const areaCenter = useReportAreaCenter(bbox as Bbox) return ( viewState?.latitude === areaCenter?.latitude && @@ -93,8 +132,7 @@ export function useReportAreaInViewport() { export function useFitAreaInViewport() { const setMapCoordinates = useSetMapCoordinates() - const area = useSelector(selectReportArea) - const bbox = area?.geometry?.bbox || area!?.bounds + const { bbox } = useReportAreaBounds() const areaCenter = useReportAreaCenter(bbox as Bbox) const areaInViewport = useReportAreaInViewport() return useCallback(() => { diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx index 0d299588e0..33330f6c54 100644 --- a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx @@ -5,12 +5,17 @@ import { useLocationConnect } from 'routes/routes.hook' import { VGRActivitySubsection } from 'features/vessel-groups/vessel-groups.types' import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks' +import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' +import { resetReportData } from 'features/reports/activity/reports-activity.slice' +import { useAppDispatch } from 'features/app/app.hooks' function VGRActivitySubsectionSelector() { const { t } = useTranslation() + const dispatch = useAppDispatch() const { dispatchQueryParams } = useLocationConnect() const subsection = useSelector(selectVGRActivitySubsection) const loading = useReportFeaturesLoading() + const fitAreaInViewport = useFitAreaInViewport() const options: ChoiceOption[] = [ { id: 'fishing-effort', @@ -31,6 +36,8 @@ function VGRActivitySubsectionSelector() { // action: `Click on ${option.id} activity graph`, // }) dispatchQueryParams({ vGRActivitySubsection: option.id }) + fitAreaInViewport() + dispatch(resetReportData()) } } diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts index 2e502c6641..4f321bc7d2 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.selectors.ts @@ -5,7 +5,7 @@ import { VesselGroupInsightParams, } from 'queries/vessel-insight-api' import { RootState } from 'reducers' -import { InsightType } from '@globalfishingwatch/api-types' +import { DataviewCategory, InsightType } from '@globalfishingwatch/api-types' import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectReportVesselGroupId } from 'routes/routes.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' @@ -28,13 +28,21 @@ export const IUU_INSIGHT_ID = 'VESSEL-IDENTITY-IUU-VESSEL-LIST' as InsightType export const FLAG_CHANGE_INSIGHT_ID = 'VESSEL-IDENTITY-FLAG-CHANGES' as InsightType export const MOU_INSIGHT_ID = 'VESSEL-IDENTITY-MOU-LIST' as InsightType -export const selectVGRDataview = createSelector( +export const selectAllVGRDataviews = createSelector( [selectDataviewInstancesResolvedVisible, selectReportVesselGroupId], (dataviews, reportVesselGroupId) => { - return dataviews?.find((dataview) => dataviewHasVesselGroupId(dataview, reportVesselGroupId)) + return dataviews?.filter((dataview) => dataviewHasVesselGroupId(dataview, reportVesselGroupId)) } ) +export const selectVGRDataview = createSelector([selectAllVGRDataviews], (dataviews) => { + return dataviews?.find((dataview) => dataview.category === DataviewCategory.VesselGroups) +}) + +export const selectVGRActivityDataview = createSelector([selectAllVGRDataviews], (dataviews) => { + return dataviews?.find((dataview) => dataview.category === DataviewCategory.Activity) +}) + export const selectVGREventsSubsectionDataview = createSelector( [ selectEventsDataviews, diff --git a/apps/fishing-map/queries/stats-api.ts b/apps/fishing-map/queries/stats-api.ts index c41bee7c24..15bbf84ea9 100644 --- a/apps/fishing-map/queries/stats-api.ts +++ b/apps/fishing-map/queries/stats-api.ts @@ -5,13 +5,13 @@ import { uniq } from 'es-toolkit' import { DateTime } from 'luxon' import type { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client' import { getDatasetsExtent } from '@globalfishingwatch/datasets-client' -import { StatField, StatFields, StatType, StatsParams } from '@globalfishingwatch/api-types' +import { StatFields, StatType, StatsParams } from '@globalfishingwatch/api-types' import type { TimeRange } from 'features/timebar/timebar.slice' type FetchDataviewStatsParams = { timerange: TimeRange dataview: UrlDataviewInstance - fields?: StatField[] + fields?: StatsParams[] } interface CustomBaseQueryArg extends FetchBaseQueryArgs { @@ -26,7 +26,6 @@ const serializeStatsDataviewKey: SerializeQueryArgs = ({ que ].join('-') } -const DEFAULT_STATS_FIELDS: StatsParams[] = ['VESSEL-IDS', 'FLAGS'] // Define a service using a base URL and expected endpoints export const dataviewStatsApi = createApi({ reducerPath: 'dataviewStatsApi', @@ -36,7 +35,7 @@ export const dataviewStatsApi = createApi({ endpoints: (builder) => ({ getStatsByDataview: builder.query({ serializeQueryArgs: serializeStatsDataviewKey, - query: ({ dataview, timerange, fields = DEFAULT_STATS_FIELDS }) => { + query: ({ dataview, timerange, fields }) => { const datasets = dataview.datasets?.filter((dataset) => dataview.config?.datasets?.includes(dataset.id) ) @@ -52,17 +51,25 @@ export const dataviewStatsApi = createApi({ ) const statsParams = { datasets: [dataview.config?.datasets?.join(',') || []], - filters: [dataview.config?.filter] || [], - 'vessel-groups': [dataview.config?.['vessel-groups']] || [], 'date-range': [laterStartDate.toISODate(), earlierEndDate.toISODate()].join(','), + ...(fields?.length && { + fields: fields.join(','), + }), + ...(dataview.config?.filter && { filters: [dataview.config?.filter] || [] }), + ...(dataview.config?.['vessel-groups'] && { + 'vessel-groups': [dataview.config?.['vessel-groups']] || [], + }), } return { - url: `?fields=${fields.join(',')}&${stringify(statsParams, { + url: `?${stringify(statsParams, { arrayFormat: 'indices', })}`, } }, transformResponse: (response: StatFields[], meta, args) => { + if (!args.fields?.length) { + return response?.[0] + } const units = uniq(args?.dataview?.datasets?.flatMap((d) => d.unit || []) || []) if (units.length > 1) { console.warn('Incompatible datasets stats unit, using the first type', units[0]) From 348e935bb24524260ad6f4e0ce3e703646f81bb9 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 18:47:45 +0200 Subject: [PATCH 10/15] rename reports to area-reports --- apps/fishing-map/features/app/App.tsx | 4 ++-- .../features/app/selectors/app.reports.selector.ts | 6 +++--- .../app/selectors/app.workspace.selectors.ts | 2 +- .../selectors/dataviews.instances.selectors.ts | 2 +- .../selectors/dataviews.resolvers.selectors.ts | 4 ++-- .../dataviews/selectors/dataviews.selectors.ts | 4 ++-- apps/fishing-map/features/map/Map.tsx | 2 +- apps/fishing-map/features/map/controls/MapInfo.tsx | 2 +- apps/fishing-map/features/map/map-layers.hooks.ts | 2 +- apps/fishing-map/features/map/map.selectors.ts | 2 +- .../map/popups/categories/ContextLayersRow.tsx | 2 +- .../features/reports/activity/ReportActivity.tsx | 13 ++++++++----- .../reports/activity/ReportActivityBeforeAfter.tsx | 9 ++++++--- .../activity/ReportActivityBeforeAfterGraph.tsx | 6 +++--- .../reports/activity/ReportActivityEvolution.tsx | 2 +- .../reports/activity/ReportActivityGraph.tsx | 11 +++++++---- .../activity/ReportActivityGraphSelector.tsx | 6 +++--- .../activity/ReportActivityPeriodComparison.tsx | 9 ++++++--- .../ReportActivityPeriodComparisonGraph.tsx | 8 ++++++-- .../reports/activity/download/ReportDownload.tsx | 5 ++++- .../reports-activity-timecomparison.hooks.ts | 11 +++++++---- .../activity/reports-activity-timeseries.hooks.ts | 10 +++++----- .../activity/reports-activity-timeseries.utils.ts | 2 +- .../reports/activity/vessels/ReportVessels.tsx | 8 ++++---- .../activity/vessels/ReportVesselsGraph.tsx | 6 +++--- .../vessels/ReportVesselsGraphSelector.tsx | 2 +- .../activity/vessels/ReportVesselsTable.tsx | 6 +++--- .../activity/vessels/ReportVesselsTableFooter.tsx | 6 +++--- .../vessels/report-activity-vessels.selectors.ts | 8 ++++---- .../{Report.module.css => AreaReport.module.css} | 0 .../reports/areas/{Report.tsx => AreaReport.tsx} | 13 ++++++++----- ...al.module.css => NewAreaReportModal.module.css} | 0 .../{NewReportModal.tsx => NewAreaReportModal.tsx} | 6 +++--- ...lectors.ts => area-reports.config.selectors.ts} | 4 ++-- .../{reports.config.ts => area-reports.config.ts} | 2 +- .../{reports.hooks.ts => area-reports.hooks.ts} | 7 +++++-- ...orts.selectors.ts => area-reports.selectors.ts} | 14 ++++++++++---- .../{reports.slice.ts => area-reports.slice.ts} | 0 .../{reports.types.ts => area-reports.types.ts} | 0 .../{reports.utils.ts => area-reports.utils.ts} | 6 +++--- .../reports/areas/summary/ReportSummary.tsx | 8 ++++---- .../reports/areas/title/BufferButonTooltip.tsx | 4 ++-- .../features/reports/areas/title/ReportTitle.tsx | 6 +++--- .../features/reports/events/VGREventsGraph.tsx | 2 +- .../reports/events/vgr-events.selectors.ts | 2 +- .../reports/vessel-groups/VesselGroupReport.tsx | 2 +- .../activity/VGRActivitySubsectionSelector.tsx.tsx | 2 +- .../vessels/VesselGroupReportVesselsGraph.tsx | 2 +- .../vessels/VesselGroupReportVesselsTable.tsx | 2 +- .../vessel-group-report-vessels.selectors.ts | 2 +- apps/fishing-map/features/sidebar/Sidebar.tsx | 2 +- .../fishing-map/features/sidebar/SidebarHeader.tsx | 9 +++++---- apps/fishing-map/features/timebar/Timebar.tsx | 2 +- apps/fishing-map/features/timebar/timebar.hooks.ts | 2 +- .../features/timebar/timebar.selectors.ts | 2 +- apps/fishing-map/features/user/UserReports.tsx | 2 +- .../user/selectors/user.permissions.selectors.ts | 2 +- .../features/vessel-groups/vessel-groups.hooks.ts | 2 +- .../features/workspace/workspace.slice.ts | 4 ++-- apps/fishing-map/reducers.ts | 2 +- apps/fishing-map/types/index.ts | 2 +- 61 files changed, 155 insertions(+), 120 deletions(-) rename apps/fishing-map/features/reports/areas/{Report.module.css => AreaReport.module.css} (100%) rename apps/fishing-map/features/reports/areas/{Report.tsx => AreaReport.tsx} (94%) rename apps/fishing-map/features/reports/areas/{NewReportModal.module.css => NewAreaReportModal.module.css} (100%) rename apps/fishing-map/features/reports/areas/{NewReportModal.tsx => NewAreaReportModal.tsx} (97%) rename apps/fishing-map/features/reports/areas/{reports.config.selectors.ts => area-reports.config.selectors.ts} (91%) rename apps/fishing-map/features/reports/areas/{reports.config.ts => area-reports.config.ts} (97%) rename apps/fishing-map/features/reports/areas/{reports.hooks.ts => area-reports.hooks.ts} (98%) rename apps/fishing-map/features/reports/areas/{reports.selectors.ts => area-reports.selectors.ts} (96%) rename apps/fishing-map/features/reports/areas/{reports.slice.ts => area-reports.slice.ts} (100%) rename apps/fishing-map/features/reports/areas/{reports.types.ts => area-reports.types.ts} (100%) rename apps/fishing-map/features/reports/areas/{reports.utils.ts => area-reports.utils.ts} (98%) diff --git a/apps/fishing-map/features/app/App.tsx b/apps/fishing-map/features/app/App.tsx index 633e899679..44eb7b0097 100644 --- a/apps/fishing-map/features/app/App.tsx +++ b/apps/fishing-map/features/app/App.tsx @@ -33,7 +33,7 @@ import { import { fetchUserThunk } from 'features/user/user.slice' import { fetchHighlightWorkspacesThunk } from 'features/workspaces-list/workspaces-list.slice' import { AsyncReducerStatus } from 'utils/async-slice' -import { selectShowTimeComparison } from 'features/reports/areas/reports.selectors' +import { selectShowTimeComparison } from 'features/reports/areas/area-reports.selectors' import { DEFAULT_WORKSPACE_ID } from 'data/workspaces' import { HOME, @@ -59,7 +59,7 @@ 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/reports/areas/reports.config.selectors' +import { selectReportAreaBounds } from 'features/reports/areas/area-reports.config.selectors' import { useAppDispatch } from './app.hooks' import { selectReadOnly, selectSidebarOpen } from './selectors/app.selectors' import { useAnalytics } from './analytics.hooks' 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 ce9b77434c..18412b7483 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -1,6 +1,6 @@ import { createSelector } from '@reduxjs/toolkit' import { selectActiveDataviewsCategories } from 'features/dataviews/selectors/dataviews.resolvers.selectors' -import { selectReportById } from 'features/reports/areas/reports.slice' +import { selectReportById } from 'features/reports/areas/area-reports.slice' import { selectIsVesselGroupReportLocation, selectLocationAreaId, @@ -17,8 +17,8 @@ import { selectReportBufferValueSelector, selectReportCategorySelector, selectReportVesselGraphSelector, -} from 'features/reports/areas/reports.config.selectors' -import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' +} from 'features/reports/areas/area-reports.config.selectors' +import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/area-reports.types' import { WORLD_REGION_ID } from 'features/reports/activity/reports-activity.slice' export const selectCurrentReport = createSelector( 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 fd6609bc26..7bb7a0e2e1 100644 --- a/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts +++ b/apps/fishing-map/features/app/selectors/app.workspace.selectors.ts @@ -37,7 +37,7 @@ import { selectReportTimeComparison, selectReportVesselFilter, selectReportVesselPage, -} from 'features/reports/areas/reports.config.selectors' +} from 'features/reports/areas/area-reports.config.selectors' const selectWorkspaceReportState = createSelector( [ 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 de5dfef0b4..18769230df 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.instances.selectors.ts @@ -13,7 +13,7 @@ import { import { createDeepEqualSelector } from 'utils/selectors' import { getReportVesselGroupVisibleDataviews } from 'features/reports/vessel-groups/vessel-group-report.dataviews' import { REPORT_ONLY_VISIBLE_LAYERS } from 'data/config' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { selectVGRSection, selectVGRSubsection, diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts index 722bae3494..cb9285adcc 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts @@ -50,8 +50,8 @@ import { getVesselGroupDataviewInstance, getVesselGroupEventsDataviewInstances, } from 'features/reports/vessel-groups/vessel-group-report.dataviews' -import { ReportCategory } from 'features/reports/areas/reports.types' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { ReportCategory } from 'features/reports/areas/area-reports.types' +import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' import { FISHING_DATAVIEW_SLUG, PRESENCE_DATAVIEW_SLUG } from 'data/workspaces' diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts index 7976cee105..b07f05bf21 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.selectors.ts @@ -25,8 +25,8 @@ import { selectVGRSubsection, } from 'features/reports/vessel-groups/vessel-group.config.selectors' import { getReportVesselGroupVisibleDataviews } from 'features/reports/vessel-groups/vessel-group-report.dataviews' -import { ReportCategory } from 'features/reports/areas/reports.types' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { ReportCategory } from 'features/reports/areas/area-reports.types' +import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { selectActiveActivityDataviews, selectActiveContextAreasDataviews, diff --git a/apps/fishing-map/features/map/Map.tsx b/apps/fishing-map/features/map/Map.tsx index 924904c01a..5d931d570d 100644 --- a/apps/fishing-map/features/map/Map.tsx +++ b/apps/fishing-map/features/map/Map.tsx @@ -30,7 +30,7 @@ import MapPopups from 'features/map/popups/MapPopups' import { MapCoordinates } from 'types' import { useAppDispatch } from 'features/app/app.hooks' import { useHasReportTimeseries } from 'features/reports/activity/reports-activity-timeseries.hooks' -import { selectReportAreaStatus } from 'features/reports/areas/reports.selectors' +import { selectReportAreaStatus } from 'features/reports/areas/area-reports.selectors' import { AsyncReducerStatus } from 'utils/async-slice' import { MAP_VIEW, diff --git a/apps/fishing-map/features/map/controls/MapInfo.tsx b/apps/fishing-map/features/map/controls/MapInfo.tsx index dfbc6fdc5b..3af30df850 100644 --- a/apps/fishing-map/features/map/controls/MapInfo.tsx +++ b/apps/fishing-map/features/map/controls/MapInfo.tsx @@ -6,7 +6,7 @@ import { DateTime, DateTimeFormatOptions } from 'luxon' import { toFixed } from 'utils/shared' import { useTimerangeConnect } from 'features/timebar/timebar.hooks' import I18nDate from 'features/i18n/i18nDate' -import { selectShowTimeComparison } from 'features/reports/areas/reports.selectors' +import { selectShowTimeComparison } from 'features/reports/areas/area-reports.selectors' import styles from './MapInfo.module.css' import MapScaleControl from './MapScaleControl' diff --git a/apps/fishing-map/features/map/map-layers.hooks.ts b/apps/fishing-map/features/map/map-layers.hooks.ts index 7bb720d828..4dd7f543bb 100644 --- a/apps/fishing-map/features/map/map-layers.hooks.ts +++ b/apps/fishing-map/features/map/map-layers.hooks.ts @@ -39,7 +39,7 @@ import { selectDebugOptions } from 'features/debug/debug.slice' import { selectShowTimeComparison, selectTimeComparisonValues, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { useTimerangeConnect } from 'features/timebar/timebar.hooks' import { selectHighlightedTime, selectHighlightedEvents } from 'features/timebar/timebar.slice' import { useLocationConnect } from 'routes/routes.hook' diff --git a/apps/fishing-map/features/map/map.selectors.ts b/apps/fishing-map/features/map/map.selectors.ts index cf14b28247..6a7ebf8467 100644 --- a/apps/fishing-map/features/map/map.selectors.ts +++ b/apps/fishing-map/features/map/map.selectors.ts @@ -11,7 +11,7 @@ import { import { selectReportPreviewBufferFeature, selectReportBufferFeature, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { WorkspaceCategory } from 'data/workspaces' import { BUFFER_PREVIEW_COLOR } from 'data/config' import { selectAllDatasets } from 'features/datasets/datasets.slice' diff --git a/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx b/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx index 78bf2ef540..7bbc4b14a2 100644 --- a/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx +++ b/apps/fishing-map/features/map/popups/categories/ContextLayersRow.tsx @@ -30,7 +30,7 @@ import { DEFAULT_BUFFER_OPERATION, DEFAULT_POINT_BUFFER_UNIT, DEFAULT_POINT_BUFFER_VALUE, -} from 'features/reports/areas/reports.config' +} from 'features/reports/areas/area-reports.config' import { cleanCurrentWorkspaceReportState } from 'features/workspace/workspace.slice' import styles from '../Popup.module.css' diff --git a/apps/fishing-map/features/reports/activity/ReportActivity.tsx b/apps/fishing-map/features/reports/activity/ReportActivity.tsx index cb1b4500c6..27853af497 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivity.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivity.tsx @@ -18,12 +18,12 @@ import { } from 'features/dataviews/selectors/dataviews.selectors' import { WorkspaceLoginError } from 'features/workspace/WorkspaceError' import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' -import { selectReportDataviewsWithPermissions } from 'features/reports/areas/reports.selectors' +import { selectReportDataviewsWithPermissions } from 'features/reports/areas/area-reports.selectors' import { selectHasReportVessels } from 'features/reports/activity/vessels/report-activity-vessels.selectors' import ReportVesselsPlaceholder from 'features/reports/areas/placeholders/ReportVesselsPlaceholder' import { getDownloadReportSupported } from 'features/download/download.utils' import { SUPPORT_EMAIL } from 'data/config' -import { parseReportUrl } from 'features/reports/areas/reports.utils' +import { parseReportUrl } from 'features/reports/areas/area-reports.utils' import { getDateRangeHash, selectReportVesselsDateRangeHash, @@ -38,15 +38,18 @@ import { import { formatI18nDate } from 'features/i18n/i18nDate' import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' import DatasetLabel from 'features/datasets/DatasetLabel' -import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' +import { + LAST_REPORTS_STORAGE_KEY, + LastReportStorage, +} from 'features/reports/areas/area-reports.config' // import { REPORT_BUFFER_GENERATOR_ID } from 'features/map/map.config' import { selectIsGuestUser, selectUserData } from 'features/user/selectors/user.selectors' import { useFetchDataviewResources } from 'features/resources/resources.hooks' import ReportActivityGraph from 'features/reports/activity/ReportActivityGraph' -import { useFetchReportVessel } from 'features/reports/areas/reports.hooks' +import { useFetchReportVessel } from 'features/reports/areas/area-reports.hooks' import ReportVessels from 'features/reports/activity/vessels/ReportVessels' import ReportDownload from 'features/reports/activity/download/ReportDownload' -import styles from 'features/reports/areas/Report.module.css' +import styles from 'features/reports/areas/AreaReport.module.css' export type ReportActivityUnit = 'hour' | 'detection' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx index 21c81635e5..139a6f9130 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfter.tsx @@ -5,11 +5,14 @@ import { InputDate, InputText, Select, SelectOption } from '@globalfishingwatch/ import { useReportTimeCompareConnect } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' -import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' +import { selectReportAreaIds } from 'features/reports/areas/area-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/reports/areas/reports.config' -import { selectReportTimeComparison } from '../areas/reports.config.selectors' +import { + MAX_MONTHS_TO_COMPARE, + MAX_DAYS_TO_COMPARE, +} from 'features/reports/areas/area-reports.config' +import { selectReportTimeComparison } from '../areas/area-reports.config.selectors' import styles from './ReportActivityBeforeAfter.module.css' export default function ReportActivityBeforeAfter() { diff --git a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfterGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfterGraph.tsx index f18b89ebe5..2f63326720 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfterGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityBeforeAfterGraph.tsx @@ -16,11 +16,11 @@ import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' 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/reports/areas/reports.utils' +import { formatDate, tickFormatter } from 'features/reports/areas/area-reports.utils' import { formatI18nNumber } from 'features/i18n/i18nNumber' import { toFixed } from 'utils/shared' -import { selectReportTimeComparison } from '../areas/reports.config.selectors' -import { ReportActivityTimeComparison } from '../areas/reports.types' +import { selectReportTimeComparison } from '../areas/area-reports.config.selectors' +import { ReportActivityTimeComparison } from '../areas/area-reports.types' import styles from './ReportActivityEvolution.module.css' interface ComparisonGraphData { diff --git a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx index ed52caf526..47eb8c0ca7 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityEvolution.tsx @@ -18,7 +18,7 @@ import { formatDateForInterval, getUTCDateTime } from 'utils/dates' import { formatI18nNumber } from 'features/i18n/i18nNumber' import { formatEvolutionData } from 'features/reports/activity/reports-activity-timeseries.utils' import { ReportActivityProps } from 'features/reports/activity/ReportActivityGraph' -import { tickFormatter } from '../areas/reports.utils' +import { tickFormatter } from '../areas/area-reports.utils' import styles from './ReportActivityEvolution.module.css' type ReportGraphTooltipProps = { diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx index 45c5f260c3..459e827237 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx @@ -10,14 +10,17 @@ import { useReportFeaturesLoading, useReportFilteredTimeSeries, } from 'features/reports/activity/reports-activity-timeseries.hooks' -import { selectTimeComparisonValues } from 'features/reports/areas/reports.selectors' +import { selectTimeComparisonValues } from 'features/reports/areas/area-reports.selectors' import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder' import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison' import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph' import UserGuideLink from 'features/help/UserGuideLink' -import { useFitAreaInViewport, useReportAreaBounds } from 'features/reports/areas/reports.hooks' -import { selectReportActivityGraph } from '../areas/reports.config.selectors' -import { ReportActivityGraph } from '../areas/reports.types' +import { + useFitAreaInViewport, + useReportAreaBounds, +} from 'features/reports/areas/area-reports.hooks' +import { selectReportActivityGraph } from '../areas/area-reports.config.selectors' +import { ReportActivityGraph } from '../areas/area-reports.types' import ReportActivityEvolution from './ReportActivityEvolution' import ReportActivityBeforeAfter from './ReportActivityBeforeAfter' import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph' diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx index 3bb281b95b..1d08971808 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraphSelector.tsx @@ -9,11 +9,11 @@ import { REPORT_ACTIVITY_GRAPH_PERIOD_COMPARISON, } from 'data/config' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' -import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' +import { useFitAreaInViewport } from 'features/reports/areas/area-reports.hooks' import { useSetReportTimeComparison } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { selectReportActivityGraph } from '../areas/reports.config.selectors' -import { ReportActivityGraph } from '../areas/reports.types' +import { selectReportActivityGraph } from '../areas/area-reports.config.selectors' +import { ReportActivityGraph } from '../areas/area-reports.types' type ReportActivityGraphSelectorProps = { loading: boolean diff --git a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx index 4135b8fc11..8cdef7fd2c 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx @@ -5,13 +5,16 @@ import { InputDate, InputText, Select } from '@globalfishingwatch/ui-components' import { useReportTimeCompareConnect } from 'features/reports/activity/reports-activity-timecomparison.hooks' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { getSourcesSelectedInDataview } from 'features/workspace/activity/activity.utils' -import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' +import { selectReportAreaIds } from 'features/reports/areas/area-reports.selectors' import { selectDatasetAreaDetail } from 'features/areas/areas.slice' 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/reports/areas/reports.config' -import { selectReportTimeComparison } from '../areas/reports.config.selectors' +import { + MAX_MONTHS_TO_COMPARE, + MAX_DAYS_TO_COMPARE, +} from 'features/reports/areas/area-reports.config' +import { selectReportTimeComparison } from '../areas/area-reports.config.selectors' import styles from './ReportActivityBeforeAfter.module.css' export default function ReportActivityGraph() { diff --git a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparisonGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparisonGraph.tsx index bd3378ac02..22b8d9ea7e 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparisonGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparisonGraph.tsx @@ -17,9 +17,13 @@ import { selectLatestAvailableDataDate } from 'features/app/selectors/app.select 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/reports/areas/reports.utils' +import { + formatDate, + formatTooltipValue, + tickFormatter, +} from 'features/reports/areas/area-reports.utils' import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info' -import { selectReportTimeComparison } from '../areas/reports.config.selectors' +import { selectReportTimeComparison } from '../areas/area-reports.config.selectors' import styles from './ReportActivityEvolution.module.css' const DIFFERENCE = 'difference' diff --git a/apps/fishing-map/features/reports/activity/download/ReportDownload.tsx b/apps/fishing-map/features/reports/activity/download/ReportDownload.tsx index fc34ed82d4..f41baf33a3 100644 --- a/apps/fishing-map/features/reports/activity/download/ReportDownload.tsx +++ b/apps/fishing-map/features/reports/activity/download/ReportDownload.tsx @@ -4,7 +4,10 @@ import { Button } from '@globalfishingwatch/ui-components' import { setDownloadActivityAreaKey } from 'features/download/downloadActivity.slice' import { useAppDispatch } from 'features/app/app.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { selectReportAreaIds, selectReportAreaName } from 'features/reports/areas/reports.selectors' +import { + selectReportAreaIds, + selectReportAreaName, +} from 'features/reports/areas/area-reports.selectors' import styles from './ReportDownload.module.css' export default function ReportDownload() { diff --git a/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts index 30204563f0..ac0b056b5e 100644 --- a/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timecomparison.hooks.ts @@ -9,13 +9,16 @@ 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/reports/areas/reports.hooks' -import { MAX_DAYS_TO_COMPARE, MAX_MONTHS_TO_COMPARE } from 'features/reports/areas/reports.config' +import { useFitAreaInViewport } from 'features/reports/areas/area-reports.hooks' +import { + MAX_DAYS_TO_COMPARE, + MAX_MONTHS_TO_COMPARE, +} from 'features/reports/areas/area-reports.config' import { selectReportActivityGraph, selectReportTimeComparison, -} from '../areas/reports.config.selectors' -import { ReportActivityGraph } from '../areas/reports.types' +} from '../areas/area-reports.config.selectors' +import { ReportActivityGraph } from '../areas/area-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/reports/activity/reports-activity-timeseries.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts index e078b94a1d..1ca84b55b8 100644 --- a/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts @@ -26,22 +26,22 @@ import { featuresToTimeseries, filterTimeseriesByTimerange, } from 'features/reports/activity/reports-activity-timeseries.utils' -import { useReportAreaInViewport } from 'features/reports/areas/reports.hooks' +import { useReportAreaInViewport } from 'features/reports/areas/area-reports.hooks' import { selectReportArea, selectReportBufferHash, selectShowTimeComparison, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import { Area, AreaGeometry } from 'features/areas/areas.slice' import { useFilterCellsByPolygonWorker } from 'features/reports/activity/reports-activity-geo.utils.workers.hooks' import { TimeRange } from 'features/timebar/timebar.slice' -import { ReportActivityGraph, ReportCategory } from '../areas/reports.types' +import { ReportActivityGraph, ReportCategory } from '../areas/area-reports.types' import { selectReportActivityGraph, selectReportTimeComparison, -} from '../areas/reports.config.selectors' -import { ENTIRE_WORLD_REPORT_AREA_ID } from '../areas/reports.config' +} from '../areas/area-reports.config.selectors' +import { ENTIRE_WORLD_REPORT_AREA_ID } from '../areas/area-reports.config' interface EvolutionGraphData { date: string diff --git a/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts index e99f0c65c9..02860703b7 100644 --- a/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.utils.ts @@ -10,7 +10,7 @@ import { ReportGraphProps, } from 'features/reports/activity/reports-activity-timeseries.hooks' import { FilteredPolygons } from 'features/reports/activity/reports-activity-geo.utils' -import { DateTimeSeries } from 'features/reports/areas/reports.hooks' +import { DateTimeSeries } from 'features/reports/areas/area-reports.hooks' import { getUTCDateTime } from 'utils/dates' import { ComparisonGraphData } from 'features/reports/activity/ReportActivityPeriodComparisonGraph' import { getGraphDataFromFourwingsHeatmap } from 'features/timebar/timebar.utils' diff --git a/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx index 016b0b301b..9ad93ee3f1 100644 --- a/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVessels.tsx @@ -5,11 +5,11 @@ import ReportVesselsGraphSelector from 'features/reports/activity/vessels/Report import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/reports/areas/summary/ReportSummaryTags' -import { FIELDS, getCommonProperties } from 'features/reports/areas/reports.utils' +import { FIELDS, getCommonProperties } from 'features/reports/areas/area-reports.utils' import { PROPERTIES_EXCLUDED } from 'features/reports/areas/summary/ReportSummary' -import { ReportActivityUnit } from 'features/reports/areas/Report' -import { selectReportVesselFilter } from 'features/reports/areas/reports.config.selectors' -import { ReportCategory } from 'features/reports/areas/reports.types' +import { ReportActivityUnit } from 'features/reports/areas/AreaReport' +import { selectReportVesselFilter } from 'features/reports/areas/area-reports.config.selectors' +import { ReportCategory } from 'features/reports/areas/area-reports.types' import ReportVesselsGraph from './ReportVesselsGraph' import ReportVesselsFilter from './ReportVesselsFilter' import ReportVesselsTable from './ReportVesselsTable' diff --git a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx index 7521d54adb..131e6ce11c 100644 --- a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraph.tsx @@ -13,15 +13,15 @@ import { REPORT_VESSELS_GRAPH_GEARTYPE, REPORT_VESSELS_GRAPH_VESSELTYPE, } from 'data/config' -import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/reports.config' +import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/area-reports.config' import { getVesselGearTypeLabel } from 'utils/info' -import { ReportVesselGraph } from 'features/reports/areas/reports.types' +import { ReportVesselGraph } from 'features/reports/areas/area-reports.types' import { selectReportVesselsGraphDataGrouped, selectReportVesselsGraphDataOthers, } from 'features/reports/activity/vessels/report-activity-vessels.selectors' import { cleanFlagState } from 'features/reports/activity/vessels/report-activity-vessels.utils' -import { selectReportDataviewsWithPermissions } from 'features/reports/areas/reports.selectors' +import { selectReportDataviewsWithPermissions } from 'features/reports/areas/area-reports.selectors' import styles from './ReportVesselsGraph.module.css' const MAX_OTHER_TOOLTIP_ITEMS = 10 diff --git a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx index f1eb4eb8bc..89ff75cff2 100644 --- a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsGraphSelector.tsx @@ -11,7 +11,7 @@ import { import { selectReportVesselGraph } from 'features/app/selectors/app.reports.selector' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' -import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/reports.types' +import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/area-reports.types' export default function ReportVesselsGraphSelector() { const { dispatchQueryParams } = useLocationConnect() diff --git a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx index 82a85df3db..14bbb10bfc 100644 --- a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTable.tsx @@ -10,13 +10,13 @@ import { selectReportCategory } from 'features/app/selectors/app.reports.selecto import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectUserData } from 'features/user/selectors/user.selectors' import DatasetLabel from 'features/datasets/DatasetLabel' -import { EMPTY_API_VALUES } from 'features/reports/areas/reports.config' +import { EMPTY_API_VALUES } from 'features/reports/areas/area-reports.config' import VesselLink from 'features/vessel/VesselLink' import VesselPin from 'features/vessel/VesselPin' import { GLOBAL_VESSELS_DATASET_ID } from 'data/workspaces' -import { ReportActivityUnit } from 'features/reports/areas/Report' +import { ReportActivityUnit } from 'features/reports/areas/AreaReport' import { selectReportVesselsPaginated } from 'features/reports/activity/vessels/report-activity-vessels.selectors' -import { ReportCategory } from 'features/reports/areas/reports.types' +import { ReportCategory } from 'features/reports/areas/area-reports.types' import ReportVesselsTableFooter from 'features/reports/activity/vessels/ReportVesselsTableFooter' import styles from 'features/reports/activity/vessels/ReportVesselsTable.module.css' diff --git a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx index 15be5c32cd..f3bf986ed7 100644 --- a/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx +++ b/apps/fishing-map/features/reports/activity/vessels/ReportVesselsTableFooter.tsx @@ -17,15 +17,15 @@ import { } from 'features/vessel-groups/vessel-groups.slice' import { selectActiveActivityAndDetectionsDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { selectReportVesselFilter } from 'features/reports/areas/reports.config.selectors' +import { selectReportVesselFilter } from 'features/reports/areas/area-reports.config.selectors' import { selectReportAreaName, ReportVesselWithDatasets, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { parseReportVesselsToIdentity, getVesselsFiltered, -} from 'features/reports/areas/reports.utils' +} from 'features/reports/areas/area-reports.utils' import styles from './ReportVesselsTableFooter.module.css' import { selectReportVesselsListWithAllInfo, diff --git a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts index 41a8d5bb88..ece0c5e14b 100644 --- a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts +++ b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts @@ -8,22 +8,22 @@ import { } from 'features/app/selectors/app.reports.selector' import { selectAllDatasets } from 'features/datasets/datasets.slice' import { getRelatedDatasetByType } from 'features/datasets/datasets.utils' -import { getVesselsFiltered } from 'features/reports/areas/reports.utils' +import { getVesselsFiltered } from 'features/reports/areas/area-reports.utils' import { EMPTY_API_VALUES, MAX_CATEGORIES, OTHERS_CATEGORY_LABEL, -} from 'features/reports/areas/reports.config' +} from 'features/reports/areas/area-reports.config' import { ReportVesselWithDatasets, selectReportActivityFlatten, selectReportDataviewsWithPermissions, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { selectReportResultsPerPage, selectReportVesselFilter, selectReportVesselPage, -} from 'features/reports/areas/reports.config.selectors' +} from 'features/reports/areas/area-reports.config.selectors' import { cleanFlagState, cleanVesselOrGearType, diff --git a/apps/fishing-map/features/reports/areas/Report.module.css b/apps/fishing-map/features/reports/areas/AreaReport.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/Report.module.css rename to apps/fishing-map/features/reports/areas/AreaReport.module.css diff --git a/apps/fishing-map/features/reports/areas/Report.tsx b/apps/fishing-map/features/reports/areas/AreaReport.tsx similarity index 94% rename from apps/fishing-map/features/reports/areas/Report.tsx rename to apps/fishing-map/features/reports/areas/AreaReport.tsx index 053f8b2862..9cce20e3b3 100644 --- a/apps/fishing-map/features/reports/areas/Report.tsx +++ b/apps/fishing-map/features/reports/areas/AreaReport.tsx @@ -17,13 +17,13 @@ import { selectHasReportBuffer, selectReportArea, selectReportAreaStatus, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { TimebarVisualisations } from 'types' import { useTimebarEnvironmentConnect, useTimebarVisualisationConnect, } from 'features/timebar/timebar.hooks' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { resetReportData, selectReportVesselsStatus, @@ -34,11 +34,14 @@ import { useSetTimeseries } from 'features/reports/activity/reports-activity-tim import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import ActivityReport from 'features/reports/activity/ReportActivity' import ReportTitle from 'features/reports/areas/title/ReportTitle' -import { ReportCategory } from 'features/reports/areas/reports.types' +import { ReportCategory } from 'features/reports/areas/area-reports.types' import ReportSummary from 'features/reports/areas/summary/ReportSummary' import ReportEnvironment from 'features/reports/areas/environment/ReportEnvironment' -import { useFitAreaInViewport, useHighlightReportArea } from 'features/reports/areas/reports.hooks' -import styles from 'features/reports/areas/Report.module.css' +import { + useFitAreaInViewport, + useHighlightReportArea, +} from 'features/reports/areas/area-reports.hooks' +import styles from 'features/reports/areas/AreaReport.module.css' export type ReportActivityUnit = 'hour' | 'detection' diff --git a/apps/fishing-map/features/reports/areas/NewReportModal.module.css b/apps/fishing-map/features/reports/areas/NewAreaReportModal.module.css similarity index 100% rename from apps/fishing-map/features/reports/areas/NewReportModal.module.css rename to apps/fishing-map/features/reports/areas/NewAreaReportModal.module.css diff --git a/apps/fishing-map/features/reports/areas/NewReportModal.tsx b/apps/fishing-map/features/reports/areas/NewAreaReportModal.tsx similarity index 97% rename from apps/fishing-map/features/reports/areas/NewReportModal.tsx rename to apps/fishing-map/features/reports/areas/NewAreaReportModal.tsx index 9ff346e347..8e4b1e67d4 100644 --- a/apps/fishing-map/features/reports/areas/NewReportModal.tsx +++ b/apps/fishing-map/features/reports/areas/NewAreaReportModal.tsx @@ -12,15 +12,15 @@ import { } from '@globalfishingwatch/api-types' import { useAppDispatch } from 'features/app/app.hooks' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' +import { selectReportAreaIds } from 'features/reports/areas/area-reports.selectors' import { selectDatasetAreaDetail } from 'features/areas/areas.slice' -import { createReportThunk, updateReportThunk } from 'features/reports/areas/reports.slice' +import { createReportThunk, updateReportThunk } from 'features/reports/areas/area-reports.slice' import { selectPrivateDatasetsInWorkspace } from 'features/dataviews/selectors/dataviews.selectors' import { ROOT_DOM_ELEMENT } from 'data/config' import { selectWorkspaceWithCurrentState } from 'features/app/selectors/app.workspace.selectors' import { AsyncError } from 'utils/async-slice' import { getViewAccessOptions } from 'features/workspace/save/workspace-save.utils' -import styles from './NewReportModal.module.css' +import styles from './NewAreaReportModal.module.css' type NewReportModalProps = { title?: string diff --git a/apps/fishing-map/features/reports/areas/reports.config.selectors.ts b/apps/fishing-map/features/reports/areas/area-reports.config.selectors.ts similarity index 91% rename from apps/fishing-map/features/reports/areas/reports.config.selectors.ts rename to apps/fishing-map/features/reports/areas/area-reports.config.selectors.ts index abc6a1940f..f1a8dfd268 100644 --- a/apps/fishing-map/features/reports/areas/reports.config.selectors.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.config.selectors.ts @@ -1,7 +1,7 @@ import { createSelector } from '@reduxjs/toolkit' import { selectLocationQuery } from 'routes/routes.selectors' -import { AreaReportState, AreaReportStateProperty } from './reports.types' -import { DEFAULT_AREA_REPORT_STATE } from './reports.config' +import { AreaReportState, AreaReportStateProperty } from './area-reports.types' +import { DEFAULT_AREA_REPORT_STATE } from './area-reports.config' type AreaReportProperty

= Required[P] function selectAreaReportStateProperty

(property: P) { diff --git a/apps/fishing-map/features/reports/areas/reports.config.ts b/apps/fishing-map/features/reports/areas/area-reports.config.ts similarity index 97% rename from apps/fishing-map/features/reports/areas/reports.config.ts rename to apps/fishing-map/features/reports/areas/area-reports.config.ts index 1d4c621e90..43175c5ce4 100644 --- a/apps/fishing-map/features/reports/areas/reports.config.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.config.ts @@ -5,7 +5,7 @@ import { } from 'data/config' import { BufferUnit, BufferOperation } from 'types' import { Area, AreaGeometry } from 'features/areas/areas.slice' -import { AreaReportState } from './reports.types' +import { AreaReportState } from './area-reports.types' export const REPORT_BUFFER_FEATURE_ID: string = 'buffer' export const DEFAULT_BUFFER_VALUE: number = 50 diff --git a/apps/fishing-map/features/reports/areas/reports.hooks.ts b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts similarity index 98% rename from apps/fishing-map/features/reports/areas/reports.hooks.ts rename to apps/fishing-map/features/reports/areas/area-reports.hooks.ts index ea1b9a477b..b7b2984f05 100644 --- a/apps/fishing-map/features/reports/areas/reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts @@ -24,14 +24,17 @@ import { selectReportAreaIds, selectReportAreaStatus, selectReportDataviewsWithPermissions, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import { useDeckMap } from 'features/map/map-context.hooks' import { Bbox } from 'types' import { useSetMapCoordinates, useMapViewState } from 'features/map/map-viewport.hooks' import { FIT_BOUNDS_REPORT_PADDING } from 'data/config' import { RFMO_DATAVIEW_SLUG } from 'data/workspaces' import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' -import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config' +import { + LAST_REPORTS_STORAGE_KEY, + LastReportStorage, +} from 'features/reports/areas/area-reports.config' import { selectIsVesselGroupReportLocation, selectUrlTimeRange } from 'routes/routes.selectors' import { AsyncReducerStatus } from 'utils/async-slice' import { diff --git a/apps/fishing-map/features/reports/areas/reports.selectors.ts b/apps/fishing-map/features/reports/areas/area-reports.selectors.ts similarity index 96% rename from apps/fishing-map/features/reports/areas/reports.selectors.ts rename to apps/fishing-map/features/reports/areas/area-reports.selectors.ts index 96f228fc9d..91cec98518 100644 --- a/apps/fishing-map/features/reports/areas/reports.selectors.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.selectors.ts @@ -18,10 +18,13 @@ import { getBufferedArea, getBufferedFeature, getReportCategoryFromDataview, -} from 'features/reports/areas/reports.utils' +} from 'features/reports/areas/area-reports.utils' import { createDeepEqualSelector } from 'utils/selectors' import { Area, AreaGeometry, selectAreas } from 'features/areas/areas.slice' -import { EMPTY_API_VALUES, ENTIRE_WORLD_REPORT_AREA } from 'features/reports/areas/reports.config' +import { + EMPTY_API_VALUES, + ENTIRE_WORLD_REPORT_AREA, +} from 'features/reports/areas/area-reports.config' import { selectDataviewInstancesResolved } from 'features/dataviews/selectors/dataviews.resolvers.selectors' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors' @@ -29,8 +32,11 @@ import { selectReportVesselsData, selectReportPreviewBuffer, } from '../activity/reports-activity.slice' -import { selectReportActivityGraph, selectReportTimeComparison } from './reports.config.selectors' -import { ReportCategory } from './reports.types' +import { + selectReportActivityGraph, + selectReportTimeComparison, +} from './area-reports.config.selectors' +import { ReportCategory } from './area-reports.types' const EMPTY_ARRAY: [] = [] diff --git a/apps/fishing-map/features/reports/areas/reports.slice.ts b/apps/fishing-map/features/reports/areas/area-reports.slice.ts similarity index 100% rename from apps/fishing-map/features/reports/areas/reports.slice.ts rename to apps/fishing-map/features/reports/areas/area-reports.slice.ts diff --git a/apps/fishing-map/features/reports/areas/reports.types.ts b/apps/fishing-map/features/reports/areas/area-reports.types.ts similarity index 100% rename from apps/fishing-map/features/reports/areas/reports.types.ts rename to apps/fishing-map/features/reports/areas/area-reports.types.ts diff --git a/apps/fishing-map/features/reports/areas/reports.utils.ts b/apps/fishing-map/features/reports/areas/area-reports.utils.ts similarity index 98% rename from apps/fishing-map/features/reports/areas/reports.utils.ts rename to apps/fishing-map/features/reports/areas/area-reports.utils.ts index ce1f272469..5d2c916cfa 100644 --- a/apps/fishing-map/features/reports/areas/reports.utils.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.utils.ts @@ -28,9 +28,9 @@ import { DEFAULT_POINT_BUFFER_VALUE, DIFFERENCE, REPORT_BUFFER_FEATURE_ID, -} from './reports.config' -import { ReportVesselWithDatasets } from './reports.selectors' -import { ReportCategory } from './reports.types' +} from './area-reports.config' +import { ReportVesselWithDatasets } from './area-reports.selectors' +import { ReportCategory } from './area-reports.types' const ALWAYS_SHOWN_FILTERS = ['vessel-groups'] diff --git a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx index 09123b98eb..be6e94ab12 100644 --- a/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx +++ b/apps/fishing-map/features/reports/areas/summary/ReportSummary.tsx @@ -8,8 +8,8 @@ import { Locale } from '@globalfishingwatch/api-types' import { formatI18nDate } from 'features/i18n/i18nDate' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import ReportSummaryTags from 'features/reports/areas/summary/ReportSummaryTags' -import { FIELDS, getCommonProperties } from 'features/reports/areas/reports.utils' -import { ReportActivityUnit } from 'features/reports/areas/Report' +import { FIELDS, getCommonProperties } from 'features/reports/areas/area-reports.utils' +import { ReportActivityUnit } from 'features/reports/areas/AreaReport' import { getDatasetTitleByDataview } from 'features/datasets/datasets.utils' import { useReportFilteredTimeSeries, @@ -33,8 +33,8 @@ import { selectReportVesselsHours, selectReportVesselsNumber, } from 'features/reports/activity/vessels/report-activity-vessels.selectors' -import { selectReportTimeComparison } from '../reports.config.selectors' -import { ReportCategory } from '../reports.types' +import { selectReportTimeComparison } from '../area-reports.config.selectors' +import { ReportCategory } from '../area-reports.types' import styles from './ReportSummary.module.css' type ReportSummaryProps = { diff --git a/apps/fishing-map/features/reports/areas/title/BufferButonTooltip.tsx b/apps/fishing-map/features/reports/areas/title/BufferButonTooltip.tsx index 4287c14e56..fd63f62092 100644 --- a/apps/fishing-map/features/reports/areas/title/BufferButonTooltip.tsx +++ b/apps/fishing-map/features/reports/areas/title/BufferButonTooltip.tsx @@ -8,10 +8,10 @@ import { NAUTICAL_MILES, DISSOLVE, DIFFERENCE, -} from 'features/reports/areas/reports.config' +} from 'features/reports/areas/area-reports.config' import { BufferOperation, BufferUnit } from 'types' import { BUFFER_PREVIEW_COLOR } from 'data/config' -import { selectReportPreviewBufferFeature } from 'features/reports/areas/reports.selectors' +import { selectReportPreviewBufferFeature } from 'features/reports/areas/area-reports.selectors' import styles from './ReportTitle.module.css' type BufferButonTooltipProps = { diff --git a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx index c71d34591f..f089f769a2 100644 --- a/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx +++ b/apps/fishing-map/features/reports/areas/title/ReportTitle.tsx @@ -14,7 +14,7 @@ import { DEFAULT_BUFFER_OPERATION, DEFAULT_BUFFER_VALUE, NAUTICAL_MILES, -} from 'features/reports/areas/reports.config' +} from 'features/reports/areas/area-reports.config' import { resetReportData, selectReportPreviewBuffer, @@ -24,7 +24,7 @@ import { selectReportArea, selectReportAreaDataviews, selectReportAreaStatus, -} from 'features/reports/areas/reports.selectors' +} from 'features/reports/areas/area-reports.selectors' import ReportTitlePlaceholder from 'features/reports/areas/placeholders/ReportTitlePlaceholder' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' import { @@ -39,7 +39,7 @@ import { cleanCurrentWorkspaceStateBufferParams } from 'features/workspace/works import { AsyncReducerStatus } from 'utils/async-slice' import { formatI18nNumber } from 'features/i18n/i18nNumber' import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks' -import { useHighlightReportArea } from '../reports.hooks' +import { useHighlightReportArea } from '../area-reports.hooks' import { BufferButtonTooltip } from './BufferButonTooltip' import styles from './ReportTitle.module.css' diff --git a/apps/fishing-map/features/reports/events/VGREventsGraph.tsx b/apps/fishing-map/features/reports/events/VGREventsGraph.tsx index 8a0d88f157..551e44b605 100644 --- a/apps/fishing-map/features/reports/events/VGREventsGraph.tsx +++ b/apps/fishing-map/features/reports/events/VGREventsGraph.tsx @@ -16,7 +16,7 @@ import { FourwingsInterval } from '@globalfishingwatch/deck-loaders' import i18n from 'features/i18n/i18n' import { formatDateForInterval, getUTCDateTime } from 'utils/dates' import { formatI18nNumber } from 'features/i18n/i18nNumber' -import { tickFormatter } from 'features/reports/areas/reports.utils' +import { tickFormatter } from 'features/reports/areas/area-reports.utils' import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' import styles from './VGREventsGraph.module.css' diff --git a/apps/fishing-map/features/reports/events/vgr-events.selectors.ts b/apps/fishing-map/features/reports/events/vgr-events.selectors.ts index 629ced9e44..f8f25a0444 100644 --- a/apps/fishing-map/features/reports/events/vgr-events.selectors.ts +++ b/apps/fishing-map/features/reports/events/vgr-events.selectors.ts @@ -13,7 +13,7 @@ import { selectVGREventsVesselFilter, selectVGREventsVesselPage, } from 'features/reports/vessel-groups/vessel-group.config.selectors' -import { getVesselsFiltered } from 'features/reports/areas/reports.utils' +import { getVesselsFiltered } from 'features/reports/areas/area-reports.utils' import { REPORT_FILTER_PROPERTIES } from 'features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors' import { selectVGREventsSubsectionDataview } from 'features/reports/vessel-groups/vessel-group-report.selectors' diff --git a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx index 3e3f088619..15e12c74a9 100644 --- a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx @@ -14,7 +14,7 @@ import { } from 'features/timebar/timebar.hooks' import VGREvents from 'features/reports/events/VGREvents' import VGRActivity from 'features/reports/vessel-groups/activity/VGRActivity' -import { useFitAreaInViewport } from '../areas/reports.hooks' +import { useFitAreaInViewport } from '../areas/area-reports.hooks' import { useFetchVesselGroupReport } from './vessel-group-report.hooks' import { selectVGRData, selectVGRStatus } from './vessel-group-report.slice' import VesselGroupReportTitle from './VesselGroupReportTitle' diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx index 33330f6c54..76fa79aaad 100644 --- a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx @@ -5,7 +5,7 @@ import { useLocationConnect } from 'routes/routes.hook' import { VGRActivitySubsection } from 'features/vessel-groups/vessel-groups.types' import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks' -import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' +import { useFitAreaInViewport } from 'features/reports/areas/area-reports.hooks' import { resetReportData } from 'features/reports/activity/reports-activity.slice' import { useAppDispatch } from 'features/app/app.hooks' diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx index c5d66cd70d..29540a1c64 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsGraph.tsx @@ -4,7 +4,7 @@ import { BarChart, Bar, XAxis, Tooltip, ResponsiveContainer, LabelList } from 'r import { useTranslation } from 'react-i18next' import { VesselGroupEventsStatsResponseGroups } from 'queries/vessel-group-events-stats-api' import I18nNumber, { formatI18nNumber } from 'features/i18n/i18nNumber' -import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/reports.config' +import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/area-reports.config' import { formatInfoField } from 'utils/info' import { useLocationConnect } from 'routes/routes.hook' import { diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx index 6de396764b..9112fd45c8 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/VesselGroupReportVesselsTable.tsx @@ -9,7 +9,7 @@ import { getDatasetsReportNotSupported } from 'features/datasets/datasets.utils' import { selectActiveReportDataviews } from 'features/dataviews/selectors/dataviews.selectors' import { selectUserData } from 'features/user/selectors/user.selectors' import DatasetLabel from 'features/datasets/DatasetLabel' -import { EMPTY_API_VALUES } from 'features/reports/areas/reports.config' +import { EMPTY_API_VALUES } from 'features/reports/areas/area-reports.config' import VesselLink from 'features/vessel/VesselLink' import VesselPin from 'features/vessel/VesselPin' import { selectWorkspaceStatus } from 'features/workspace/workspace.selectors' diff --git a/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts b/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts index 16b8c66ff5..f585ada653 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors.ts @@ -14,7 +14,7 @@ import { FILTER_PROPERTIES, FilterProperty, getVesselsFiltered, -} from 'features/reports/areas/reports.utils' +} from 'features/reports/areas/area-reports.utils' import { selectVGRVesselsOrderDirection, selectVGRVesselsOrderProperty, diff --git a/apps/fishing-map/features/sidebar/Sidebar.tsx b/apps/fishing-map/features/sidebar/Sidebar.tsx index 13555de2e7..d5e8c59962 100644 --- a/apps/fishing-map/features/sidebar/Sidebar.tsx +++ b/apps/fishing-map/features/sidebar/Sidebar.tsx @@ -26,7 +26,7 @@ import CategoryTabs from './CategoryTabs' import SidebarHeader from './SidebarHeader' const AreaReport = dynamic( - () => import(/* webpackChunkName: "Report" */ 'features/reports/areas/Report') + () => import(/* webpackChunkName: "Report" */ 'features/reports/areas/AreaReport') ) const VesselGroupReport = dynamic( () => import(/* webpackChunkName: "Report" */ 'features/reports/vessel-groups/VesselGroupReport') diff --git a/apps/fishing-map/features/sidebar/SidebarHeader.tsx b/apps/fishing-map/features/sidebar/SidebarHeader.tsx index 605d345c5c..1c8afccc51 100644 --- a/apps/fishing-map/features/sidebar/SidebarHeader.tsx +++ b/apps/fishing-map/features/sidebar/SidebarHeader.tsx @@ -50,13 +50,13 @@ import { resetSidebarScroll } from 'features/sidebar/sidebar.utils' import { useAppDispatch } from 'features/app/app.hooks' import { resetReportData } from 'features/reports/activity/reports-activity.slice' import { TrackCategory, trackEvent } from 'features/app/analytics.hooks' -import { selectReportsStatus } from 'features/reports/areas/reports.slice' +import { selectReportsStatus } from 'features/reports/areas/area-reports.slice' import { selectCurrentReport } from 'features/app/selectors/app.reports.selector' import { useLocationConnect } from 'routes/routes.hook' import { HOME, REPORT, ROUTE_TYPES, WORKSPACE } from 'routes/routes' import { EMPTY_FILTERS, IMO_LENGTH, SSVID_LENGTH, SearchType } from 'features/search/search.config' import { resetAreaDetail } from 'features/areas/areas.slice' -import { selectReportAreaIds } from 'features/reports/areas/reports.selectors' +import { selectReportAreaIds } from 'features/reports/areas/area-reports.selectors' import { useSearchFiltersConnect } from 'features/search/search.hook' import { resetVesselState } from 'features/vessel/vessel.slice' import { cleanVesselSearchResults } from 'features/search/search.slice' @@ -65,13 +65,14 @@ import LanguageToggle from 'features/i18n/LanguageToggle' import { DEFAULT_VESSEL_STATE } from 'features/vessel/vessel.config' import { isPrivateWorkspaceNotAllowed } from 'features/workspace/workspace.utils' import { setModalOpen } from 'features/modals/modals.slice' -import { useHighlightReportArea } from 'features/reports/areas/reports.hooks' +import { useHighlightReportArea } from 'features/reports/areas/area-reports.hooks' import { resetVesselGroupReportData } from 'features/reports/vessel-groups/vessel-group-report.slice' import { useClipboardNotification } from './sidebar.hooks' import styles from './SidebarHeader.module.css' const NewReportModal = dynamic( - () => import(/* webpackChunkName: "NewWorkspaceModal" */ 'features/reports/areas/NewReportModal') + () => + import(/* webpackChunkName: "NewWorkspaceModal" */ 'features/reports/areas/NewAreaReportModal') ) function SaveReportButton() { diff --git a/apps/fishing-map/features/timebar/Timebar.tsx b/apps/fishing-map/features/timebar/Timebar.tsx index 94cc7b53c5..16d161d5ab 100644 --- a/apps/fishing-map/features/timebar/Timebar.tsx +++ b/apps/fishing-map/features/timebar/Timebar.tsx @@ -29,7 +29,7 @@ import { TimebarGraphs, TimebarVisualisations } from 'types' import { selectLatestAvailableDataDate } from 'features/app/selectors/app.selectors' import { getEventLabel } from 'utils/analytics' import { upperFirst } from 'utils/info' -import { selectShowTimeComparison } from 'features/reports/areas/reports.selectors' +import { selectShowTimeComparison } from 'features/reports/areas/area-reports.selectors' import Hint from 'features/help/Hint' import { MAX_TIMEBAR_VESSELS } from 'features/timebar/timebar.config' import { useAppDispatch } from 'features/app/app.hooks' diff --git a/apps/fishing-map/features/timebar/timebar.hooks.ts b/apps/fishing-map/features/timebar/timebar.hooks.ts index 3bc0575806..0c18c4a6cf 100644 --- a/apps/fishing-map/features/timebar/timebar.hooks.ts +++ b/apps/fishing-map/features/timebar/timebar.hooks.ts @@ -21,7 +21,7 @@ import { updateUrlTimerange } from 'routes/routes.actions' import { selectIsAnyReportLocation } from 'routes/routes.selectors' import { selectHintsDismissed, setHintDismissed } from 'features/help/hints.slice' import { useAppDispatch } from 'features/app/app.hooks' -import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks' +import { useFitAreaInViewport } from 'features/reports/areas/area-reports.hooks' import { DEFAULT_TIME_RANGE } from 'data/config' import { selectActiveTrackDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsWorkspaceMapReady } from 'features/workspace/workspace.selectors' diff --git a/apps/fishing-map/features/timebar/timebar.selectors.ts b/apps/fishing-map/features/timebar/timebar.selectors.ts index 5e80400504..2948509bf3 100644 --- a/apps/fishing-map/features/timebar/timebar.selectors.ts +++ b/apps/fishing-map/features/timebar/timebar.selectors.ts @@ -19,7 +19,7 @@ import { selectActivityVisualizationMode, selectDetectionsVisualizationMode, } from 'features/app/selectors/app.selectors' -import { getReportCategoryFromDataview } from 'features/reports/areas/reports.utils' +import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { selectIsAnyReportLocation } from 'routes/routes.selectors' import { selectReportCategory } from 'features/app/selectors/app.reports.selector' import { diff --git a/apps/fishing-map/features/user/UserReports.tsx b/apps/fishing-map/features/user/UserReports.tsx index 92aa8d145a..630d315a30 100644 --- a/apps/fishing-map/features/user/UserReports.tsx +++ b/apps/fishing-map/features/user/UserReports.tsx @@ -12,7 +12,7 @@ import { fetchReportsThunk, selectReportsStatus, selectReportsStatusId, -} from 'features/reports/areas/reports.slice' +} from 'features/reports/areas/area-reports.slice' import { REPORT } from 'routes/routes' import { selectUserReports } from 'features/user/selectors/user.permissions.selectors' import { resetReportData } from 'features/reports/activity/reports-activity.slice' diff --git a/apps/fishing-map/features/user/selectors/user.permissions.selectors.ts b/apps/fishing-map/features/user/selectors/user.permissions.selectors.ts index 31185db4d7..7cc148dad7 100644 --- a/apps/fishing-map/features/user/selectors/user.permissions.selectors.ts +++ b/apps/fishing-map/features/user/selectors/user.permissions.selectors.ts @@ -9,7 +9,7 @@ import { selectAllVesselGroups, selectWorkspaceVesselGroups, } from 'features/vessel-groups/vessel-groups.slice' -import { selectAllReports } from 'features/reports/areas/reports.slice' +import { selectAllReports } from 'features/reports/areas/area-reports.slice' import { selectUserData } from 'features/user/selectors/user.selectors' import { DEFAULT_GROUP_ID } from 'features/user/user.config' import { USER_GROUP_WORKSPACE } from '../user.slice' 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 98f6330c46..8b801669c7 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.hooks.ts @@ -8,7 +8,7 @@ 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/reports/areas/reports.selectors' +import { ReportVesselWithDatasets } from 'features/reports/areas/area-reports.selectors' import { useAppDispatch } from 'features/app/app.hooks' import { sortByCreationDate } from 'utils/dates' import { diff --git a/apps/fishing-map/features/workspace/workspace.slice.ts b/apps/fishing-map/features/workspace/workspace.slice.ts index d671523bc5..e444421fec 100644 --- a/apps/fishing-map/features/workspace/workspace.slice.ts +++ b/apps/fishing-map/features/workspace/workspace.slice.ts @@ -48,12 +48,12 @@ import { AppWorkspace } from 'features/workspaces-list/workspaces-list.slice' import { getVesselDataviewInstanceDatasetConfig } from 'features/dataviews/dataviews.utils' import { mergeDataviewIntancesToUpsert } from 'features/workspace/workspace.hook' import { getUTCDateTime } from 'utils/dates' -import { fetchReportsThunk } from 'features/reports/areas/reports.slice' +import { fetchReportsThunk } from 'features/reports/areas/area-reports.slice' 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/reports/areas/reports.config' +import { DEFAULT_AREA_REPORT_STATE } from 'features/reports/areas/area-reports.config' import { DEFAULT_VESSEL_GROUP_REPORT_STATE } from 'features/reports/vessel-groups/vessel-group-report.config' import { fetchVesselGroupsThunk } from 'features/vessel-groups/vessel-groups.slice' import { diff --git a/apps/fishing-map/reducers.ts b/apps/fishing-map/reducers.ts index 3409029c1c..5cf15acb8a 100644 --- a/apps/fishing-map/reducers.ts +++ b/apps/fishing-map/reducers.ts @@ -20,7 +20,7 @@ import mapReducer from 'features/map/map.slice' import modalsReducer from 'features/modals/modals.slice' import regionsReducer from 'features/regions/regions.slice' import reportReducer from 'features/reports/activity/reports-activity.slice' -import reportsReducer from 'features/reports/areas/reports.slice' +import reportsReducer from 'features/reports/areas/area-reports.slice' import resourcesReducer from 'features/resources/resources.slice' import searchReducer from 'features/search/search.slice' import timebarReducer from 'features/timebar/timebar.slice' diff --git a/apps/fishing-map/types/index.ts b/apps/fishing-map/types/index.ts index e8807282d4..3c7ad8a575 100644 --- a/apps/fishing-map/types/index.ts +++ b/apps/fishing-map/types/index.ts @@ -8,7 +8,7 @@ import { RulerData, } from '@globalfishingwatch/deck-layers' import { MapAnnotation } from 'features/map/overlays/annotations/annotations.types' -import { AreaReportState, AreaReportStateProperty } from 'features/reports/areas/reports.types' +import { AreaReportState, AreaReportStateProperty } from 'features/reports/areas/area-reports.types' import { VesselProfileState, VesselProfileStateProperty } from 'features/vessel/vessel.types' import { VesselGroupReportState, From 81ace7d4d8c45d5c922d3c59d6a546f0284f1c8e Mon Sep 17 00:00:00 2001 From: j8seangel Date: Tue, 24 Sep 2024 19:00:29 +0200 Subject: [PATCH 11/15] fix vessel activity reports in vgr --- .../app/selectors/app.reports.selector.ts | 18 ++++++++++++-- .../features/download/download.selectors.ts | 2 +- .../report-activity-vessels.selectors.ts | 5 +++- .../reports/areas/area-reports.hooks.ts | 24 ------------------- .../VGRActivitySubsectionSelector.tsx.tsx | 2 +- .../vessel-group-report.config.ts | 2 +- .../vessel-groups/vessel-groups.types.ts | 2 +- 7 files changed, 24 insertions(+), 31 deletions(-) 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 18412b7483..903a5ca6aa 100644 --- a/apps/fishing-map/features/app/selectors/app.reports.selector.ts +++ b/apps/fishing-map/features/app/selectors/app.reports.selector.ts @@ -20,6 +20,7 @@ import { } from 'features/reports/areas/area-reports.config.selectors' import { ReportCategory, ReportVesselGraph } from 'features/reports/areas/area-reports.types' import { WORLD_REGION_ID } from 'features/reports/activity/reports-activity.slice' +import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' export const selectCurrentReport = createSelector( [selectReportId, (state) => state.reports], @@ -62,8 +63,21 @@ export const selectReportActiveCategories = createSelector( ) export const selectReportCategory = createSelector( - [selectReportCategorySelector, selectReportActiveCategories], - (reportCategory, activeCategories): ReportCategory => { + [ + selectReportCategorySelector, + selectReportActiveCategories, + selectIsVesselGroupReportLocation, + selectVGRActivitySubsection, + ], + ( + reportCategory, + activeCategories, + isVesselGroupReportLocation, + vGRActivitySubsection + ): ReportCategory => { + if (isVesselGroupReportLocation) { + return vGRActivitySubsection as ReportCategory + } return activeCategories.some((category) => category === reportCategory) ? reportCategory : activeCategories[0] diff --git a/apps/fishing-map/features/download/download.selectors.ts b/apps/fishing-map/features/download/download.selectors.ts index db2a6fa21c..99ce12bbda 100644 --- a/apps/fishing-map/features/download/download.selectors.ts +++ b/apps/fishing-map/features/download/download.selectors.ts @@ -7,7 +7,7 @@ import { AsyncReducerStatus } from 'utils/async-slice' export const selectDownloadActivityArea = createSelector( [selectDownloadActivityAreaKey, selectAreas], (areaKey, areas): DatasetAreaDetail => { - return areas[areaKey!?.datasetId]!.detail[areaKey!?.areaId] + return areas[areaKey!?.datasetId]?.detail?.[areaKey!?.areaId] } ) diff --git a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts index ece0c5e14b..0ff9d77e5a 100644 --- a/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts +++ b/apps/fishing-map/features/reports/activity/vessels/report-activity-vessels.selectors.ts @@ -36,9 +36,12 @@ export const selectReportVesselsList = createSelector( [selectReportActivityFlatten, selectAllDatasets, selectReportCategory], (vessels, datasets, reportCategory) => { if (!vessels?.length) return null + return Object.values(groupBy(vessels, (v) => v.vesselId)) .flatMap((vesselActivity) => { - if (vesselActivity[0]?.category !== reportCategory) return EMPTY_ARRAY + if (vesselActivity[0]?.category !== reportCategory) { + return EMPTY_ARRAY + } const activityDataset = datasets.find((d) => vesselActivity[0].activityDatasetId === d.id) const infoDatasetId = getRelatedDatasetByType(activityDataset, DatasetTypes.Vessels)?.id const infoDataset = datasets.find((d) => d.id === infoDatasetId) diff --git a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts index b7b2984f05..45215faac9 100644 --- a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts @@ -253,30 +253,6 @@ export function useFetchReportVessel() { updateWorkspaceReportUrls, ]) - // useEffect(() => { - // const isDifferentDateRange = reportDateRangeHash !== getDateRangeHash(timerange) - // if ( - // areaId && - // reportDataviews?.length && - // timerangeSupported && - // isDifferentDateRange && - // workspaceStatus === AsyncReducerStatus.Finished - // ) { - // dispatchFetchReport() - // } - // // Avoid re-fetching when timerange changes - // // eslint-disable-next-line react-hooks/exhaustive-deps - // }, [ - // dispatch, - // areaId, - // datasetId, - // reportBufferHash, - // reportDataviews, - // timerangeSupported, - // reportDateRangeHash, - // workspaceStatus, - // ]) - return useMemo( () => ({ status, data, error, dispatchFetchReport }), [status, data, error, dispatchFetchReport] diff --git a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx index 76fa79aaad..1f76e8bcbe 100644 --- a/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/activity/VGRActivitySubsectionSelector.tsx.tsx @@ -18,7 +18,7 @@ function VGRActivitySubsectionSelector() { const fitAreaInViewport = useFitAreaInViewport() const options: ChoiceOption[] = [ { - id: 'fishing-effort', + id: 'fishing', label: t('common.apparentFishing', 'Apparent fishing effort'), disabled: loading, }, diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.config.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.config.ts index c416d3b4a4..b9e7ce4b36 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.config.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.config.ts @@ -7,7 +7,7 @@ export const DEFAULT_VESSEL_GROUP_REPORT_STATE: VesselGroupReportState = { viewOnlyVesselGroup: true, vGRSection: 'vessels', vGRVesselsSubsection: 'flag', - vGRActivitySubsection: 'fishing-effort', + vGRActivitySubsection: 'fishing', vGREventsSubsection: 'encounter', vGREventsVesselsProperty: 'flag', vGRVesselPage: 0, diff --git a/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts b/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts index 6238b8840e..f0c2daa343 100644 --- a/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts +++ b/apps/fishing-map/features/vessel-groups/vessel-groups.types.ts @@ -1,6 +1,6 @@ export type VGRSection = 'vessels' | 'insights' | 'activity' | 'events' export type VGRVesselsSubsection = 'flag' | 'shiptypes' | 'geartypes' | 'source' -export type VGRActivitySubsection = 'fishing-effort' | 'presence' +export type VGRActivitySubsection = 'fishing' | 'presence' export type VGREventsVesselsProperty = 'flag' | 'geartype' export type VGREventsSubsection = 'encounter' | 'loitering' | 'gaps' | 'port_visits' export type VGRVesselsOrderProperty = 'shipname' | 'flag' | 'shiptype' From 04413966a583dd6e5c3e01c56279528638d9fcb3 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Wed, 25 Sep 2024 13:41:08 +0200 Subject: [PATCH 12/15] fix missing fishing events ids request --- .../reports/vessel-groups/insights/VGRInsightFishing.tsx | 7 ++++--- .../vessel-groups/insights/VGRInsightVesselEvents.tsx | 4 ++-- apps/fishing-map/queries/vessel-events-api.ts | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightFishing.tsx b/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightFishing.tsx index 14219e39d6..ef2a6abb1a 100644 --- a/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightFishing.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightFishing.tsx @@ -55,7 +55,7 @@ const VesselGroupReportInsightFishing = () => { const getVesselGroupReportInsighFishingVessels = ( vessels: VesselGroupReportInsightVessel[], - insight: 'eventsInNoTakeMPAs' | 'eventsInRFMOWithoutKnownAuthorization' + insight: 'eventsInNoTakeMPAs' | 'eventsInRfmoWithoutKnownAuthorization' ) => { const expandedIdPrefix = insight === 'eventsInNoTakeMPAs' ? 'no-take-' : 'rfmo-' return ( @@ -89,7 +89,8 @@ const VesselGroupReportInsightFishing = () => { > {isExpandedVessel && vessel.datasets?.[0] && ( { > {getVesselGroupReportInsighFishingVessels( vesselsInRfmoWithoutKnownAuthorization, - 'eventsInRFMOWithoutKnownAuthorization' + 'eventsInRfmoWithoutKnownAuthorization' )} )} diff --git a/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightVesselEvents.tsx b/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightVesselEvents.tsx index f1cc9423df..d28e7feb41 100644 --- a/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightVesselEvents.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/insights/VGRInsightVesselEvents.tsx @@ -12,7 +12,7 @@ const VesselGroupReportInsightVesselEvents = ({ end, }: { ids?: string[] - vesselId?: string + vesselId: string datasetId: string start: string end: string @@ -20,7 +20,7 @@ const VesselGroupReportInsightVesselEvents = ({ const { t } = useTranslation() const { data, isLoading, error } = useGetVesselEventsQuery( { - ...(vesselId && { vessels: [vesselId] }), + vessels: [vesselId], ...(ids && { ids: ids }), datasets: [datasetId], 'start-date': start, diff --git a/apps/fishing-map/queries/vessel-events-api.ts b/apps/fishing-map/queries/vessel-events-api.ts index 7fc23ed0c8..476c78545c 100644 --- a/apps/fishing-map/queries/vessel-events-api.ts +++ b/apps/fishing-map/queries/vessel-events-api.ts @@ -3,8 +3,8 @@ import { getQueryParamsResolved, gfwBaseQuery } from 'queries/base' import { ApiEvents } from '@globalfishingwatch/api-types' type VesselEventsApiParams = { - vessels?: string[] ids?: string[] + vessels: string[] datasets: string[] 'start-date': string 'end-date': string From edccb92de51d95ed6808908fbff6a893f052a091 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Wed, 25 Sep 2024 13:41:34 +0200 Subject: [PATCH 13/15] fix timeseries on vessel group subcategory change --- .../dataviews.resolvers.selectors.ts | 4 +-- .../reports/activity/ReportActivityGraph.tsx | 3 ++- .../reports-activity-timeseries.hooks.ts | 14 +++++++--- .../vessel-group-report.dataviews.ts | 26 ++++++++++++++----- .../features/timebar/timebar.hooks.ts | 6 +++-- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts index cb9285adcc..86d95cf590 100644 --- a/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts +++ b/apps/fishing-map/features/dataviews/selectors/dataviews.resolvers.selectors.ts @@ -53,7 +53,6 @@ import { import { ReportCategory } from 'features/reports/areas/area-reports.types' import { getReportCategoryFromDataview } from 'features/reports/areas/area-reports.utils' import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors' -import { FISHING_DATAVIEW_SLUG, PRESENCE_DATAVIEW_SLUG } from 'data/workspaces' const EMPTY_ARRAY: [] = [] @@ -136,8 +135,7 @@ export const selectDataviewInstancesMerged = createSelector( vesselGroupId: reportVesselGroupId, color: vesselGroupDataviewInstance?.config?.color, colorRamp: vesselGroupDataviewInstance?.config?.colorRamp as ColorRampId, - dataviewId: - vGRActivitySubsection === 'presence' ? PRESENCE_DATAVIEW_SLUG : FISHING_DATAVIEW_SLUG, + activityType: vGRActivitySubsection, }) if (activityVGRInstance) { mergedDataviewInstances.push(activityVGRInstance) diff --git a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx index 459e827237..59c609b9aa 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx @@ -50,6 +50,7 @@ export default function ReportActivity() { const fitAreaInViewport = useFitAreaInViewport() const { loaded, bbox } = useReportAreaBounds() + const bboxHash = bbox ? bbox.join(',') : '' // This ensures that the area is in viewport when then area load finishes useEffect(() => { @@ -57,7 +58,7 @@ export default function ReportActivity() { fitAreaInViewport() } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loaded]) + }, [loaded, bboxHash]) const { t } = useTranslation() const { start, end } = useTimerangeConnect() diff --git a/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts index 1ca84b55b8..8e7e52c0c9 100644 --- a/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts +++ b/apps/fishing-map/features/reports/activity/reports-activity-timeseries.hooks.ts @@ -42,6 +42,7 @@ import { selectReportTimeComparison, } from '../areas/area-reports.config.selectors' import { ENTIRE_WORLD_REPORT_AREA_ID } from '../areas/area-reports.config' +import { selectVGRActivitySubsection } from '../vessel-groups/vessel-group.config.selectors' interface EvolutionGraphData { date: string @@ -131,6 +132,7 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { const areaInViewport = useReportAreaInViewport() const reportGraph = useSelector(selectReportActivityGraph) const reportCategory = useSelector(selectReportCategory) + const vGRActivitySubsection = useSelector(selectVGRActivitySubsection) const timeComparison = useSelector(selectReportTimeComparison) const reportBufferHash = useSelector(selectReportBufferHash) const dataviews = useSelector(selectActiveReportDataviews) @@ -155,6 +157,7 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { }, [ area?.id, reportCategory, + vGRActivitySubsection, timeComparisonHash, instancesChunkHash, reportGraphMode, @@ -164,7 +167,6 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { const updateFeaturesFiltered = useCallback( async (instances: FourwingsLayer[], area: Area, mode?: 'point' | 'cell') => { setFeaturesFiltered([]) - for (const instance of instances) { const features = instance.getData() as FourwingsFeature[] const filteredInstanceFeatures = @@ -182,12 +184,18 @@ const useReportTimeseries = (reportLayers: DeckLayerAtom[]) => { ) useEffect(() => { - if (area?.geometry && layersLoaded && featuresFilteredDirtyRef.current && instances.length) { + if ( + area?.geometry && + areaInViewport && + layersLoaded && + featuresFilteredDirtyRef.current && + instances.length + ) { updateFeaturesFiltered(instances, area, reportCategory === 'environment' ? 'point' : 'cell') featuresFilteredDirtyRef.current = false } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [area, reportCategory, layersLoaded, reportBufferHash]) + }, [area, reportCategory, areaInViewport, layersLoaded, reportBufferHash]) const computeTimeseries = useCallback( ( diff --git a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts index 4165b12eab..b9a6812e14 100644 --- a/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts +++ b/apps/fishing-map/features/reports/vessel-groups/vessel-group-report.dataviews.ts @@ -16,6 +16,7 @@ import { PRESENCE_DATAVIEW_SLUG, } from 'data/workspaces' import { + VGRActivitySubsection, VGREventsSubsection, VGRSection, VGRSubsection, @@ -23,11 +24,20 @@ import { export const VESSEL_GROUP_DATAVIEW_PREFIX = `vessel-group-` +export type VesselGroupActivityDataviewId = + `${typeof VESSEL_GROUP_DATAVIEW_PREFIX}${VGRActivitySubsection}` + +export const VESSEL_GROUP_FISHING_ACTIVITY_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}fishing` +export const VESSEL_GROUP_PRESENCE_ACTIVITY_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}presence` + +export const VESSEL_GROUP_ACTIVITY_DATAVIEW_IDS: VesselGroupActivityDataviewId[] = [ + VESSEL_GROUP_FISHING_ACTIVITY_ID, + VESSEL_GROUP_PRESENCE_ACTIVITY_ID, +] + export type VesselGroupEventsDataviewId = `${typeof VESSEL_GROUP_DATAVIEW_PREFIX}${VGREventsSubsection}` -export const VESSEL_GROUP_ACTIVITY_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}activity` - export const VESSEL_GROUP_ENCOUNTER_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}encounter` export const VESSEL_GROUP_LOITERING_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}loitering` export const VESSEL_GROUP_PORT_VISITS_EVENTS_ID = `${VESSEL_GROUP_DATAVIEW_PREFIX}port_visits` @@ -72,7 +82,9 @@ export function getReportVesselGroupVisibleDataviews({ return id.toString() === dataviewIdBySubSection } if (vesselGroupReportSection === 'activity') { - return id.toString() === VESSEL_GROUP_ACTIVITY_ID + return VESSEL_GROUP_ACTIVITY_DATAVIEW_IDS.includes( + id.toString() as VesselGroupActivityDataviewId + ) } return ( category === DataviewCategory.VesselGroups && @@ -105,16 +117,16 @@ export const getVesselGroupActivityDataviewInstance = ({ vesselGroupId, color, colorRamp, - dataviewId, + activityType, }: { vesselGroupId: string color?: string colorRamp?: ColorRampId - dataviewId: typeof FISHING_DATAVIEW_SLUG | typeof PRESENCE_DATAVIEW_SLUG + activityType: VGRActivitySubsection }): DataviewInstance | undefined => { if (vesselGroupId) { return { - id: VESSEL_GROUP_ACTIVITY_ID, + id: `${VESSEL_GROUP_DATAVIEW_PREFIX}${activityType}`, category: DataviewCategory.Activity, config: { visible: true, @@ -124,7 +136,7 @@ export const getVesselGroupActivityDataviewInstance = ({ 'vessel-groups': [vesselGroupId], }, }, - dataviewId, + dataviewId: activityType === 'presence' ? PRESENCE_DATAVIEW_SLUG : FISHING_DATAVIEW_SLUG, } } } diff --git a/apps/fishing-map/features/timebar/timebar.hooks.ts b/apps/fishing-map/features/timebar/timebar.hooks.ts index 0c18c4a6cf..67ca8a964d 100644 --- a/apps/fishing-map/features/timebar/timebar.hooks.ts +++ b/apps/fishing-map/features/timebar/timebar.hooks.ts @@ -15,7 +15,6 @@ import { useLocationConnect } from 'routes/routes.hook' import { selectActiveReportActivityDataviews, selectActiveHeatmapEnvironmentalDataviewsWithoutStatic, - selectActiveVesselGroupDataviews, } from 'features/dataviews/selectors/dataviews.selectors' import { updateUrlTimerange } from 'routes/routes.actions' import { selectIsAnyReportLocation } from 'routes/routes.selectors' @@ -25,7 +24,10 @@ import { useFitAreaInViewport } from 'features/reports/areas/area-reports.hooks' import { DEFAULT_TIME_RANGE } from 'data/config' import { selectActiveTrackDataviews } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectIsWorkspaceMapReady } from 'features/workspace/workspace.selectors' -import { selectActiveDetectionsDataviews } from 'features/dataviews/selectors/dataviews.categories.selectors' +import { + selectActiveDetectionsDataviews, + selectActiveVesselGroupDataviews, +} from 'features/dataviews/selectors/dataviews.categories.selectors' import { changeSettings, setHighlightedEvents, From 62b956c0d76d5efb75a3f9371c5903b53ebecc97 Mon Sep 17 00:00:00 2001 From: j8seangel Date: Wed, 25 Sep 2024 13:52:28 +0200 Subject: [PATCH 14/15] use fallback when no report area bounds --- .../features/reports/areas/area-reports.config.ts | 5 +++-- .../features/reports/areas/area-reports.hooks.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/fishing-map/features/reports/areas/area-reports.config.ts b/apps/fishing-map/features/reports/areas/area-reports.config.ts index 43175c5ce4..cc5f78397c 100644 --- a/apps/fishing-map/features/reports/areas/area-reports.config.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.config.ts @@ -3,7 +3,7 @@ import { REPORT_VESSELS_GRAPH_FLAG, REPORT_VESSELS_PER_PAGE, } from 'data/config' -import { BufferUnit, BufferOperation } from 'types' +import { BufferUnit, BufferOperation, Bbox } from 'types' import { Area, AreaGeometry } from 'features/areas/areas.slice' import { AreaReportState } from './area-reports.types' @@ -43,12 +43,13 @@ export const DEFAULT_AREA_REPORT_STATE: AreaReportState = { } export const ENTIRE_WORLD_REPORT_AREA_ID = 'world' +export const ENTIRE_WORLD_REPORT_AREA_BOUNDS = [-180, -90, 180, 90] as Bbox export const ENTIRE_WORLD_REPORT_AREA: Area = { id: ENTIRE_WORLD_REPORT_AREA_ID, name: 'Entire World', type: 'Feature', - bounds: [-180, -90, 180, 90], + bounds: ENTIRE_WORLD_REPORT_AREA_BOUNDS, geometry: { type: 'Polygon', coordinates: [ diff --git a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts index 45215faac9..e76f86c902 100644 --- a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts @@ -32,6 +32,7 @@ import { FIT_BOUNDS_REPORT_PADDING } from 'data/config' import { RFMO_DATAVIEW_SLUG } from 'data/workspaces' import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' import { + ENTIRE_WORLD_REPORT_AREA_BOUNDS, LAST_REPORTS_STORAGE_KEY, LastReportStorage, } from 'features/reports/areas/area-reports.config' @@ -104,9 +105,14 @@ function useVesselGroupReportBounds() { ) const statsBbox = stats && ([stats.minLon, stats.minLat, stats.maxLon, stats.maxLat] as Bbox) + const loaded = !isFetching && isSuccess return { - loaded: !isFetching && isSuccess, - bbox: statsBbox?.some((v) => v === null || v === undefined) ? null : statsBbox!, + loaded: loaded, + bbox: loaded + ? statsBbox?.some((v) => v === null || v === undefined) + ? ENTIRE_WORLD_REPORT_AREA_BOUNDS + : statsBbox! + : null, } } From 7b2c26487664926f21958229b9f6dea2435379eb Mon Sep 17 00:00:00 2001 From: j8seangel Date: Wed, 25 Sep 2024 15:46:46 +0200 Subject: [PATCH 15/15] fitBounds in vesselGroupReport vessels load --- .../features/map/map-bounds.hooks.ts | 2 +- .../ReportActivityPeriodComparison.tsx | 8 ++--- .../reports/areas/area-reports.hooks.ts | 31 +++++++++++++------ .../vessel-groups/VesselGroupReport.tsx | 18 ++++++++++- .../vessel-groups/VesselGroupReportLink.tsx | 17 ++++++++-- .../vessel-groups/VesselGroupsLayerPanel.tsx | 4 +-- 6 files changed, 61 insertions(+), 19 deletions(-) diff --git a/apps/fishing-map/features/map/map-bounds.hooks.ts b/apps/fishing-map/features/map/map-bounds.hooks.ts index 0185706a3d..e4ba0dd53a 100644 --- a/apps/fishing-map/features/map/map-bounds.hooks.ts +++ b/apps/fishing-map/features/map/map-bounds.hooks.ts @@ -35,7 +35,7 @@ export const useMapBounds = (): { bounds: MiniglobeBounds } => { return { bounds } } -type FitBoundsParams = { +export type FitBoundsParams = { mapWidth?: number mapHeight?: number padding?: number diff --git a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx index 8cdef7fd2c..b704cf07e1 100644 --- a/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx +++ b/apps/fishing-map/features/reports/activity/ReportActivityPeriodComparison.tsx @@ -40,7 +40,7 @@ export default function ReportActivityGraph() { action: `Select comparison date in 'period comparison'`, label: JSON.stringify({ date: date.target.value, - regionName: reportArea.name, + regionName: reportArea?.name, sourceNames: dataviews.flatMap((dataview) => getSourcesSelectedInDataview(dataview).map((source) => source.label) ), @@ -55,7 +55,7 @@ export default function ReportActivityGraph() { action: `Select baseline date in 'period comparison'`, label: JSON.stringify({ date: date.target.value, - regionName: reportArea.name, + regionName: reportArea?.name, sourceNames: dataviews.flatMap((dataview) => getSourcesSelectedInDataview(dataview).map((source) => source.label) ), @@ -70,7 +70,7 @@ export default function ReportActivityGraph() { action: `Select duration in 'period comparison'`, label: JSON.stringify({ duration: duration?.target?.value + ' ' + durationTypeOption?.label, - regionName: reportArea.name, + regionName: reportArea?.name, sourceNames: dataviews.flatMap((dataview) => getSourcesSelectedInDataview(dataview).map((source) => source.label) ), @@ -85,7 +85,7 @@ export default function ReportActivityGraph() { action: `Select duration in 'period comparison'`, label: JSON.stringify({ duration: timeComparison?.duration + ' ' + duration?.label, - regionName: reportArea.name, + regionName: reportArea?.name, sourceNames: dataviews.flatMap((dataview) => getSourcesSelectedInDataview(dataview).map((source) => source.label) ), diff --git a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts index e76f86c902..0f39fd614f 100644 --- a/apps/fishing-map/features/reports/areas/area-reports.hooks.ts +++ b/apps/fishing-map/features/reports/areas/area-reports.hooks.ts @@ -30,7 +30,7 @@ import { Bbox } from 'types' import { useSetMapCoordinates, useMapViewState } from 'features/map/map-viewport.hooks' import { FIT_BOUNDS_REPORT_PADDING } from 'data/config' import { RFMO_DATAVIEW_SLUG } from 'data/workspaces' -import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' +import { FitBoundsParams, getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks' import { ENTIRE_WORLD_REPORT_AREA_BOUNDS, LAST_REPORTS_STORAGE_KEY, @@ -45,6 +45,7 @@ import { selectReportVesselsError, selectReportVesselsStatus, } from 'features/reports/activity/reports-activity.slice' +import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors' import { selectVGRActivityDataview } from '../vessel-groups/vessel-group-report.selectors' export type DateTimeSeries = { @@ -70,24 +71,24 @@ export const useHighlightReportArea = () => { ) } -function useReportAreaCenter(bounds?: Bbox) { +const defaultParams = {} as FitBoundsParams +export function useReportAreaCenter(bounds?: Bbox, params = defaultParams) { const map = useDeckMap() return useMemo(() => { if (!bounds || !map) return null const { latitude, longitude, zoom } = getMapCoordinatesFromBounds(map, bounds, { padding: FIT_BOUNDS_REPORT_PADDING, + ...params, }) return { latitude: parseFloat(latitude.toFixed(8)), longitude: parseFloat(longitude.toFixed(8)), zoom: parseFloat(zoom.toFixed(8)), } - }, [bounds, map]) + }, [bounds, map, params]) } -function useVesselGroupReportBounds() { - const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) - const dataview = useSelector(selectVGRActivityDataview)! +export function useStatsBounds(dataview?: UrlDataviewInstance) { const urlTimeRange = useSelector(selectUrlTimeRange) const { data: stats, @@ -95,12 +96,12 @@ function useVesselGroupReportBounds() { isSuccess, } = useGetStatsByDataviewQuery( { - dataview, + dataview: dataview!, timerange: urlTimeRange as any, fields: [], }, { - skip: !isVesselGroupReportLocation || !dataview || !urlTimeRange, + skip: !dataview || !urlTimeRange, } ) @@ -116,9 +117,21 @@ function useVesselGroupReportBounds() { } } +export function useVesselGroupActivityBounds() { + const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) + const dataview = useSelector(selectVGRActivityDataview)! + return useStatsBounds(isVesselGroupReportLocation ? dataview : undefined) +} + +export function useVesselGroupBounds(dataviewId?: string) { + const dataviews = useSelector(selectDataviewInstancesResolvedVisible) + const dataview = dataviews?.find((d) => d.id === dataviewId) + return useStatsBounds(dataview) +} + export function useReportAreaBounds() { const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation) - const { loaded, bbox } = useVesselGroupReportBounds() + const { loaded, bbox } = useVesselGroupActivityBounds() const area = useSelector(selectReportArea) const areaStatus = useSelector(selectReportAreaStatus) const areaBbox = isVesselGroupReportLocation ? bbox : area?.geometry?.bbox || area!?.bounds diff --git a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx index 15e12c74a9..7cc3ab7e97 100644 --- a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReport.tsx @@ -14,7 +14,12 @@ import { } from 'features/timebar/timebar.hooks' import VGREvents from 'features/reports/events/VGREvents' import VGRActivity from 'features/reports/vessel-groups/activity/VGRActivity' -import { useFitAreaInViewport } from '../areas/area-reports.hooks' +import { useSetMapCoordinates } from 'features/map/map-viewport.hooks' +import { + useFitAreaInViewport, + useReportAreaCenter, + useVesselGroupBounds, +} from '../areas/area-reports.hooks' import { useFetchVesselGroupReport } from './vessel-group-report.hooks' import { selectVGRData, selectVGRStatus } from './vessel-group-report.slice' import VesselGroupReportTitle from './VesselGroupReportTitle' @@ -35,6 +40,10 @@ function VesselGroupReport() { const { dispatchTimebarVisualisation } = useTimebarVisualisationConnect() const { dispatchTimebarSelectedVGId } = useTimebarVesselGroupConnect() const fitAreaInViewport = useFitAreaInViewport() + const { bbox } = useVesselGroupBounds(reportDataview?.id) + const coordinates = useReportAreaCenter(bbox!) + const setMapCoordinates = useSetMapCoordinates() + const bboxHash = bbox ? bbox.join(',') : '' useEffect(() => { fetchVesselGroupReport(vesselGroupId) @@ -50,6 +59,13 @@ function VesselGroupReport() { vesselGroupId, ]) + useEffect(() => { + if (reportSection === 'vessels' && coordinates) { + setMapCoordinates(coordinates) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [bboxHash, setMapCoordinates]) + const changeTab = useCallback( (tab: Tab) => { dispatchQueryParams({ vGRSection: tab.id }) diff --git a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReportLink.tsx b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReportLink.tsx index 3568d6a353..650b95b577 100644 --- a/apps/fishing-map/features/reports/vessel-groups/VesselGroupReportLink.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/VesselGroupReportLink.tsx @@ -5,15 +5,23 @@ import { VESSEL_GROUP_REPORT } from 'routes/routes' import { selectWorkspace } from 'features/workspace/workspace.selectors' import { DEFAULT_WORKSPACE_CATEGORY, DEFAULT_WORKSPACE_ID } from 'data/workspaces' import { selectLocationQuery } from 'routes/routes.selectors' +import { useReportAreaCenter, useVesselGroupBounds } from '../areas/area-reports.hooks' import styles from './VesselGroupReport.module.css' type VesselGroupReportLinkProps = { + dataviewId?: string vesselGroupId: string children: React.ReactNode } -function VesselGroupReportLink({ children, vesselGroupId }: VesselGroupReportLinkProps) { +function VesselGroupReportLink({ + children, + vesselGroupId, + dataviewId, +}: VesselGroupReportLinkProps) { const workspace = useSelector(selectWorkspace) + const { bbox } = useVesselGroupBounds(dataviewId) + const coordinates = useReportAreaCenter(bbox!, { mapWidth: window.innerWidth / 2 }) const query = useSelector(selectLocationQuery) if (!workspace || !vesselGroupId) { return children @@ -28,7 +36,12 @@ function VesselGroupReportLink({ children, vesselGroupId }: VesselGroupReportLin workspaceId: workspace?.id || DEFAULT_WORKSPACE_ID, vesselGroupId: vesselGroupId, }, - query, + query: { + ...query, + ...(coordinates && { + ...coordinates, + }), + }, }} > {children} diff --git a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx index d4342e59f6..c051861529 100644 --- a/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx +++ b/apps/fishing-map/features/workspace/vessel-groups/VesselGroupsLayerPanel.tsx @@ -98,7 +98,7 @@ function VesselGroupLayerPanel({ + <VesselGroupReportLink vesselGroupId={vesselGroup?.id!} dataviewId={dataview.id}> <Tooltip content={t('vesselGroupReport.clickToSee', 'Click to see the vessel group report')} > @@ -130,7 +130,7 @@ function VesselGroupLayerPanel({ <Fragment> {layerActive && ( <Fragment> - <VesselGroupReportLink vesselGroupId={vesselGroup?.id!}> + <VesselGroupReportLink vesselGroupId={vesselGroup?.id!} dataviewId={dataview.id}> <IconButton tooltip={t( 'vesselGroupReport.clickToSee',