Skip to content

Commit

Permalink
add vesselGroup layer from modal creation
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel committed Aug 30, 2024
1 parent d21409e commit 33af5df
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default function ReportVesselsTableFooter({ reportName }: ReportVesselsTa
}
const onAddToVesselGroup = () => {
const dataviewIds = heatmapDataviews.map(({ id }) => id)
dispatch(setVesselGroupConfirmationMode('saveAndNavigate'))
dispatch(setVesselGroupConfirmationMode('saveAndSeeInWorkspace'))
if (dataviewIds?.length) {
dispatch(setVesselGroupCurrentDataviewIds(dataviewIds))
}
Expand Down
2 changes: 1 addition & 1 deletion apps/fishing-map/features/search/SearchActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function SearchActions() {

const onAddToVesselGroup = () => {
const dataviewIds = heatmapDataviews.map(({ id }) => id)
dispatch(setVesselGroupConfirmationMode('saveAndNavigate'))
dispatch(setVesselGroupConfirmationMode('saveAndSeeInWorkspace'))
if (dataviewIds?.length) {
dispatch(setVesselGroupCurrentDataviewIds(dataviewIds))
}
Expand Down
12 changes: 9 additions & 3 deletions apps/fishing-map/features/vessel-groups/VesselGroupAddButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { useCallback } from 'react'
import cx from 'classnames'
import { useTranslation } from 'react-i18next'
import React from 'react'
import { useSelector } from 'react-redux'
import { Button, ButtonType, ButtonSize } from '@globalfishingwatch/ui-components'
import { MAX_VESSEL_GROUP_VESSELS } from 'features/vessel-groups/vessel-groups.slice'
import { selectIsGuestUser } from 'features/user/selectors/user.selectors'
import styles from './VesselGroupListTooltip.module.css'
import VesselGroupListTooltip from './VesselGroupListTooltip'
import {
Expand Down Expand Up @@ -38,17 +40,21 @@ export function VesselGroupAddActionButton({
onToggleClick,
}: VesselGroupAddButtonToggleProps) {
const { t } = useTranslation()
const guestUser = useSelector(selectIsGuestUser)
const tooManyVessels = vessels && vessels?.length > MAX_VESSEL_GROUP_VESSELS
const disabled = guestUser || !vessels?.length || tooManyVessels

return (
<Button
size={buttonSize}
type={buttonType}
className={cx('print-hidden', styles.button, className)}
onClick={onToggleClick}
disabled={!vessels?.length || tooManyVessels}
onClick={disabled ? undefined : onToggleClick}
disabled={disabled}
tooltip={
tooManyVessels
guestUser
? t('vesselGroup.loginToAdd', 'Login to add to group')
: tooManyVessels
? t('vesselGroup.tooManyVessels', {
count: MAX_VESSEL_GROUP_VESSELS,
defaultValue: 'Maximum number of vessels is {{count}}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ function VesselGroupListTooltip(props: VesselGroupListTooltipProps) {
const [vesselGroupsOpen, setVesselGroupsOpen] = useState(false)

const toggleVesselGroupsOpen = useCallback(() => {
setVesselGroupsOpen(!vesselGroupsOpen)
}, [vesselGroupsOpen])
if (vesselGroupOptions?.length) {
setVesselGroupsOpen(!vesselGroupsOpen)
}
}, [vesselGroupOptions?.length, vesselGroupsOpen])

return (
<Popover
Expand Down
110 changes: 45 additions & 65 deletions apps/fishing-map/features/vessel-groups/VesselGroupModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useCallback, useEffect, Fragment, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { VesselGroupVessel } from '@globalfishingwatch/api-types'
import { VesselGroup, VesselGroupVessel } from '@globalfishingwatch/api-types'
import {
Modal,
Button,
Expand All @@ -19,15 +19,15 @@ import {
selectAllVesselGroupSearchVessels,
selectHasVesselGroupSearchVessels,
selectHasVesselGroupVesselsOverflow,
selectVesselGroupWorkspaceToNavigate,
selectWorkspaceVessselGroupsIds,
} from 'features/vessel-groups/vessel-groups.selectors'
import {
mergeDataviewIntancesToUpsert,
useDataviewInstancesConnect,
} from 'features/workspace/workspace.hook'
import { selectUrlDataviewInstances } from 'routes/routes.selectors'
import { AsyncReducerStatus } from 'utils/async-slice'
import { getEventLabel } from 'utils/analytics'
import { selectLastVisitedWorkspace } from 'features/workspace/workspace.selectors'
import { updateLocation } from 'routes/routes.actions'
import { ROUTE_TYPES } from 'routes/routes'
import { resetSidebarScroll } from 'features/sidebar/sidebar.utils'
Expand All @@ -36,6 +36,7 @@ import { TrackCategory, trackEvent } from 'features/app/analytics.hooks'
import UserGuideLink from 'features/help/UserGuideLink'
import { getVesselId } from 'features/vessel/vessel.utils'
import { ID_COLUMNS_OPTIONS } from 'features/vessel-groups/vessel-groups.config'
import { getVesselGroupDataviewInstance } from 'features/dataviews/dataviews.utils'
import {
IdField,
resetVesselGroup,
Expand All @@ -50,14 +51,13 @@ import {
setVesselGroupSearchId,
resetVesselGroupStatus,
setVesselGroupSearchVessels,
updateVesselGroupThunk,
searchVesselGroupsVesselsThunk,
MAX_VESSEL_GROUP_SEARCH_VESSELS,
MAX_VESSEL_GROUP_VESSELS,
getVesselInVesselGroupThunk,
selectCurrentDataviewIds,
selectVesselGroupConfirmationMode,
VesselGroupConfirmationMode,
updateVesselGroupVesselsThunk,
} from './vessel-groups.slice'
import styles from './VesselGroupModal.module.css'

Expand All @@ -66,15 +66,14 @@ function VesselGroupModal(): React.ReactElement {
const dispatch = useAppDispatch()
const [buttonLoading, setButtonLoading] = useState<VesselGroupConfirmationMode | ''>('')
const isModalOpen = useSelector(selectVesselGroupModalOpen)
const currentDataviewIds = useSelector(selectCurrentDataviewIds)
const confirmationMode = useSelector(selectVesselGroupConfirmationMode)
const searchIdField = useSelector(selectVesselGroupSearchId)
const editingVesselGroupId = useSelector(selectVesselGroupEditId)
const vesselGroupVessels = useSelector(selectVesselGroupsVessels)
const editingVesselGroup = useSelector(selectVesselGroupById(editingVesselGroupId as string))
const searchVesselStatus = useSelector(selectVesselGroupSearchStatus)
const vesselGroupsStatus = useSelector(selectVesselGroupsStatus)
const lastVisitedWorkspace = useSelector(selectLastVisitedWorkspace)
const workspaceToNavigate = useSelector(selectVesselGroupWorkspaceToNavigate)
const searchQuery = useSelector(selectSearchQuery)
const loading =
searchVesselStatus === AsyncReducerStatus.Loading ||
Expand All @@ -92,7 +91,7 @@ function VesselGroupModal(): React.ReactElement {
const vesselGroupSearchVessels = useSelector(selectAllVesselGroupSearchVessels)
const hasVesselsOverflow = useSelector(selectHasVesselGroupVesselsOverflow)
const hasVesselGroupsVessels = useSelector(selectHasVesselGroupSearchVessels)
const urlDataviewInstances = useSelector(selectUrlDataviewInstances)
const vesselGroupsInWorkspace = useSelector(selectWorkspaceVessselGroupsIds)
const { upsertDataviewInstance } = useDataviewInstancesConnect()
const searchVesselGroupsVesselsRef = useRef<any>()
const searchVesselGroupsVesselsAllowed = vesselGroupVessels
Expand Down Expand Up @@ -175,41 +174,12 @@ function VesselGroupModal(): React.ReactElement {
}
}, [dispatchSearchVesselsGroupsThunk, vesselGroupVessels, searchIdField])

const getDataviewInstancesWithVesselGroups = useCallback(
(vesselGroupId: string) => {
if (currentDataviewIds?.length) {
const dataviewInstances = currentDataviewIds.map((currentDataviewId) => {
let config = {
filters: {
'vessel-groups': [vesselGroupId],
},
}
const currentDataviewInstance = urlDataviewInstances?.find(
(dvi) => dvi?.id === currentDataviewId
)

if (currentDataviewInstance) {
config = {
filters: {
...(currentDataviewInstance.config?.filters || {}),
'vessel-groups': [vesselGroupId],
},
}
}
return { id: currentDataviewId, config }
})
return dataviewInstances
}
},
[currentDataviewIds, urlDataviewInstances]
)

const onCreateGroupClick = useCallback(
async (
e: React.MouseEvent<Element, MouseEvent>,
{ addToDataviews = false, navigateToWorkspace = false } = {}
) => {
setButtonLoading(navigateToWorkspace ? 'saveAndNavigate' : 'save')
setButtonLoading(navigateToWorkspace ? 'saveAndSeeInWorkspace' : 'save')
const vessels: VesselGroupVessel[] = vesselGroupSearchVessels.map((vessel) => {
return {
vesselId: getVesselId(vessel),
Expand All @@ -223,7 +193,7 @@ function VesselGroupModal(): React.ReactElement {
name: groupName,
vessels,
}
dispatchedAction = await dispatch(updateVesselGroupThunk(vesselGroup))
dispatchedAction = await dispatch(updateVesselGroupVesselsThunk(vesselGroup))
} else {
const vesselGroup = {
name: groupName,
Expand All @@ -234,18 +204,25 @@ function VesselGroupModal(): React.ReactElement {
}

if (
updateVesselGroupThunk.fulfilled.match(dispatchedAction) ||
updateVesselGroupVesselsThunk.fulfilled.match(dispatchedAction) ||
createVesselGroupThunk.fulfilled.match(dispatchedAction)
) {
const dataviewInstances = getDataviewInstancesWithVesselGroups(dispatchedAction.payload.id)
if (navigateToWorkspace && dataviewInstances) {
if (lastVisitedWorkspace) {
const { type, ...rest } = lastVisitedWorkspace
const vesselGroupId = updateVesselGroupVesselsThunk.fulfilled.match(dispatchedAction)
? (dispatchedAction.payload?.payload as VesselGroup)?.id
: dispatchedAction.payload?.id
const isVesselGroupInWorkspace = vesselGroupsInWorkspace.includes(vesselGroupId)
const dataviewInstance = !isVesselGroupInWorkspace
? getVesselGroupDataviewInstance(vesselGroupId)
: undefined
if (navigateToWorkspace && dataviewInstance) {
if (workspaceToNavigate) {
const { type, ...rest } = workspaceToNavigate
const { query, payload, replaceQuery } = rest
const dataviewInstancesMerged = addToDataviews
? mergeDataviewIntancesToUpsert(dataviewInstances, rest.query.dataviewInstances)
: rest.query.dataviewInstances
// TODO ensure we don't insert duplicates in the new dataview instance
const dataviewInstancesMerged = mergeDataviewIntancesToUpsert(
dataviewInstance,
rest.query.dataviewInstances
)

dispatch(
updateLocation(type as ROUTE_TYPES, {
query: { ...query, dataviewInstances: dataviewInstancesMerged },
Expand All @@ -255,12 +232,12 @@ function VesselGroupModal(): React.ReactElement {
)
} else if (searchQuery) {
// TODO check if is search location and navigate back to workspace
upsertDataviewInstance(dataviewInstances)
upsertDataviewInstance(dataviewInstance)
// dispatchQueryParams({ query: undefined })
}
resetSidebarScroll()
} else if (addToDataviews && dataviewInstances) {
upsertDataviewInstance(dataviewInstances)
} else if (addToDataviews && dataviewInstance) {
upsertDataviewInstance(dataviewInstance)
}
close()
setButtonLoading('')
Expand All @@ -280,9 +257,9 @@ function VesselGroupModal(): React.ReactElement {
groupName,
dispatch,
createAsPublic,
getDataviewInstancesWithVesselGroups,
vesselGroupsInWorkspace,
close,
lastVisitedWorkspace,
workspaceToNavigate,
searchQuery,
upsertDataviewInstance,
]
Expand All @@ -308,6 +285,7 @@ function VesselGroupModal(): React.ReactElement {
appSelector={ROOT_DOM_ELEMENT}
title={t('vesselGroup.vesselGroup', 'Vessel group')}
isOpen={isModalOpen}
className={styles.modal}
contentClassName={styles.modalContainer}
onClose={() => onBackClick('close')}
fullScreen={true}
Expand Down Expand Up @@ -417,7 +395,7 @@ function VesselGroupModal(): React.ReactElement {
disabled={confirmButtonDisabled}
onClick={(e) => onCreateGroupClick(e, { addToDataviews: false })}
loading={loading && buttonLoading === 'save'}
type="secondary"
type={workspaceToNavigate ? 'secondary' : 'default'}
tooltip={
confirmButtonTooltip ||
t(
Expand All @@ -428,17 +406,19 @@ function VesselGroupModal(): React.ReactElement {
>
{t('vesselGroup.saveForLater', 'Save for later')}
</Button>
<Button
className={styles.footerButton}
disabled={confirmButtonDisabled}
onClick={(e) =>
onCreateGroupClick(e, { addToDataviews: true, navigateToWorkspace: true })
}
loading={loading && buttonLoading === 'saveAndNavigate'}
tooltip={confirmButtonTooltip}
>
{t('vesselGroup.saveAndFilter', 'Save and filter workspace')}
</Button>
{workspaceToNavigate && (
<Button
className={styles.footerButton}
disabled={confirmButtonDisabled}
onClick={(e) =>
onCreateGroupClick(e, { addToDataviews: true, navigateToWorkspace: true })
}
loading={loading && buttonLoading === 'saveAndSeeInWorkspace'}
tooltip={confirmButtonTooltip}
>
{t('vesselGroup.saveAndSeeInWorkspace', 'Save and see in workspace')}
</Button>
)}
</Fragment>
))}
</div>
Expand Down
29 changes: 26 additions & 3 deletions apps/fishing-map/features/vessel-groups/vessel-groups.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { createSelector } from '@reduxjs/toolkit'
import { isAdvancedSearchAllowed } from 'features/search/search.selectors'
import { selectUrlDataviewInstances } from 'routes/routes.selectors'
import { selectLocationQuery, selectUrlDataviewInstances } from 'routes/routes.selectors'
import {
MAX_VESSEL_GROUP_VESSELS,
selectNewVesselGroupSearchVessels,
selectVesselGroupSearchVessels,
} from 'features/vessel-groups/vessel-groups.slice'
import { selectWorkspaceDataviewInstances } from 'features/workspace/workspace.selectors'
import {
selectLastVisitedWorkspace,
selectWorkspace,
selectWorkspaceDataviewInstances,
} from 'features/workspace/workspace.selectors'
import { selectHasUserGroupsPermissions } from 'features/user/selectors/user.permissions.selectors'
import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.selectors'
import { LastWorkspaceVisited } from 'features/workspace/workspace.slice'
import { WORKSPACE } from 'routes/routes'
import { DEFAULT_WORKSPACE_CATEGORY, DEFAULT_WORKSPACE_ID } from 'data/workspaces'

export const selectAllVesselGroupSearchVessels = createSelector(
[selectVesselGroupSearchVessels, selectNewVesselGroupSearchVessels],
Expand Down Expand Up @@ -47,6 +53,23 @@ export const selectWorkspaceVessselGroupsIds = createSelector(
}
)

export const selectVesselGroupWorkspaceToNavigate = createSelector(
[selectLastVisitedWorkspace, selectWorkspace, selectLocationQuery],
(lastVisitedWorkspace, workspace, query): LastWorkspaceVisited => {
if (lastVisitedWorkspace) {
return lastVisitedWorkspace
}
return {
type: WORKSPACE,
payload: {
category: workspace?.category || DEFAULT_WORKSPACE_CATEGORY,
workspaceId: workspace?.id || DEFAULT_WORKSPACE_ID,
},
query: query,
}
}
)

export const selectIsVessselGroupsFiltering = createSelector(
[selectWorkspaceVessselGroupsIds],
(workspaceVesselGroupIds = []) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const MAX_VESSEL_GROUP_VESSELS = 1000
export const MAX_VESSEL_GROUP_SEARCH_VESSELS = 400

export type IdField = 'vesselId' | 'mmsi'
export type VesselGroupConfirmationMode = 'save' | 'saveAndNavigate'
export type VesselGroupConfirmationMode = 'save' | 'saveAndSeeInWorkspace'

interface VesselGroupsState extends AsyncReducer<VesselGroup> {
isModalOpen: boolean
Expand Down
7 changes: 6 additions & 1 deletion apps/fishing-map/features/workspace/workspace.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ import {
} from './workspace.selectors'
import { parseUpsertWorkspace } from './workspace.utils'

type LastWorkspaceVisited = { type: ROUTE_TYPES; payload: any; query: any; replaceQuery?: boolean }
export type LastWorkspaceVisited = {
type: ROUTE_TYPES
payload: any
query: any
replaceQuery?: boolean
}

interface WorkspaceSliceState {
status: AsyncReducerStatus
Expand Down
1 change: 1 addition & 0 deletions apps/fishing-map/public/locales/source/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,7 @@
"remove": "Remove vessel group",
"removeVessel": "Remove vessel from group",
"saveAndFilter": "Save and filter workspace",
"saveAndSeeInWorkspace": "Save and see in workspace",
"saveForLater": "Save for later",
"saveForLaterTooltip": "You'll find the group in the activity layers filters or your user panel button",
"searchLimit": "Search is limited up to {{limit}} vessels",
Expand Down

0 comments on commit 33af5df

Please sign in to comment.