From f235b7f1c6acf43d0297cf26d76c2a2de629d6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gonz=C3=A1lez=20Mu=C3=B1oz?= Date: Tue, 15 Oct 2024 18:35:53 +0200 Subject: [PATCH 1/8] fetches data for terrestrial countries and countries popups --- .../map/content/map/popup/regions/index.tsx | 29 ++++++++++++++----- frontend/translations/en.json | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index 03ca47ee..11014585 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -32,6 +32,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom); const layerQuery = useGetLayersId<{ + environment: LayerTyped['environment']['data']['attributes']; source: LayerTyped['config']['source']; click: LayerTyped['interaction_config']['events'][0]; }>( @@ -40,12 +41,13 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore locale, - populate: 'metadata', + populate: 'metadata,environment', }, { query: { select: ({ data }) => ({ - source: (data.attributes as LayerTyped).config?.source, + environment: (data.attributes as LayerTyped)?.environment?.data?.attributes, + source: (data.attributes as LayerTyped)?.config?.source, click: (data.attributes as LayerTyped)?.interaction_config?.events.find( (ev) => ev.type === 'click' ), @@ -54,7 +56,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { } ); - const { source } = layerQuery.data; + const { source, environment } = layerQuery.data || {}; const DATA = useMemo(() => { if (source?.type === 'vector' && rendered && popup && map) { @@ -95,14 +97,14 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { locale, filters: { location: { - code: DATA?.region_id, + code: DATA?.GID_0 || DATA?.region_id, }, is_last_year: { $eq: true, }, environment: { slug: { - $eq: 'marine', + $eq: environment?.slug, }, }, }, @@ -110,7 +112,14 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { // @ts-ignore populate: { location: { - fields: ['name', 'name_es', 'name_fr', 'code', 'total_marine_area'], + fields: [ + ...(locale === 'en' ? ['name'] : []), + ...(locale === 'es' ? ['name_es'] : []), + ...(locale === 'fr' ? ['name_fr'] : []), + 'code', + 'total_marine_area', + 'total_terrestrial_area', + ], }, }, // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -121,7 +130,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { { query: { select: ({ data }) => data?.[0].attributes, - enabled: !!DATA?.region_id, + enabled: (!!DATA?.region_id || !!DATA?.GID_0) && !!environment, }, } ); @@ -198,7 +207,11 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { {!isFetching && !!protectionCoverageStats && ( <>
-
{t('marine-conservation-coverage')}
+
+ {environment?.slug === 'marine' + ? t('marine-conservation-coverage') + : t('terrestrial-conservation-coverage')} +
{formattedStats.percentage !== '-' && t.rich('percentage-bold', { diff --git a/frontend/translations/en.json b/frontend/translations/en.json index c870d329..4ab0d3fb 100644 --- a/frontend/translations/en.json +++ b/frontend/translations/en.json @@ -253,6 +253,7 @@ "no-data-available": "No data available", "no-results": "No results.", "marine-conservation-coverage": "Marine Conservation Coverage", + "terrestrial-conservation-coverage": "Terrestrial Conservation Coverage", "percentage": "{percentage}%", "percentage-bold": "{percentage}%", "marine-protected-area": "{protectedArea} km² out of {totalArea} km²", From 0eb7f27afa76fa248886b96f31479e113f963fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gonz=C3=A1lez=20Mu=C3=B1oz?= Date: Tue, 15 Oct 2024 19:16:34 +0200 Subject: [PATCH 2/8] relies on local environment --- .../map/content/map/popup/regions/index.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index 11014585..26acef1c 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -11,6 +11,7 @@ import { useLocale, useTranslations } from 'next-intl'; import { PAGES } from '@/constants/pages'; import { useMapSearchParams } from '@/containers/map/content/map/sync-settings'; import { layersInteractiveIdsAtom, popupAtom } from '@/containers/map/store'; +import { useSyncMapContentSettings } from '@/containers/map/sync-settings'; import { formatPercentage, formatKM } from '@/lib/utils/formats'; import { FCWithMessages } from '@/types'; import { useGetLayersId } from '@/types/generated/layer'; @@ -30,9 +31,9 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const [popup, setPopup] = useAtom(popupAtom); const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom); + const [{ tab }] = useSyncMapContentSettings(); const layerQuery = useGetLayersId<{ - environment: LayerTyped['environment']['data']['attributes']; source: LayerTyped['config']['source']; click: LayerTyped['interaction_config']['events'][0]; }>( @@ -41,12 +42,11 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore locale, - populate: 'metadata,environment', + populate: 'metadata', }, { query: { select: ({ data }) => ({ - environment: (data.attributes as LayerTyped)?.environment?.data?.attributes, source: (data.attributes as LayerTyped)?.config?.source, click: (data.attributes as LayerTyped)?.interaction_config?.events.find( (ev) => ev.type === 'click' @@ -56,7 +56,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { } ); - const { source, environment } = layerQuery.data || {}; + const { source } = layerQuery.data || {}; const DATA = useMemo(() => { if (source?.type === 'vector' && rendered && popup && map) { @@ -104,7 +104,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { }, environment: { slug: { - $eq: environment?.slug, + $eq: tab, }, }, }, @@ -130,7 +130,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { { query: { select: ({ data }) => data?.[0].attributes, - enabled: (!!DATA?.region_id || !!DATA?.GID_0) && !!environment, + enabled: !!DATA?.region_id || !!DATA?.GID_0, }, } ); @@ -208,7 +208,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { <>
- {environment?.slug === 'marine' + {tab === 'marine' ? t('marine-conservation-coverage') : t('terrestrial-conservation-coverage')}
From 27b57c70f78d7f54a05bddb0d68332ffe34145e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 09:33:08 +0200 Subject: [PATCH 3/8] Display popups in the Summary tab --- .../map/content/map/popup/regions/index.tsx | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index 26acef1c..d0422f05 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -11,7 +11,6 @@ import { useLocale, useTranslations } from 'next-intl'; import { PAGES } from '@/constants/pages'; import { useMapSearchParams } from '@/containers/map/content/map/sync-settings'; import { layersInteractiveIdsAtom, popupAtom } from '@/containers/map/store'; -import { useSyncMapContentSettings } from '@/containers/map/sync-settings'; import { formatPercentage, formatKM } from '@/lib/utils/formats'; import { FCWithMessages } from '@/types'; import { useGetLayersId } from '@/types/generated/layer'; @@ -31,33 +30,38 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const [popup, setPopup] = useAtom(popupAtom); const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom); - const [{ tab }] = useSyncMapContentSettings(); - const layerQuery = useGetLayersId<{ + const { + data: { source, environment }, + } = useGetLayersId<{ source: LayerTyped['config']['source']; - click: LayerTyped['interaction_config']['events'][0]; + environment: string; }>( layerId, { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore locale, - populate: 'metadata', + fields: ['config'], + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + populate: { + environment: { + fields: ['slug'], + }, + }, }, { query: { + placeholderData: { data: {} }, select: ({ data }) => ({ source: (data.attributes as LayerTyped)?.config?.source, - click: (data.attributes as LayerTyped)?.interaction_config?.events.find( - (ev) => ev.type === 'click' - ), + environment: data.attributes?.environment?.data.attributes.slug, }), }, } ); - const { source } = layerQuery.data || {}; - const DATA = useMemo(() => { if (source?.type === 'vector' && rendered && popup && map) { const point = map.project(popup.lngLat); @@ -104,7 +108,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { }, environment: { slug: { - $eq: tab, + $eq: environment, }, }, }, @@ -208,7 +212,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { <>
- {tab === 'marine' + {environment === 'marine' ? t('marine-conservation-coverage') : t('terrestrial-conservation-coverage')}
From 1a633d248adc79ab9112063aa35c804824af3b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 09:36:38 +0200 Subject: [PATCH 4/8] Display correct figures in tooltip --- .../src/containers/map/content/map/popup/eez/index.tsx | 2 +- .../containers/map/content/map/popup/regions/index.tsx | 8 ++++++-- frontend/translations/en.json | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/eez/index.tsx b/frontend/src/containers/map/content/map/popup/eez/index.tsx index b920aa83..882b3669 100644 --- a/frontend/src/containers/map/content/map/popup/eez/index.tsx +++ b/frontend/src/containers/map/content/map/popup/eez/index.tsx @@ -205,7 +205,7 @@ const EEZLayerPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { )}
- {t('marine-protected-area', { + {t('protected-area', { protectedArea: formatKM(locale, protectionCoverageStats.protected_area), totalArea: formatKM( locale, diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index d0422f05..4f680b8c 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -232,11 +232,15 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { )}
- {t('marine-protected-area', { + {t('protected-area', { protectedArea: formattedStats.protectedArea, totalArea: formatKM( locale, - Number(protectionCoverageStats?.location.data.attributes.total_marine_area) + Number( + protectionCoverageStats?.location.data.attributes[ + environment === 'marine' ? 'total_marine_area' : 'total_terrestrial_area' + ] + ) ), })}
diff --git a/frontend/translations/en.json b/frontend/translations/en.json index 4ab0d3fb..a8b2f4b0 100644 --- a/frontend/translations/en.json +++ b/frontend/translations/en.json @@ -256,7 +256,7 @@ "terrestrial-conservation-coverage": "Terrestrial Conservation Coverage", "percentage": "{percentage}%", "percentage-bold": "{percentage}%", - "marine-protected-area": "{protectedArea} km² out of {totalArea} km²", + "protected-area": "{protectedArea} km² out of {totalArea} km²", "open-country-insights": "Open country insights", "global-coverage": "Global coverage", "area-km2": "{area} km²", From bea1012a35bf5bad9edafce4782e656ee0fe0077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 09:45:49 +0200 Subject: [PATCH 5/8] Remove popup from map if layer not active anymore --- .../containers/map/content/map/popup/regions/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index 4f680b8c..739cd9f7 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -9,7 +9,7 @@ import { useAtom, useAtomValue } from 'jotai'; import { useLocale, useTranslations } from 'next-intl'; import { PAGES } from '@/constants/pages'; -import { useMapSearchParams } from '@/containers/map/content/map/sync-settings'; +import { useMapSearchParams, useSyncMapLayers } from '@/containers/map/content/map/sync-settings'; import { layersInteractiveIdsAtom, popupAtom } from '@/containers/map/store'; import { formatPercentage, formatKM } from '@/lib/utils/formats'; import { FCWithMessages } from '@/types'; @@ -26,6 +26,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const DATA_REF = useRef(); const { default: map } = useMap(); const searchParams = useMapSearchParams(); + const [activeLayers] = useSyncMapLayers(); const { push } = useRouter(); const [popup, setPopup] = useAtom(popupAtom); @@ -199,6 +200,13 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { }; }, [map, handleMapRender]); + // Close the tooltip if the layer that was clicked is not active anymore + useEffect(() => { + if (!activeLayers.includes(layerId)) { + setPopup({}); + } + }, [layerId, activeLayers, setPopup]); + if (!DATA) return null; return ( From fb542c6956a586f4d52631a6484c1f6854ae9607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 10:25:54 +0200 Subject: [PATCH 6/8] Reuse `POPUP_PROPERTIES_BY_SOURCE` variable --- .../map/content/map/popup/constants.ts | 40 ++++++++++------ .../map/content/map/popup/index.tsx | 8 ++-- .../map/content/map/popup/regions/index.tsx | 48 +++++++++++-------- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/constants.ts b/frontend/src/containers/map/content/map/popup/constants.ts index 668445ce..a0ebf1d3 100644 --- a/frontend/src/containers/map/content/map/popup/constants.ts +++ b/frontend/src/containers/map/content/map/popup/constants.ts @@ -2,30 +2,42 @@ import { IconProps } from '@/components/ui/icon'; import Mountain from '@/styles/icons/mountain.svg'; import Wave from '@/styles/icons/wave.svg'; -export const HOVER_POPUP_PROPERTIES_BY_SOURCE = { +export const POPUP_PROPERTIES_BY_SOURCE = { 'ezz-source': { - en: 'GEONAME', - es: 'GEONAME_ES', - fr: 'GEONAME_FR', + id: 'ISO_SOV1', + name: { + en: 'GEONAME', + es: 'GEONAME_ES', + fr: 'GEONAME_FR', + }, }, 'regions-source': { - en: 'name', - es: 'name_es', - fr: 'name_fr', + id: 'region_id', + name: { + en: 'name', + es: 'name_es', + fr: 'name_fr', + }, }, 'gadm-countries': { - en: 'COUNTRY', - es: 'name_es', - fr: 'name_fr', + id: 'GID_0', + name: { + en: 'COUNTRY', + es: 'name_es', + fr: 'name_fr', + }, }, 'gadm-regions': { - en: 'name', - es: 'name_es', - fr: 'name_fr', + id: 'region_id', + name: { + en: 'name', + es: 'name_es', + fr: 'name_fr', + }, }, }; -export const HOVER_POPUP_ICON_BY_SOURCE = { +export const POPUP_ICON_BY_SOURCE = { 'ezz-source': Wave as IconProps['icon'], 'regions-source': Wave as IconProps['icon'], 'gadm-countries': Mountain as IconProps['icon'], diff --git a/frontend/src/containers/map/content/map/popup/index.tsx b/frontend/src/containers/map/content/map/popup/index.tsx index b0fb6c19..36799dcf 100644 --- a/frontend/src/containers/map/content/map/popup/index.tsx +++ b/frontend/src/containers/map/content/map/popup/index.tsx @@ -23,7 +23,7 @@ import { useGetLayers } from '@/types/generated/layer'; import { useSyncMapLayers } from '../sync-settings'; -import { HOVER_POPUP_ICON_BY_SOURCE, HOVER_POPUP_PROPERTIES_BY_SOURCE } from './constants'; +import { POPUP_ICON_BY_SOURCE, POPUP_PROPERTIES_BY_SOURCE } from './constants'; const PopupContainer: FCWithMessages = () => { const locale = useLocale(); @@ -85,10 +85,10 @@ const PopupContainer: FCWithMessages = () => { return (
- {HOVER_POPUP_ICON_BY_SOURCE[source] ? ( - + {POPUP_ICON_BY_SOURCE[source] ? ( + ) : null} - {properties[HOVER_POPUP_PROPERTIES_BY_SOURCE[source]?.[locale]] ?? null} + {properties[POPUP_PROPERTIES_BY_SOURCE[source]?.name[locale]] ?? null}
); }, [locale, popup]); diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index 739cd9f7..e34da10b 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -18,18 +18,23 @@ import { useGetProtectionCoverageStats } from '@/types/generated/protection-cove import { ProtectionCoverageStat } from '@/types/generated/strapi.schemas'; import { LayerTyped } from '@/types/layers'; +import { POPUP_PROPERTIES_BY_SOURCE } from '../constants'; + const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const t = useTranslations('containers.map'); const locale = useLocale(); const [rendered, setRendered] = useState(false); - const DATA_REF = useRef(); + + const geometryDataRef = useRef(); const { default: map } = useMap(); + const searchParams = useMapSearchParams(); const [activeLayers] = useSyncMapLayers(); + const { push } = useRouter(); - const [popup, setPopup] = useAtom(popupAtom); + const [popup, setPopup] = useAtom(popupAtom); const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom); const { @@ -63,7 +68,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { } ); - const DATA = useMemo(() => { + const geometryData = useMemo(() => { if (source?.type === 'vector' && rendered && popup && map) { const point = map.project(popup.lngLat); @@ -74,7 +79,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { point.y < 0 || point.y > map.getCanvas().height ) { - return DATA_REF.current; + return geometryDataRef.current; } const query = map.queryRenderedFeatures(point, { layers: layersInteractiveIds, @@ -84,25 +89,28 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { return d.source === source.id; })?.properties; - DATA_REF.current = d; + geometryDataRef.current = d; if (d) { - return DATA_REF.current; + return geometryDataRef.current; } } - return DATA_REF.current; + return geometryDataRef.current; }, [popup, source, layersInteractiveIds, map, rendered]); - // ? I had to type the data ad hoc because the generated type is wrong when we are adding - // ? the `sort` query param + const locationCode = useMemo( + () => geometryData?.[POPUP_PROPERTIES_BY_SOURCE[source?.['id']]?.id], + [geometryData, source] + ); + const { data: protectionCoverageStats, isFetching } = useGetProtectionCoverageStats( { locale, filters: { location: { - code: DATA?.GID_0 || DATA?.region_id, + code: locationCode, }, is_last_year: { $eq: true, @@ -135,7 +143,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { { query: { select: ({ data }) => data?.[0].attributes, - enabled: !!DATA?.region_id || !!DATA?.GID_0, + enabled: !!geometryData, }, } ); @@ -165,15 +173,17 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { return null; } + const { attributes } = protectionCoverageStats.location.data; + if (locale === 'es') { - return protectionCoverageStats.location.data.attributes.name_es; + return attributes.name_es; } if (locale === 'fr') { - return protectionCoverageStats.location.data.attributes.name_fr; + return attributes.name_fr; } - return protectionCoverageStats.location.data.attributes.name; + return attributes.name; }, [locale, protectionCoverageStats]); // handle renderer @@ -182,13 +192,9 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { }, [map]); const handleLocationSelected = useCallback(async () => { - if (!protectionCoverageStats?.location?.data.attributes) return undefined; - - const { code } = protectionCoverageStats.location.data.attributes; - - await push(`${PAGES.progressTracker}/${code.toUpperCase()}?${searchParams.toString()}`); + await push(`${PAGES.progressTracker}/${locationCode.toUpperCase()}?${searchParams.toString()}`); setPopup({}); - }, [push, searchParams, protectionCoverageStats, setPopup]); + }, [push, locationCode, searchParams, setPopup]); useEffect(() => { map?.on('render', handleMapRender); @@ -207,7 +213,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { } }, [layerId, activeLayers, setPopup]); - if (!DATA) return null; + if (!geometryData) return null; return (
From 781cb2e5d0663dcc164dcda0668c49e608271275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 10:30:31 +0200 Subject: [PATCH 7/8] Use different button content based on layer --- frontend/src/containers/map/content/map/popup/constants.ts | 7 +++++++ .../src/containers/map/content/map/popup/regions/index.tsx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/src/containers/map/content/map/popup/constants.ts b/frontend/src/containers/map/content/map/popup/constants.ts index a0ebf1d3..d6e4767c 100644 --- a/frontend/src/containers/map/content/map/popup/constants.ts +++ b/frontend/src/containers/map/content/map/popup/constants.ts @@ -43,3 +43,10 @@ export const POPUP_ICON_BY_SOURCE = { 'gadm-countries': Mountain as IconProps['icon'], 'gadm-regions': Mountain as IconProps['icon'], }; + +export const POPUP_BUTTON_CONTENT_BY_SOURCE = { + 'ezz-source': 'open-country-insights', + 'regions-source': 'open-region-insights', + 'gadm-countries': 'open-country-insights', + 'gadm-regions': 'open-region-insights', +}; diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/regions/index.tsx index e34da10b..d0973abf 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/regions/index.tsx @@ -18,7 +18,7 @@ import { useGetProtectionCoverageStats } from '@/types/generated/protection-cove import { ProtectionCoverageStat } from '@/types/generated/strapi.schemas'; import { LayerTyped } from '@/types/layers'; -import { POPUP_PROPERTIES_BY_SOURCE } from '../constants'; +import { POPUP_BUTTON_CONTENT_BY_SOURCE, POPUP_PROPERTIES_BY_SOURCE } from '../constants'; const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const t = useTranslations('containers.map'); @@ -264,7 +264,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { className="block w-full border border-black p-4 text-center font-mono uppercase" onClick={handleLocationSelected} > - {t('open-region-insights')} + {t(POPUP_BUTTON_CONTENT_BY_SOURCE[source?.['id']])} )} From 945afad2e940ab3edd819b3660085cf5812fdabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Wed, 16 Oct 2024 10:39:29 +0200 Subject: [PATCH 8/8] Unique tooltip for all boundaries layer --- .../src/containers/map/content/map/index.tsx | 6 +- .../map/layers-toolbox/legend/item.tsx | 6 +- .../popup/{regions => boundaries}/index.tsx | 29 +-- .../map/content/map/popup/eez/index.tsx | 234 ------------------ .../containers/map/content/map/popup/item.tsx | 6 +- frontend/src/lib/json-converter/index.ts | 6 +- 6 files changed, 16 insertions(+), 271 deletions(-) rename frontend/src/containers/map/content/map/popup/{regions => boundaries}/index.tsx (93%) delete mode 100644 frontend/src/containers/map/content/map/popup/eez/index.tsx diff --git a/frontend/src/containers/map/content/map/index.tsx b/frontend/src/containers/map/content/map/index.tsx index 9801fdfc..4a8e84f7 100644 --- a/frontend/src/containers/map/content/map/index.tsx +++ b/frontend/src/containers/map/content/map/index.tsx @@ -17,10 +17,9 @@ import LabelsManager from '@/containers/map/content/map/labels-manager'; import LayersToolbox from '@/containers/map/content/map/layers-toolbox'; import Modelling from '@/containers/map/content/map/modelling'; import Popup from '@/containers/map/content/map/popup'; -import EEZLayerPopup from '@/containers/map/content/map/popup/eez'; +import BoundariesPopup from '@/containers/map/content/map/popup/boundaries'; import GenericPopup from '@/containers/map/content/map/popup/generic'; import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area'; -import RegionsPopup from '@/containers/map/content/map/popup/regions'; import { useSyncMapLayers, useSyncMapSettings } from '@/containers/map/content/map/sync-settings'; import { layersAtom, sidebarAtom } from '@/containers/map/store'; import { @@ -349,10 +348,9 @@ MainMap.messages = [ ...LayersToolbox.messages, ...ZoomControls.messages, // Indirectly imported by the layer manager - ...EEZLayerPopup.messages, ...GenericPopup.messages, ...ProtectedAreaPopup.messages, - ...RegionsPopup.messages, + ...BoundariesPopup.messages, ]; export default MainMap; diff --git a/frontend/src/containers/map/content/map/layers-toolbox/legend/item.tsx b/frontend/src/containers/map/content/map/layers-toolbox/legend/item.tsx index 3e014e60..06f82a0d 100644 --- a/frontend/src/containers/map/content/map/layers-toolbox/legend/item.tsx +++ b/frontend/src/containers/map/content/map/layers-toolbox/legend/item.tsx @@ -2,10 +2,9 @@ import { ReactElement, isValidElement, useMemo } from 'react'; import TooltipButton from '@/components/tooltip-button'; import Icon from '@/components/ui/icon'; -import EEZLayerPopup from '@/containers/map/content/map/popup/eez'; +import BoundariesPopup from '@/containers/map/content/map/popup/boundaries'; import GenericPopup from '@/containers/map/content/map/popup/generic'; import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area'; -import RegionsPopup from '@/containers/map/content/map/popup/regions'; import { cn } from '@/lib/classnames'; import { parseConfig } from '@/lib/json-converter'; import CircleWithDottedRedStrokeIcon from '@/styles/icons/circle-with-dotted-red-stroke.svg'; @@ -157,10 +156,9 @@ const LegendItem: FCWithMessages = ({ config }) => { LegendItem.messages = [ // Imported by `parseConfig` - ...EEZLayerPopup.messages, ...GenericPopup.messages, ...ProtectedAreaPopup.messages, - ...RegionsPopup.messages, + ...BoundariesPopup.messages, ]; export default LegendItem; diff --git a/frontend/src/containers/map/content/map/popup/regions/index.tsx b/frontend/src/containers/map/content/map/popup/boundaries/index.tsx similarity index 93% rename from frontend/src/containers/map/content/map/popup/regions/index.tsx rename to frontend/src/containers/map/content/map/popup/boundaries/index.tsx index d0973abf..57139f78 100644 --- a/frontend/src/containers/map/content/map/popup/regions/index.tsx +++ b/frontend/src/containers/map/content/map/popup/boundaries/index.tsx @@ -20,7 +20,7 @@ import { LayerTyped } from '@/types/layers'; import { POPUP_BUTTON_CONTENT_BY_SOURCE, POPUP_PROPERTIES_BY_SOURCE } from '../constants'; -const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { +const BoundariesPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { const t = useTranslations('containers.map'); const locale = useLocale(); @@ -104,6 +104,11 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { [geometryData, source] ); + const localizedLocationName = useMemo( + () => geometryData?.[POPUP_PROPERTIES_BY_SOURCE[source?.['id']]?.name[locale]], + [geometryData, locale, source] + ); + const { data: protectionCoverageStats, isFetching } = useGetProtectionCoverageStats( { @@ -168,24 +173,6 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { }; }, [locale, protectionCoverageStats]); - const localizedLocationName = useMemo(() => { - if (!protectionCoverageStats) { - return null; - } - - const { attributes } = protectionCoverageStats.location.data; - - if (locale === 'es') { - return attributes.name_es; - } - - if (locale === 'fr') { - return attributes.name_fr; - } - - return attributes.name; - }, [locale, protectionCoverageStats]); - // handle renderer const handleMapRender = useCallback(() => { setRendered(map?.loaded() && map?.areTilesLoaded()); @@ -272,6 +259,6 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { ); }; -RegionsPopup.messages = ['containers.map']; +BoundariesPopup.messages = ['containers.map']; -export default RegionsPopup; +export default BoundariesPopup; diff --git a/frontend/src/containers/map/content/map/popup/eez/index.tsx b/frontend/src/containers/map/content/map/popup/eez/index.tsx deleted file mode 100644 index 882b3669..00000000 --- a/frontend/src/containers/map/content/map/popup/eez/index.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; - -import { useMap } from 'react-map-gl'; - -import { useParams } from 'next/navigation'; -import { useRouter } from 'next/router'; - -import type { Feature } from 'geojson'; -import { useAtom, useAtomValue } from 'jotai'; -import { useLocale, useTranslations } from 'next-intl'; - -import { PAGES } from '@/constants/pages'; -import { useMapSearchParams } from '@/containers/map/content/map/sync-settings'; -import { layersInteractiveIdsAtom, popupAtom } from '@/containers/map/store'; -import { formatPercentage, formatKM } from '@/lib/utils/formats'; -import { FCWithMessages } from '@/types'; -import { useGetLayersId } from '@/types/generated/layer'; -import { useGetProtectionCoverageStats } from '@/types/generated/protection-coverage-stat'; -import { ProtectionCoverageStat } from '@/types/generated/strapi.schemas'; -import { LayerTyped } from '@/types/layers'; - -const EEZLayerPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => { - const t = useTranslations('containers.map'); - const locale = useLocale(); - - const [rendered, setRendered] = useState(false); - const DATA_REF = useRef(); - const { default: map } = useMap(); - const searchParams = useMapSearchParams(); - const { push } = useRouter(); - const [popup, setPopup] = useAtom(popupAtom); - const { locationCode } = useParams(); - - const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom); - - const { data: source } = useGetLayersId( - layerId, - { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - fields: ['config'], - }, - { - query: { - select: ({ data }) => (data.attributes as LayerTyped).config?.source, - }, - } - ); - - const DATA = useMemo(() => { - if (source?.type === 'vector' && rendered && popup && map) { - const point = map.project(popup.lngLat); - - // check if the point is outside the canvas - if ( - point.x < 0 || - point.x > map.getCanvas().width || - point.y < 0 || - point.y > map.getCanvas().height - ) { - return DATA_REF.current; - } - const query = map.queryRenderedFeatures(point, { - layers: layersInteractiveIds, - }); - - const d = query.find((d) => { - return d.source === source.id; - })?.properties; - - DATA_REF.current = d; - - if (d) { - return DATA_REF.current; - } - } - - return DATA_REF.current; - }, [popup, source, layersInteractiveIds, map, rendered]); - - const selectedLocationCode: string = Object.keys(DATA || {}) - .filter((key) => key.startsWith('ISO_') && DATA[key]) - .map((key) => DATA[key]) - .find((code, index, codes) => { - if (codes.length > 1) return code === locationCode; - return true; - }); - - const { data: protectionCoverageStats, isFetching } = - useGetProtectionCoverageStats( - { - locale, - filters: { - location: { - code: selectedLocationCode, - }, - is_last_year: { - $eq: true, - }, - environment: { - slug: { - $eq: 'marine', - }, - }, - }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - populate: { - location: { - fields: ['code', 'total_marine_area'], - }, - }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - fields: ['coverage', 'protected_area'], - 'pagination[limit]': 1, - }, - { - query: { - enabled: !!selectedLocationCode, - select: ({ data }) => { - if (!data?.length) return undefined; - return data[0].attributes; - }, - }, - } - ); - - const formattedCoverage = useMemo(() => { - if (protectionCoverageStats?.coverage !== undefined) { - return formatPercentage(locale, protectionCoverageStats.coverage, { - displayPercentageSign: false, - }); - } - - return '-'; - }, [locale, protectionCoverageStats]); - - const EEZName = useMemo(() => { - let name = null; - - if (!DATA) { - return name; - } - - if (locale === 'es') { - name = DATA.GEONAME_ES; - } - - if (locale === 'fr') { - name = DATA.GEONAME_FR; - } - - return name ?? DATA.GEONAME; - }, [locale, DATA]); - - // handle renderer - const handleMapRender = useCallback(() => { - setRendered(map?.loaded() && map?.areTilesLoaded()); - }, [map]); - - const handleLocationSelected = useCallback(async () => { - if (!protectionCoverageStats?.location?.data.attributes) return undefined; - - const { code } = protectionCoverageStats.location.data.attributes; - - await push(`${PAGES.progressTracker}/${code.toUpperCase()}?${searchParams.toString()}`); - setPopup({}); - }, [push, searchParams, protectionCoverageStats, setPopup]); - - useEffect(() => { - map?.on('render', handleMapRender); - - setRendered(map?.loaded() && map?.areTilesLoaded()); - - return () => { - map?.off('render', handleMapRender); - }; - }, [map, handleMapRender]); - - if (!DATA) return null; - - return ( -
-

{EEZName}

- {isFetching &&
{t('loading')}
} - {!isFetching && !protectionCoverageStats && ( -
{t('no-data-available')}
- )} - {!isFetching && !!protectionCoverageStats && ( - <> -
-
{t('marine-conservation-coverage')}
-
- {formattedCoverage !== '-' && - t.rich('percentage-bold', { - percentage: formattedCoverage, - b1: (chunks) => ( - {chunks} - ), - b2: (chunks) => {chunks}, - })} - {formattedCoverage === '-' && ( - {formattedCoverage} - )} -
-
- {t('protected-area', { - protectedArea: formatKM(locale, protectionCoverageStats.protected_area), - totalArea: formatKM( - locale, - Number(protectionCoverageStats.location.data.attributes.total_marine_area) - ), - })} -
-
- {!!selectedLocationCode && ( - - )} - - )} -
- ); -}; - -EEZLayerPopup.messages = ['containers.map']; - -export default EEZLayerPopup; diff --git a/frontend/src/containers/map/content/map/popup/item.tsx b/frontend/src/containers/map/content/map/popup/item.tsx index e9d750a3..95b6d70e 100644 --- a/frontend/src/containers/map/content/map/popup/item.tsx +++ b/frontend/src/containers/map/content/map/popup/item.tsx @@ -2,10 +2,9 @@ import { ReactElement, isValidElement, useMemo } from 'react'; import { useLocale } from 'next-intl'; -import EEZLayerPopup from '@/containers/map/content/map/popup/eez'; +import BoundariesPopup from '@/containers/map/content/map/popup/boundaries'; import GenericPopup from '@/containers/map/content/map/popup/generic'; import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area'; -import RegionsPopup from '@/containers/map/content/map/popup/regions'; import { parseConfig } from '@/lib/json-converter'; import { FCWithMessages } from '@/types'; import { useGetLayersId } from '@/types/generated/layer'; @@ -52,10 +51,9 @@ const PopupItem: FCWithMessages = ({ id }) => { PopupItem.messages = [ // These components are used by `parseConfig` - ...EEZLayerPopup.messages, ...GenericPopup.messages, ...ProtectedAreaPopup.messages, - ...RegionsPopup.messages, + ...BoundariesPopup.messages, ]; export default PopupItem; diff --git a/frontend/src/lib/json-converter/index.ts b/frontend/src/lib/json-converter/index.ts index 61e156c9..a0fce70e 100644 --- a/frontend/src/lib/json-converter/index.ts +++ b/frontend/src/lib/json-converter/index.ts @@ -8,10 +8,9 @@ import { JSONConfiguration, JSONConverter } from '@deck.gl/json/typed'; // LegendTypeChoropleth, // LegendTypeGradient, // } from '@/components/map/legend/item-types'; -import EEZLayerPopup from '@/containers/map/content/map/popup/eez'; +import BoundariesPopup from '@/containers/map/content/map/popup/boundaries'; import GenericPopup from '@/containers/map/content/map/popup/generic'; import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area'; -import RegionsPopup from '@/containers/map/content/map/popup/regions'; import FUNCTIONS from '@/lib/utils'; import { ParamsConfig } from '@/types/layers'; @@ -26,10 +25,9 @@ export const JSON_CONFIGURATION = new JSONConfiguration({ functions: FUNCTIONS, enumerations: {}, reactComponents: { - EEZLayerPopup, GenericPopup, ProtectedAreaPopup, - RegionsPopup, + BoundariesPopup: BoundariesPopup, // LegendTypeBasic, // LegendTypeChoropleth, // LegendTypeGradient,