client/src/containers/analysis-visualization/analysis-legend/material-legend-item/component.tsx
import { useCallback, useMemo } from 'react';
import { useAppSelector, useAppDispatch, useSyncIndicators } from 'store/hooks';
import { analysisMap, setLayer } from 'store/features/analysis/map';
import { analysisFilters } from 'store/features/analysis';
import LegendTypeChoropleth from 'components/legend/types/choropleth';
import LegendItem from 'components/legend/item';
import { formatNumber } from 'utils/number-format';
import { COLOR_RAMPS } from 'utils/colors';
import Materials from 'containers/analysis-visualization/analysis-filters/materials/component';
import { useMaterial } from 'hooks/materials';
import { setFilter } from 'store/features/analysis/filters';
import useH3MaterialData from 'hooks/h3-data/material';
import type { TreeSelectOption } from 'components/tree-select/types';
import type { Legend, LegendItem as LegendItemsProps } from 'types';
const LAYER_ID = 'material';
const MaterialLayer = () => {
const dispatch = useAppDispatch();
const { materialId } = useAppSelector(analysisFilters);
const [syncedIndicators] = useSyncIndicators();
const {
layers: { [LAYER_ID]: layer },
} = useAppSelector(analysisMap);
const handleOpacity = useCallback(
(opacity: number) => {
dispatch(setLayer({ id: LAYER_ID, layer: { opacity } }));
},
[dispatch],
);
const { isFetching, isSuccess, data, isError, error } = useH3MaterialData(undefined, {
onSuccess: (data) => {
dispatch(
setLayer({
id: LAYER_ID,
layer: {
metadata: {
legend: {
id: `${LAYER_ID}-${syncedIndicators?.[0]}`,
type: 'basic',
name: `${material.metadata.name}`,
unit: data.metadata.unit,
min: !!data.metadata.quantiles.length && formatNumber(data.metadata.quantiles[0]),
items: data.metadata.quantiles.slice(1).map(
(v, index): LegendItemsProps => ({
value: formatNumber(v),
color: COLOR_RAMPS[LAYER_ID][index],
}),
),
},
},
},
}),
);
},
});
const { data: material } = useMaterial(materialId);
const legendItems = useMemo<Legend['items']>(
() =>
layer.metadata?.legend?.items?.map((item) => ({
...item,
label: item.label || `${item.value}`,
})) || [],
[layer.metadata?.legend.items],
);
const handleMaterialChange = useCallback(
(material: TreeSelectOption) => {
dispatch(setFilter({ id: 'materialId', value: material?.value }));
},
[dispatch],
);
const Selector = useMemo(() => {
return (
<div>
<div>Material Production</div>
<Materials
current={material ? { label: material.name, value: material.id } : null}
onChange={handleMaterialChange}
/>
</div>
);
}, [handleMaterialChange, material]);
const onToggleLayer = useCallback(
(active: boolean) => {
dispatch(setLayer({ id: layer.id, layer: { active } }));
},
[dispatch, layer.id],
);
const handleHideLayer = useCallback(() => {
dispatch(setLayer({ id: layer.id, layer: { visible: false, active: false } }));
dispatch(setFilter({ id: 'materialId', value: null }));
}, [dispatch, layer.id]);
return (
<LegendItem
isActive={layer.active}
onToggle={onToggleLayer}
name={Selector}
info={{
title: material?.metadata?.name,
description: material?.metadata?.overview,
source: material?.metadata?.source,
}}
{...data?.metadata?.legend}
unit={data?.metadata?.unit}
showToolbar
opacity={layer.opacity}
onChangeOpacity={handleOpacity}
isLoading={isFetching}
onHideLayer={handleHideLayer}
main
>
{isSuccess && (
<LegendTypeChoropleth
className="text-sm text-gray-500"
min={data.metadata.legend?.min}
items={legendItems}
/>
)}
{isError && error.response?.status === 404 && (
<div className="text-sm text-red-500">No data found for this parameters</div>
)}
</LegendItem>
);
};
export default MaterialLayer;