diff --git a/client/package.json b/client/package.json index 2d8cdffcf..54e340648 100644 --- a/client/package.json +++ b/client/package.json @@ -100,6 +100,7 @@ "@types/chroma-js": "2.1.3", "@types/d3-format": "3.0.1", "@types/d3-scale": "4.0.2", + "@types/geojson": "7946.0.14", "@types/lodash-es": "4.17.6", "@types/node": "16.11.6", "@types/react": "18.2.28", diff --git a/client/src/containers/analysis-eudr/category-list/index.tsx b/client/src/containers/analysis-eudr/category-list/index.tsx index fabb6e405..0ba6ce815 100644 --- a/client/src/containers/analysis-eudr/category-list/index.tsx +++ b/client/src/containers/analysis-eudr/category-list/index.tsx @@ -6,68 +6,60 @@ import SuppliersWithNoLocationDataBreakdown from './breakdown/suppliers-with-no- import { Button } from '@/components/button'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; -import { cn } from '@/lib/utils'; import { useEUDRData } from '@/hooks/eudr'; import { useAppDispatch, useAppSelector } from '@/store/hooks'; import { eudr, setTableFilters } from '@/store/features/eudr'; import { themeColors } from '@/utils/colors'; -import type { EUDRState } from '@/store/features/eudr'; - export const CATEGORIES = [ { name: 'Deforestation-free suppliers', + key: 'dfs', color: themeColors.blue[400], }, { name: 'Suppliers with deforestation alerts', + key: 'sda', color: '#FFC038', }, { name: 'Suppliers with no location data', + key: 'tpl', color: '#8561FF', }, ] as const; -const CATEGORY_TO_FILTER: Record< - (typeof CATEGORIES)[number]['name'], - Partial -> = { - [CATEGORIES[0].name]: 'dfs', - [CATEGORIES[1].name]: 'sda', - [CATEGORIES[2].name]: 'tpl', -} as const; - -type CategoryState = Record<(typeof CATEGORIES)[number]['name'], boolean>; +type CategoryState = Record<(typeof CATEGORIES)[number]['key'], boolean>; export const CategoryList = (): JSX.Element => { + const { + viewBy, + filters: { dates, suppliers, origins, materials, plots }, + table: { filters: tableFilters }, + } = useAppSelector(eudr); + const [categories, toggleCategory] = useState( CATEGORIES.reduce( (acc, category) => ({ ...acc, - [category.name]: false, + [category.key]: tableFilters[category.key], }), {} as CategoryState, ), ); - const { - viewBy, - filters: { dates, suppliers, origins, materials, plots }, - table: { filters: tableFilters }, - } = useAppSelector(eudr); const dispatch = useAppDispatch(); const onClickCategory = useCallback( (category: (typeof CATEGORIES)[number]) => { toggleCategory((prev) => ({ ...prev, - [category.name]: !prev[category.name], + [category.key]: !prev[category.key], })); dispatch( setTableFilters({ - [CATEGORY_TO_FILTER[category.name]]: !tableFilters[CATEGORY_TO_FILTER[category.name]], + [category.key]: !tableFilters[category.key], }), ); }, @@ -91,19 +83,23 @@ export const CategoryList = (): JSX.Element => { const parsedData = useMemo(() => { const dataByView = data?.[viewBy] || []; - return Object.keys(dataByView).map((key) => ({ - name: key, - ...dataByView[key], - color: CATEGORIES.find((category) => category.name === key)?.color || '#000', - })); + return Object.keys(dataByView).map((key) => { + const category = CATEGORIES.find((category) => category.name === key); + return { + name: key, + ...dataByView[key], + key: category?.key, + color: category?.color || '#000', + }; + }); }, [data, viewBy]); return ( <> {parsedData.map((category) => ( onClickCategory(category)} > @@ -140,21 +136,16 @@ export const CategoryList = (): JSX.Element => { type="button" size="xs" variant="white" - className={cn( - 'w-[98px] rounded-md border-none text-sm text-gray-500 shadow-none transition-colors hover:shadow-none', - { - 'bg-navy-400 text-white hover:bg-navy-600': categories[category.name], - }, - )} + className="w-[98px] rounded-md border-none text-sm text-gray-500 shadow-none transition-colors hover:shadow-none group-data-[state=open]:bg-navy-400 group-data-[state=open]:text-white group-data-[state=open]:hover:bg-navy-600" > - {categories[category.name] ? 'Close detail' : 'View detail'} + {categories[category.key] ? 'Close detail' : 'View detail'} - {category.name === CATEGORIES[0].name && } - {category.name === CATEGORIES[1].name && } - {category.name === CATEGORIES[2].name && } + {categories['dfs'] && } + {categories['sda'] && } + {categories['tpl'] && } ))} diff --git a/client/src/containers/analysis-eudr/map/component.tsx b/client/src/containers/analysis-eudr/map/component.tsx index 68543fd11..53e94f4a9 100644 --- a/client/src/containers/analysis-eudr/map/component.tsx +++ b/client/src/containers/analysis-eudr/map/component.tsx @@ -43,6 +43,7 @@ const EUDRMap = () => { supplierLayer, contextualLayers, filters: { suppliers, materials, origins, plots, dates }, + table: { filters: tableFilters }, } = useAppSelector((state) => state.eudr); const [hoverInfo, setHoverInfo] = useState(null); @@ -74,9 +75,17 @@ const EUDRMap = () => { }; } + const filteredData = data?.table.filter((dataRow) => { + if (Object.values(tableFilters).every((filter) => !filter)) return true; + + if (tableFilters.dfs && dataRow.dfs > 0) return true; + if (tableFilters.sda && dataRow.sda > 0) return true; + if (tableFilters.tpl && dataRow.tpl > 0) return true; + }); + return { - dfs: data.table.map((row) => row.plots.dfs.flat()).flat(), - sda: data.table.map((row) => row.plots.sda.flat()).flat(), + dfs: filteredData.map((row) => row.plots.dfs.flat()).flat(), + sda: filteredData.map((row) => row.plots.sda.flat()).flat(), }; }, }, @@ -91,12 +100,30 @@ const EUDRMap = () => { geoRegionIds: plots?.map(({ value }) => value), }); - const eudrSupplierLayer = useMemo(() => { + const filteredGeometries: typeof plotGeometries.data = useMemo(() => { if (!plotGeometries.data || !data) return null; - return new GeoJsonLayer({ + if (params?.supplierId) return plotGeometries.data; + + return { + type: 'FeatureCollection', + features: plotGeometries.data.features.filter((feature) => { + if (Object.values(tableFilters).every((filter) => !filter)) return true; + + if (tableFilters.dfs && data.dfs.indexOf(feature.properties.id) > -1) return true; + if (tableFilters.sda && data.sda.indexOf(feature.properties.id) > -1) return true; + return false; + }), + }; + }, [data, plotGeometries.data, tableFilters, params]); + + const eudrSupplierLayer = useMemo(() => { + if (!filteredGeometries?.features || !data) return null; + + return new GeoJsonLayer<(typeof filteredGeometries)['features'][number]>({ id: 'full-plots-layer', - data: plotGeometries.data, + // @ts-expect-error will fix this later... + data: filteredGeometries, // Styles filled: true, getFillColor: ({ properties }) => { @@ -132,7 +159,7 @@ const EUDRMap = () => { onHover: setHoverInfo, opacity: supplierLayer.opacity, }); - }, [plotGeometries.data, data, supplierLayer.active, supplierLayer.opacity]); + }, [filteredGeometries, data, supplierLayer.active, supplierLayer.opacity]); const basemapPlanetLayer = new TileLayer({ id: 'top-planet-monthly-layer', diff --git a/client/src/hooks/eudr/index.ts b/client/src/hooks/eudr/index.ts index 99b0ec8ab..5b5c11746 100644 --- a/client/src/hooks/eudr/index.ts +++ b/client/src/hooks/eudr/index.ts @@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import { apiService } from 'services/api'; +import type { FeatureCollection, Geometry } from 'geojson'; import type { Supplier as SupplierRow } from '@/containers/analysis-eudr/supplier-list-table/table'; import type { MaterialTreeItem, OriginRegion, Supplier } from '@/types'; import type { UseQueryOptions } from '@tanstack/react-query'; @@ -33,15 +34,15 @@ export const useEUDRSuppliers = ( ); }; -export const usePlotGeometries = ( +export const usePlotGeometries = ( params?: EUDRParams, - options: UseQueryOptions = {}, + options?: UseQueryOptions>, ) => { return useQuery( ['eudr-geo-features-collection', params], () => apiService - .request<{ geojson }>({ + .request<{ geojson: FeatureCollection }>({ method: 'GET', url: '/eudr/geo-features/collection', params, diff --git a/client/yarn.lock b/client/yarn.lock index ed158db3e..5c54aaf6c 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2791,7 +2791,7 @@ __metadata: languageName: node linkType: hard -"@types/geojson@npm:^7946.0.13": +"@types/geojson@npm:7946.0.14, @types/geojson@npm:^7946.0.13": version: 7946.0.14 resolution: "@types/geojson@npm:7946.0.14" checksum: ae511bee6488ae3bd5a3a3347aedb0371e997b14225b8983679284e22fa4ebd88627c6e3ff8b08bf4cc35068cb29310c89427311ffc9322c255615821a922e71 @@ -7892,6 +7892,7 @@ __metadata: "@types/chroma-js": 2.1.3 "@types/d3-format": 3.0.1 "@types/d3-scale": 4.0.2 + "@types/geojson": 7946.0.14 "@types/lodash-es": 4.17.6 "@types/node": 16.11.6 "@types/react": 18.2.28