Skip to content

Commit

Permalink
Add map filters and state to app url params
Browse files Browse the repository at this point in the history
  • Loading branch information
barbara-chaves committed Oct 19, 2023
1 parent a7c07e9 commit 2debda7
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useCallback, useMemo, useEffect } from 'react';
import { useCallback, useMemo } from 'react';
import { useRouter } from 'next/router';

import { useAppDispatch, useAppSelector } from 'store/hooks';
import { analysisUI } from 'store/features/analysis/ui';
import { analysisFilters, setFilter } from 'store/features/analysis/filters';
import { setFilter } from 'store/features/analysis/filters';
import { useIndicators } from 'hooks/indicators';
import Select from 'components/forms/select';

Expand All @@ -17,10 +17,9 @@ const ALL = {
};

const IndicatorsFilter = () => {
const { query = {}, replace } = useRouter();
const { query = {} } = useRouter();
const { indicator } = query;
const { visualizationMode } = useAppSelector(analysisUI);
const filters = useAppSelector(analysisFilters);
const dispatch = useAppDispatch();

const {
Expand Down Expand Up @@ -50,25 +49,16 @@ const IndicatorsFilter = () => {
return selected || options?.[0];
}, [indicator, options]);

// Update the filter when the indicator changes
useEffect(() => {
if (current && filters.indicator?.value !== current.value) {
const handleChange: SelectProps['onChange'] = useCallback(
(selected: Option<string>) => {
dispatch(
setFilter({
id: 'indicator',
value: current,
value: selected.value,
}),
);
}
}, [current, dispatch, filters.indicator?.value]);

const handleChange: SelectProps['onChange'] = useCallback(
(selected: Option<string>) => {
replace({ query: { ...query, indicator: selected?.value } }, undefined, {
shallow: true,
});
},
[query, replace],
[dispatch],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@floating-ui/react';
import { Popover, Transition } from '@headlessui/react';
import { useRouter } from 'next/router';
import { pickBy } from 'lodash-es';

import Materials from '../materials/component';
import OriginRegions from '../origin-regions/component';
Expand Down Expand Up @@ -63,7 +64,7 @@ const DEFAULT_QUERY_OPTIONS = {

const MoreFilters = () => {
const { query } = useRouter();
const { scenarioId, compareScenarioId } = query;
const { scenarioId, compareScenarioId, ...restQueries } = query;

const dispatch = useAppDispatch();
const { materials, origins, t1Suppliers, producers, locationTypes } =
Expand Down Expand Up @@ -273,6 +274,43 @@ const MoreFilters = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [scenarioId]);

useEffect(() => {
const options = [
materialOptions,
originOptions,
t1SupplierOptions,
producerOptions,
locationTypeOptions,
];
// Execute only when all options are loaded and there is no filters selected
if (
options.some((option) => !option.length) ||
Object.values(moreFilters).some((value) => value.length)
) {
return;
}

const { materials, origins, t1Suppliers, producers, locationTypes } = restQueries;

const findOptions = (options: { label: string }[], filterQuery: string | string[]) =>
options.filter((o) => {
return (Array.isArray(filterQuery) ? filterQuery : [filterQuery]).includes(o.label);
});

const initialFilters = {
materials: findOptions(materialOptions, materials),
origins: findOptions(originOptions, origins),
t1Suppliers: findOptions(t1SupplierOptions, t1Suppliers),
producers: findOptions(producerOptions, producers),
locationTypes: findOptions(locationTypeOptions, locationTypes),
};

const filtersToSave = pickBy(initialFilters, (value) => value.length);

dispatch(setFilters(filtersToSave));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [locationTypeOptions, materialOptions, originOptions, producerOptions, t1SupplierOptions]);

return (
<Popover className="relative">
{({ open, close }) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { XCircleIcon } from '@heroicons/react/solid';

import { scaleByLegendType } from 'hooks/h3-data/utils';
import LayerManager from 'components/map/layer-manager';
import { useAppSelector } from 'store/hooks';
import { useAppDispatch, useAppSelector } from 'store/hooks';
import { analysisMap } from 'store/features/analysis';
import { DEFAULT_MAP_STATE, setMapState } from 'store/features/analysis/map';
import { analysisUI } from 'store/features/analysis/ui';
import { useImpactLayer } from 'hooks/layers/impact';
import Legend from 'containers/analysis-visualization/analysis-legend';
Expand All @@ -13,7 +14,7 @@ import ZoomControl from 'components/map/controls/zoom';
import PopUp from 'components/map/popup';
import BasemapControl from 'components/map/controls/basemap';
import { NUMBER_FORMAT } from 'utils/number-format';
import Map, { INITIAL_VIEW_STATE } from 'components/map';
import Map from 'components/map';
import { getLayerConfig } from 'components/map/layers/utils';

import type { LayerConstructor } from 'components/map/layers/utils';
Expand All @@ -22,6 +23,7 @@ import type { ViewState } from 'react-map-gl';
import type { MapStyle } from 'components/map/types';
import type { BasemapValue } from 'components/map/controls/basemap/types';
import type { Layer, Legend as LegendType } from 'types';
import { useRouter } from 'next/router';

const getLegendScale = (legendInfo: LegendType) => {
if (legendInfo?.type === 'range' || legendInfo?.type === 'category') {
Expand All @@ -38,12 +40,17 @@ const getLegendScale = (legendInfo: LegendType) => {
};

const AnalysisMap = () => {
const { layers } = useAppSelector(analysisMap);
const { query } = useRouter();
const { layers, mapState } = useAppSelector(analysisMap);
const { isSidebarCollapsed } = useAppSelector(analysisUI);
const dispatch = useAppDispatch();

const [mapStyle, setMapStyle] = useState<MapStyle>('terrain');
const [viewState, setViewState] = useState<Partial<ViewState>>(INITIAL_VIEW_STATE);
const handleViewState = useCallback((viewState: ViewState) => setViewState(viewState), []);

const handleViewState = useCallback((viewState: ViewState) => {
dispatch(setMapState(viewState));
}, []);

const [tooltipData, setTooltipData] = useState(null);

// Pre-Calculating legend scales
Expand Down Expand Up @@ -97,13 +104,27 @@ const AnalysisMap = () => {
.map((layer) => ({ id: layer.id, layer: getLayerConfig(layers[layer.id]), props: layer }));
}, [layers]);

useEffect(() => {
const { longitude, latitude, zoom } = query;
if (longitude && latitude && zoom) {
dispatch(
setMapState({
...DEFAULT_MAP_STATE,
longitude: Number(longitude),
latitude: Number(latitude),
zoom: Number(zoom),
}),
);
}
}, []);

return (
<div className="absolute top-0 left-0 w-full h-full overflow-hidden" data-testid="analysis-map">
{isFetching && <PageLoading />}
<Map
className="w-screen h-full"
mapStyle={mapStyle}
viewState={viewState}
viewState={mapState}
onMapViewStateChange={handleViewState}
// style={{ width}}
sidebarCollapsed={isSidebarCollapsed}
Expand Down
19 changes: 14 additions & 5 deletions client/src/containers/years/component.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/router';

import { useAppDispatch, useAppSelector } from 'store/hooks';
import { analysisFilters, setFilter, setFilters } from 'store/features/analysis/filters';
import { analysisFilters, setFilter } from 'store/features/analysis/filters';
import { useYears } from 'hooks/years';
import Select from 'components/forms/select';

Expand All @@ -11,6 +12,7 @@ const YearsFilter: React.FC = () => {
const dispatch = useAppDispatch();
const filters = useAppSelector(analysisFilters);
const { layer, materials, indicator, startYear } = filters;
const { query = {} } = useRouter();

const materialsIds = useMemo(() => materials.map((mat) => mat.value), [materials]);
const { data: years, isLoading } = useYears(layer, materialsIds, indicator?.value, {
Expand Down Expand Up @@ -42,14 +44,21 @@ const YearsFilter: React.FC = () => {
[dispatch],
);

useEffect(() => {
const initialStartYear = query.startYear;
if (initialStartYear) {
dispatch(setFilter({ id: 'startYear', value: initialStartYear }));
}
}, []);

// Update filters when data changes
useEffect(() => {
if (years?.length && !isLoading) {
dispatch(
setFilters({ ...(startYear ? {} : { startYear: years[years.length - 1] }), endYear: null }),
);
if (years.includes(Number(startYear))) return;
const lastYearValue = years[years.length - 1];
dispatch(setFilter({ id: 'startYear', value: lastYearValue }));
}
}, [dispatch, isLoading, years, startYear]);
}, [dispatch, isLoading, startYear, years]);

return (
<Select<number>
Expand Down
26 changes: 24 additions & 2 deletions client/src/store/features/analysis/map.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { createSlice } from '@reduxjs/toolkit';

import type { ViewState } from 'react-map-gl';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from 'store';
import type { Layer } from 'types';

export const DEFAULT_MAP_STATE: Partial<ViewState> = {
longitude: 0,
latitude: 0,
zoom: 2,
pitch: 0,
bearing: 0,
padding: null,
};

const DEFAULT_LAYER_ATTRIBUTES = {
order: 0,
visible: false,
Expand Down Expand Up @@ -54,6 +64,7 @@ export type AnalysisMapState = {
};
// Deck.gl layer props by layer id
layerDeckGLProps: Record<Layer['id'], Partial<DeckGLConstructorProps>>;
mapState: Partial<ViewState>;
};

// Define the initial state using that type
Expand All @@ -79,12 +90,17 @@ export const initialState: AnalysisMapState = {
y: 0,
},
layerDeckGLProps: {},
mapState: DEFAULT_MAP_STATE,
};

export const analysisMapSlice = createSlice({
name: 'analysisMap',
initialState,
reducers: {
setMapState: (state, action: PayloadAction<AnalysisMapState['mapState']>) => ({
...state,
mapState: action.payload,
}),
setLayer: (
state,
action: PayloadAction<{
Expand Down Expand Up @@ -180,8 +196,14 @@ export const analysisMapSlice = createSlice({
},
});

export const { setLayer, setLayerDeckGLProps, setTooltipData, setTooltipPosition, setLayerOrder } =
analysisMapSlice.actions;
export const {
setLayer,
setLayerDeckGLProps,
setTooltipData,
setTooltipPosition,
setLayerOrder,
setMapState,
} = analysisMapSlice.actions;

export const analysisMap = (state: RootState): AnalysisMapState => state['analysis/map'];

Expand Down
Loading

0 comments on commit 2debda7

Please sign in to comment.