-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
31f05d5
commit 55ca073
Showing
9 changed files
with
185 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
frontend/src/features/VesselSidebar/actions/TrackRequest/HighlightPositionCell.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { useMainAppDispatch } from '@hooks/useMainAppDispatch' | ||
import { useMainAppSelector } from '@hooks/useMainAppSelector' | ||
import { transform } from 'ol/proj' | ||
import styled from 'styled-components' | ||
|
||
import { getCoordinates } from '../../../../coordinates' | ||
import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../../domain/entities/map/constants' | ||
import { animateToCoordinates } from '../../../../domain/shared_slices/Map' | ||
import { highlightVesselTrackPosition } from '../../../../domain/shared_slices/Vessel' | ||
import ManualPositionSVG from '../../../icons/Pastille_position_manuelle.svg?react' | ||
|
||
import type { VesselPosition } from '../../../../domain/entities/vessel/types' | ||
|
||
type HighlightPositionCellProps = { | ||
isManualPositionMarkerShowed?: boolean | ||
row: VesselPosition | ||
value: unknown | ||
} | ||
export function HighlightPositionCell({ isManualPositionMarkerShowed, row, value }: HighlightPositionCellProps) { | ||
const dispatch = useMainAppDispatch() | ||
const coordinatesFormat = useMainAppSelector(state => state.map.coordinatesFormat) | ||
|
||
const coordinates = getCoordinates([row.longitude, row.latitude], WSG84_PROJECTION, coordinatesFormat) | ||
const olCoordinates = transform([row.longitude, row.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION) | ||
|
||
return ( | ||
<span | ||
onClick={() => dispatch(animateToCoordinates(olCoordinates))} | ||
onFocus={() => dispatch(highlightVesselTrackPosition(row))} | ||
onKeyDown={() => dispatch(animateToCoordinates(olCoordinates))} | ||
onMouseEnter={() => dispatch(highlightVesselTrackPosition(row))} | ||
role="presentation" | ||
style={{ cursor: 'pointer' }} | ||
title={row && coordinates ? `${coordinates[0]} ${coordinates[1]}` : ''} | ||
> | ||
{value as string} | ||
{isManualPositionMarkerShowed && row.isManual ? <ManualPosition title="Position manuelle (4h-report)" /> : ''} | ||
</span> | ||
) | ||
} | ||
|
||
const ManualPosition = styled(ManualPositionSVG)` | ||
margin-left: 3px; | ||
vertical-align: sub; | ||
` |
186 changes: 20 additions & 166 deletions
186
frontend/src/features/VesselSidebar/actions/TrackRequest/PositionsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,188 +1,42 @@ | ||
import { transform } from 'ol/proj' | ||
import { useCallback, useEffect, useRef, useState } from 'react' | ||
import { Table } from 'rsuite' | ||
import { POSITION_TABLE_COLUMNS } from '@features/VesselSidebar/actions/TrackRequest/constants' | ||
import { useClickOutsideWhenOpened } from '@hooks/useClickOutsideWhenOpened' | ||
import { useMainAppDispatch } from '@hooks/useMainAppDispatch' | ||
import { useMainAppSelector } from '@hooks/useMainAppSelector' | ||
import { DataTable } from '@mtes-mct/monitor-ui' | ||
import { useEffect, useRef } from 'react' | ||
import styled from 'styled-components' | ||
|
||
import { getCoordinates } from '../../../../coordinates' | ||
import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../../domain/entities/map/constants' | ||
import { animateToCoordinates } from '../../../../domain/shared_slices/Map' | ||
import { highlightVesselTrackPosition } from '../../../../domain/shared_slices/Vessel' | ||
import { useClickOutsideWhenOpened } from '../../../../hooks/useClickOutsideWhenOpened' | ||
import { useMainAppDispatch } from '../../../../hooks/useMainAppDispatch' | ||
import { useMainAppSelector } from '../../../../hooks/useMainAppSelector' | ||
import { isNumeric } from '../../../../utils/isNumeric' | ||
import ManualPositionSVG from '../../../icons/Pastille_position_manuelle.svg?react' | ||
import { CSVOptions } from '../../../VesselList/dataFormatting' | ||
import { sortArrayByColumn, SortType } from '../../../VesselList/tableSort' | ||
|
||
import type { VesselPosition } from 'domain/entities/vessel/types' | ||
import type { CellProps } from 'rsuite' | ||
|
||
const { Cell, Column, HeaderCell } = Table | ||
|
||
export function PositionsTable({ openBox }) { | ||
const dispatch = useMainAppDispatch() | ||
const { coordinatesFormat } = useMainAppSelector(state => state.map) | ||
const { highlightedVesselTrackPosition, selectedVesselPositions } = useMainAppSelector(state => state.vessel) | ||
|
||
const [sortColumn, setSortColumn] = useState(CSVOptions.dateTime.code) | ||
const [sortType, setSortType] = useState(SortType.DESC) | ||
const wrapperRef = useRef(null) | ||
const clickedOutsideComponent = useClickOutsideWhenOpened(wrapperRef, openBox) | ||
|
||
const handleSortColumn = useCallback((nextSortColumn, nextSortType) => { | ||
setSortColumn(nextSortColumn) | ||
setSortType(nextSortType) | ||
}, []) | ||
|
||
const getPositions = useCallback(() => { | ||
if (sortColumn && sortType && Array.isArray(selectedVesselPositions)) { | ||
return selectedVesselPositions.slice().sort((a, b) => sortArrayByColumn(a, b, sortColumn, sortType)) | ||
} | ||
|
||
return [] | ||
}, [sortColumn, sortType, selectedVesselPositions]) | ||
|
||
useEffect(() => { | ||
if (clickedOutsideComponent && highlightedVesselTrackPosition) { | ||
dispatch(highlightVesselTrackPosition(null)) | ||
} | ||
}, [clickedOutsideComponent, dispatch, highlightedVesselTrackPosition]) | ||
|
||
return ( | ||
<div ref={wrapperRef}> | ||
<Table | ||
data={getPositions()} | ||
height={400} | ||
onSortColumn={handleSortColumn} | ||
rowHeight={36} | ||
shouldUpdateScroll={false} | ||
sortColumn={sortColumn} | ||
sortType={sortType} | ||
virtualized | ||
> | ||
<Column flexGrow={1} sortable> | ||
<HeaderCell>GDH</HeaderCell> | ||
<DateTimeCell coordinatesFormat={coordinatesFormat} dataKey="dateTime" /> | ||
</Column> | ||
<Column fixed sortable width={96}> | ||
<HeaderCell>Vitesse</HeaderCell> | ||
<SpeedCell coordinatesFormat={coordinatesFormat} dataKey="speed" /> | ||
</Column> | ||
<Column fixed sortable width={64}> | ||
<HeaderCell>Cap</HeaderCell> | ||
<CourseCell coordinatesFormat={coordinatesFormat} dataKey="course" /> | ||
</Column> | ||
</Table> | ||
</div> | ||
) | ||
} | ||
|
||
type SpeedCellProps = CellProps<VesselPosition> & { | ||
coordinatesFormat: string | ||
dataKey: string | ||
rowData?: { | ||
latitude: number | ||
longitude: number | ||
} | ||
} | ||
export function SpeedCell({ coordinatesFormat, dataKey, rowData, ...nativeProps }: SpeedCellProps) { | ||
const dispatch = useMainAppDispatch() | ||
|
||
const coordinates = rowData | ||
? getCoordinates([rowData.longitude, rowData.latitude], WSG84_PROJECTION, coordinatesFormat) | ||
: '' | ||
const olCoordinates = rowData | ||
? transform([rowData.longitude, rowData.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION) | ||
: [] | ||
|
||
return ( | ||
<Cell | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...nativeProps} | ||
onClick={() => dispatch(animateToCoordinates(olCoordinates))} | ||
onMouseEnter={() => dispatch(highlightVesselTrackPosition(rowData))} | ||
style={{ cursor: 'pointer' }} | ||
title={rowData && coordinates ? `${coordinates[0]} ${coordinates[1]}` : ''} | ||
> | ||
{!rowData || !isNumeric(rowData[dataKey]) ? '' : `${rowData[dataKey]} nds`} | ||
</Cell> | ||
) | ||
} | ||
|
||
type CourseCellProps = CellProps<VesselPosition> & { | ||
coordinatesFormat: string | ||
dataKey: string | ||
rowData?: { | ||
latitude: number | ||
longitude: number | ||
} | ||
} | ||
export function CourseCell({ coordinatesFormat, dataKey, rowData, ...nativeProps }: CourseCellProps) { | ||
const dispatch = useMainAppDispatch() | ||
|
||
const coordinates = rowData | ||
? getCoordinates([rowData.longitude, rowData.latitude], WSG84_PROJECTION, coordinatesFormat) | ||
: '' | ||
const olCoordinates = rowData | ||
? transform([rowData.longitude, rowData.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION) | ||
: [] | ||
|
||
return ( | ||
<Cell | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...nativeProps} | ||
onClick={() => dispatch(animateToCoordinates(olCoordinates))} | ||
onMouseEnter={() => dispatch(highlightVesselTrackPosition(rowData))} | ||
style={{ cursor: 'pointer' }} | ||
title={rowData && coordinates ? `${coordinates[0]} ${coordinates[1]}` : ''} | ||
> | ||
{rowData && (rowData[dataKey] || rowData[dataKey] === 0) ? `${rowData[dataKey]}°` : ''} | ||
</Cell> | ||
) | ||
} | ||
|
||
type DateTimeCellProps = CellProps<VesselPosition> & { | ||
coordinatesFormat: string | ||
dataKey: string | ||
rowData?: { | ||
latitude: number | ||
longitude: number | ||
} | ||
} | ||
export function DateTimeCell({ coordinatesFormat, dataKey, rowData, ...nativeProps }: DateTimeCellProps) { | ||
const dispatch = useMainAppDispatch() | ||
|
||
const coordinates = rowData | ||
? getCoordinates([rowData.longitude, rowData.latitude], WSG84_PROJECTION, coordinatesFormat) | ||
: '' | ||
const olCoordinates = rowData | ||
? transform([rowData.longitude, rowData.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION) | ||
: [] | ||
|
||
let dateTimeStringWithoutMilliSeconds: string | undefined = rowData ? rowData[dataKey].split('.')[0] : undefined | ||
if (!!rowData && rowData[dataKey].includes('Z') && !dateTimeStringWithoutMilliSeconds?.includes('Z')) { | ||
dateTimeStringWithoutMilliSeconds += 'Z' | ||
} else if (!!rowData && rowData[dataKey].includes('+') && !dateTimeStringWithoutMilliSeconds?.includes('+')) { | ||
dateTimeStringWithoutMilliSeconds += `+${rowData[dataKey].split('+')[1]}` | ||
} | ||
|
||
return ( | ||
<Cell | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...nativeProps} | ||
onClick={() => dispatch(animateToCoordinates(olCoordinates))} | ||
onMouseEnter={() => dispatch(highlightVesselTrackPosition(rowData))} | ||
style={{ cursor: 'pointer' }} | ||
title={rowData && coordinates ? `${coordinates[0]} ${coordinates[1]}` : ''} | ||
> | ||
{dateTimeStringWithoutMilliSeconds}{' '} | ||
{rowData?.isManual ? <ManualPosition title="Position manuelle (4h-report)" /> : ''} | ||
</Cell> | ||
<Wrapper ref={wrapperRef}> | ||
<DataTable | ||
// TODO Why `accessorFn` is not defined ? | ||
columns={POSITION_TABLE_COLUMNS as any} | ||
data={selectedVesselPositions?.map((position, index) => ({ | ||
...position, | ||
id: index | ||
}))} | ||
initialSorting={[{ desc: true, id: 'dateTime' }]} | ||
/> | ||
</Wrapper> | ||
) | ||
} | ||
|
||
const ManualPosition = styled(ManualPositionSVG)` | ||
margin-left: 3px; | ||
vertical-align: sub; | ||
const Wrapper = styled.div` | ||
max-height: 500px; | ||
overflow: auto; | ||
` |
Oops, something went wrong.