From e74da3eaded9f6162555345d95eb8793083bb6d5 Mon Sep 17 00:00:00 2001 From: satellitestudiodesign Date: Tue, 14 Jan 2025 13:06:24 +0100 Subject: [PATCH] use event icons in individual timeseries --- .../shared/events/EventsReportGraph.tsx | 8 ++++--- .../shared/events/icons/event-encounter.svg | 7 ++++++ .../shared/events/icons/event-loitering.svg | 7 ++++++ .../shared/events/icons/event-port.svg | 11 +++++++++ .../vessel-groups/events/VGREvents.tsx | 21 +++++++++++++---- .../charts/points/IndividualPoint.module.css | 9 ++++---- .../src/charts/points/IndividualPoint.tsx | 23 ++++++++++++++----- .../src/charts/timeseries/Timeseries.tsx | 2 ++ .../timeseries/TimeseriesIndividual.tsx | 4 ++++ .../src/charts/types.ts | 1 + 10 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 apps/fishing-map/features/reports/shared/events/icons/event-encounter.svg create mode 100644 apps/fishing-map/features/reports/shared/events/icons/event-loitering.svg create mode 100644 apps/fishing-map/features/reports/shared/events/icons/event-port.svg diff --git a/apps/fishing-map/features/reports/shared/events/EventsReportGraph.tsx b/apps/fishing-map/features/reports/shared/events/EventsReportGraph.tsx index 5b63381fee..47c2df4181 100644 --- a/apps/fishing-map/features/reports/shared/events/EventsReportGraph.tsx +++ b/apps/fishing-map/features/reports/shared/events/EventsReportGraph.tsx @@ -1,10 +1,10 @@ +import type { ReactElement } from 'react' import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { DateTime } from 'luxon' import { groupBy } from 'es-toolkit' import { stringify } from 'qs' -import { getEventsStatsQuery } from 'queries/report-events-stats-api' import type { BaseReportEventsVesselsParamsFilters } from 'queries/report-events-stats-api' +import { getEventsStatsQuery } from 'queries/report-events-stats-api' import { getFourwingsInterval, type FourwingsInterval } from '@globalfishingwatch/deck-loaders' import type { BaseResponsiveTimeseriesProps } from '@globalfishingwatch/responsive-visualizations' import { ResponsiveTimeseries } from '@globalfishingwatch/responsive-visualizations' @@ -76,6 +76,7 @@ export default function EventsReportGraph({ end, start, timeseries, + icon, }: { datasetId: string filters?: BaseReportEventsVesselsParamsFilters @@ -84,9 +85,9 @@ export default function EventsReportGraph({ end: string start: string timeseries: { date: string; value: number }[] + icon?: ReactElement }) { const containerRef = React.useRef(null) - const { t } = useTranslation() const startMillis = DateTime.fromISO(start).toMillis() const endMillis = DateTime.fromISO(end).toMillis() @@ -130,6 +131,7 @@ export default function EventsReportGraph({ aggregatedTooltip={} individualTooltip={} color={color} + individualIcon={icon} /> ) diff --git a/apps/fishing-map/features/reports/shared/events/icons/event-encounter.svg b/apps/fishing-map/features/reports/shared/events/icons/event-encounter.svg new file mode 100644 index 0000000000..fb3c3821eb --- /dev/null +++ b/apps/fishing-map/features/reports/shared/events/icons/event-encounter.svg @@ -0,0 +1,7 @@ + + + Rectangle Copy 4 + + + + \ No newline at end of file diff --git a/apps/fishing-map/features/reports/shared/events/icons/event-loitering.svg b/apps/fishing-map/features/reports/shared/events/icons/event-loitering.svg new file mode 100644 index 0000000000..fb22d59d69 --- /dev/null +++ b/apps/fishing-map/features/reports/shared/events/icons/event-loitering.svg @@ -0,0 +1,7 @@ + + + Combined Shape Copy 3 + + + + \ No newline at end of file diff --git a/apps/fishing-map/features/reports/shared/events/icons/event-port.svg b/apps/fishing-map/features/reports/shared/events/icons/event-port.svg new file mode 100644 index 0000000000..31d5aba2dd --- /dev/null +++ b/apps/fishing-map/features/reports/shared/events/icons/event-port.svg @@ -0,0 +1,11 @@ + + + + square + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/fishing-map/features/reports/vessel-groups/events/VGREvents.tsx b/apps/fishing-map/features/reports/vessel-groups/events/VGREvents.tsx index 376c1447fc..9742863453 100644 --- a/apps/fishing-map/features/reports/vessel-groups/events/VGREvents.tsx +++ b/apps/fishing-map/features/reports/vessel-groups/events/VGREvents.tsx @@ -1,18 +1,19 @@ import { useSelector } from 'react-redux' +import type { ReactElement } from 'react' import { Fragment } from 'react' import parse from 'html-react-parser' import { DateTime } from 'luxon' import { useTranslation } from 'react-i18next' import { lowerCase } from 'es-toolkit' -import { - useGetReportEventsStatsQuery, - useGetReportEventsVesselsQuery, -} from 'queries/report-events-stats-api' import type { ReportEventsStatsResponseGroups, ReportEventsVesselsParams, ReportEventsStatsParams, } from 'queries/report-events-stats-api' +import { + useGetReportEventsStatsQuery, + useGetReportEventsVesselsQuery, +} from 'queries/report-events-stats-api' import { Icon } from '@globalfishingwatch/ui-components' import { DatasetTypes } from '@globalfishingwatch/api-types' import { getDataviewFilters } from '@globalfishingwatch/dataviews-client' @@ -50,6 +51,9 @@ import ReportEventsPlaceholder from 'features/reports/shared/placeholders/Report import { selectVGREventsVesselsPaginated } from 'features/reports/vessel-groups/events/vgr-events.selectors' import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors' import VGREventsVesselsTableFooter from '../../shared/events/EventsReportVesselsTableFooter' +import EncounterIcon from '../../shared/events/icons/event-encounter.svg' +import LoiteringIcon from '../../shared/events/icons/event-loitering.svg' +import PortVisitIcon from '../../shared/events/icons/event-port.svg' import styles from './VGREvents.module.css' function VGREvents() { @@ -136,6 +140,14 @@ function VGREvents() { } const eventDataset = eventsDataview?.datasets?.find((d) => d.type === DatasetTypes.Events) const subCategoryDatasetCategory = eventDataset?.subcategory + let icon: ReactElement | undefined + if (subCategoryDatasetCategory === 'encounter') { + icon = + } else if (subCategoryDatasetCategory === 'loitering') { + icon = + } else if (subCategoryDatasetCategory === 'port_visit') { + icon = + } const totalEvents = data.timeseries.reduce((acc, group) => acc + group.value, 0) return ( @@ -182,6 +194,7 @@ function VGREvents() { start={start} end={end} timeseries={data.timeseries || []} + icon={icon} /> )} diff --git a/libs/responsive-visualizations/src/charts/points/IndividualPoint.module.css b/libs/responsive-visualizations/src/charts/points/IndividualPoint.module.css index 585ec0051e..7e77342bf9 100644 --- a/libs/responsive-visualizations/src/charts/points/IndividualPoint.module.css +++ b/libs/responsive-visualizations/src/charts/points/IndividualPoint.module.css @@ -1,13 +1,14 @@ .point { - display: block; - background-color: red; - border-radius: 50%; position: relative; border: 2px solid transparent; transition: border-color 0.3s linear; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; } -.point:hover { +.point:not(.withIcon):hover { border: var(--border-thick); } diff --git a/libs/responsive-visualizations/src/charts/points/IndividualPoint.tsx b/libs/responsive-visualizations/src/charts/points/IndividualPoint.tsx index 12b61d2594..3b0517f5ac 100644 --- a/libs/responsive-visualizations/src/charts/points/IndividualPoint.tsx +++ b/libs/responsive-visualizations/src/charts/points/IndividualPoint.tsx @@ -1,4 +1,12 @@ -import { useFloating, offset, flip, shift, useInteractions, useHover, FloatingPortal } from '@floating-ui/react' +import { + useFloating, + offset, + flip, + shift, + useInteractions, + useHover, + FloatingPortal, +} from '@floating-ui/react' import { cloneElement, useState, type ReactElement } from 'react' import cx from 'classnames' import type { ResponsiveVisualizationItem } from '../../types' @@ -10,9 +18,10 @@ type IndividualPointProps = { point: ResponsiveVisualizationItem tooltip?: ReactElement className?: string + icon?: ReactElement } -export function IndividualPoint({ point, color, tooltip, className }: IndividualPointProps) { +export function IndividualPoint({ point, color, tooltip, className, icon }: IndividualPointProps) { const [isOpen, setIsOpen] = useState(false) const { refs, floatingStyles, context } = useFloating({ @@ -29,13 +38,14 @@ export function IndividualPoint({ point, color, tooltip, className }: Individual
  • {isOpen && ( @@ -50,6 +60,7 @@ export function IndividualPoint({ point, color, tooltip, className }: Individual )} + {icon && {icon}}
  • ) } diff --git a/libs/responsive-visualizations/src/charts/timeseries/Timeseries.tsx b/libs/responsive-visualizations/src/charts/timeseries/Timeseries.tsx index d5b2c1c248..e41cee96fa 100644 --- a/libs/responsive-visualizations/src/charts/timeseries/Timeseries.tsx +++ b/libs/responsive-visualizations/src/charts/timeseries/Timeseries.tsx @@ -37,6 +37,7 @@ export function ResponsiveTimeseries({ individualTooltip, onIndividualItemClick, onAggregatedItemClick, + individualIcon, }: ResponsiveTimeseriesProps) { const containerRef = useRef(null) const { width, data, isIndividualSupported } = useResponsiveVisualization(containerRef, { @@ -73,6 +74,7 @@ export function ResponsiveTimeseries({ onClick={onIndividualItemClick} tickLabelFormatter={tickLabelFormatter} customTooltip={individualTooltip} + icon={individualIcon} /> ) : ( & { width: number + icon?: ReactElement } export function IndividualTimeseries({ @@ -23,6 +25,7 @@ export function IndividualTimeseries({ timeseriesInterval, tickLabelFormatter, customTooltip, + icon, }: IndividualTimeseriesProps) { const domain = useTimeseriesDomain({ start, end, timeseriesInterval }) const fullTimeseries = useFullTimeseries({ @@ -62,6 +65,7 @@ export function IndividualTimeseries({ point={point} color={color} tooltip={customTooltip} + icon={icon} /> ))} diff --git a/libs/responsive-visualizations/src/charts/types.ts b/libs/responsive-visualizations/src/charts/types.ts index 1c8441a346..eea1871879 100644 --- a/libs/responsive-visualizations/src/charts/types.ts +++ b/libs/responsive-visualizations/src/charts/types.ts @@ -22,6 +22,7 @@ export type BaseResponsiveChartProps = { onIndividualItemClick?: ResponsiveVisualizationInteractionCallback getIndividualData?: () => Promise | undefined> individualValueKey?: keyof ResponsiveVisualizationData<'individual'>[0] + individualIcon?: ReactElement } export type ResponsiveVisualizationAnyItemKey =