Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
andresgnlez committed May 10, 2024
1 parent 6154b38 commit 8b3de56
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 149 deletions.
2 changes: 1 addition & 1 deletion client/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
],
plugins: ['prettier'],
rules: {
'@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/no-unused-vars': ['warn', { ignoreRestSiblings: true }],
'import/order': [
'warn',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ const AnalysisDynamicMetadata: FC<AnalysisDynamicMetadataTypes> = ({
<InformationCircleIcon className="h-4 w-4 shrink-0 text-gray-900" />
Viewing {isComparisonEnabled ? <ComparisonToggle /> : values}
<span>
<span className="whitespace-nowrap">Impact values for</span>
<span className="whitespace-nowrap">impact values for</span>
<span className="whitespace-nowrap font-bold"> {scenario1} </span>
</span>
{comparisonTemplate}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import IndicatorsFilter from './indicators';
import IndicatorsMapFilter from './indicators/map';
import GroupByFilter from './group-by';
import YearsRangeFilter from './years-range';
import MoreFilters from './more-filters';
Expand All @@ -12,10 +13,19 @@ const AnalysisFilters: React.FC = () => {

return (
<div className="inline-flex flex-wrap gap-2">
<IndicatorsFilter />
{visualizationMode !== 'map' && <GroupByFilter />}
{visualizationMode === 'map' && <YearsFilter />}
{visualizationMode !== 'map' && <YearsRangeFilter />}
{visualizationMode === 'map' && (
<>
<IndicatorsMapFilter />
<YearsFilter />
</>
)}
{visualizationMode !== 'map' && (
<>
<IndicatorsFilter />
<GroupByFilter />
<YearsRangeFilter />
</>
)}
<MoreFilters />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,151 +1,133 @@
import { useCallback, useMemo, useEffect, ComponentProps, Fragment, useState } from 'react';
import { useCallback, useMemo, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import queryString from 'query-string';
import { useSearchParams, usePathname } from 'next/navigation';
import { xor } from 'lodash-es';

import { useAppDispatch, useAppSelector } from 'store/hooks';
import { analysisUI } from 'store/features/analysis/ui';
import { setFilter } from 'store/features/analysis/filters';
import { useIndicators } from 'hooks/indicators';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
SelectLabel,
SelectSeparator,
} from '@/components/ui/select';
import TreeSelect from '@/components/tree-select';
import { TreeSelectOption } from '@/components/tree-select/types';
import { flattenTree } from '@/components/tree-select/utils';

type HasParentProperty<T> = T & { isParent?: boolean };

const IndicatorsFilter = () => {
const { query = {}, replace } = useRouter();
const { indicator } = query;
const { visualizationMode } = useAppSelector(analysisUI);
const dispatch = useAppDispatch();
const [value, setValue] = useState<string | undefined>(undefined);
const searchParams = useSearchParams();
const isTableView = usePathname().includes('/table');
const [selectedOptions, setSelectedOptions] = useState<TreeSelectOption[] | undefined>(undefined);

const { data, isFetching, isFetched } = useIndicators(
{ sort: 'name' },
const { data, isFetching } = useIndicators(
{ sort: 'category' },
{
select: (data) => data?.data,
},
);

const options = useMemo(() => {
const categories = Array.from(
new Set(data?.map(({ category }) => category).filter(Boolean)),
).sort((a, b) => a.localeCompare(b));
const options = useMemo<TreeSelectOption[]>(() => {
const categories = Array.from(new Set(data?.map(({ category }) => category).filter(Boolean)));

const categoryGroups = categories.map((category) => {
const indicators = data?.filter((indicator) => indicator.category === category);
const categoryOptions = indicators.map((indicator) => ({
label: indicator.name,
value: indicator.id,
disabled: indicator.status === 'inactive',
}));
return { label: category, value: category, options: categoryOptions };
const categoryOptions = indicators.map(
(indicator) =>
({
label: indicator.name,
value: indicator.id,
// disabled: indicator.status === 'inactive',
}) satisfies TreeSelectOption<(typeof indicator)['id']>,
);
return {
label: category,
value: category,
isParent: true,
children: categoryOptions,
} satisfies HasParentProperty<TreeSelectOption<typeof category>>;
});

return [
...(visualizationMode !== 'map'
? [
{
label: 'All indicators',
value: 'all',
options: [],
},
]
: []),
...categoryGroups,
];
}, [data, visualizationMode]);
return categoryGroups;
}, [data]);

const indicatorName = useMemo(() => {
const indicator = data?.find((indicator) => indicator.id === value);
return indicator?.name;
}, [data, value]);
const allNodes = useMemo(() => options?.flatMap((opt) => flattenTree(opt)), [options]);

const handleChange = useCallback(
(value: Parameters<ComponentProps<typeof Select>['onValueChange']>[0]) => {
replace({ query: { ...query, indicator: value } }, undefined, {
shallow: true,
});

dispatch(
setFilter({
id: 'indicator',
value: {
label: indicatorName,
value,
},
}),
(options: HasParentProperty<TreeSelectOption>[]) => {
const _v = options
.map(({ value, isParent }) => {
if (isParent) {
return allNodes
.filter(({ value: v }) => v === value)
.map(({ children }) => children.map(({ value }) => value))
.flat(1);
}
return value;
})
.flat(1);

const { indicators, detail, ...restQuery } = query;

setSelectedOptions(options);

const optionDetailRemoved = xor(selectedOptions, options).some(
({ value }) => value === detail,
);

replace(
{
query: queryString.stringify(
{
...restQuery,
indicators: _v,
...(isTableView && optionDetailRemoved && { detail: undefined }),
},
{
arrayFormat: 'comma',
arrayFormatSeparator: ',',
},
),
},
undefined,
{
shallow: true,
},
);
},
[query, replace, indicatorName, dispatch],
[query, replace, allNodes, selectedOptions, isTableView],
);

useEffect(() => {
if (indicator) {
return setValue(indicator as string);
}

if (visualizationMode === 'map') {
if (options?.at(0)?.options?.length) {
return setValue(options.at(0).options.at(0).value);
}
}
const parsedQueryParams = useMemo(() => {
return queryString.parse(searchParams.toString(), {
arrayFormat: 'comma',
arrayFormatSeparator: ',',
});
}, [searchParams]);

if (options?.length && !options?.at(0)?.options?.length) {
return setValue(options.at(0).value);
const initialSelectedOptions = useMemo(() => {
if (parsedQueryParams.indicators && options?.length) {
const selectedOptions = allNodes.filter(({ value }) =>
parsedQueryParams.indicators.includes(value),
);
return selectedOptions;
}
}, [visualizationMode, options, indicator, indicatorName, dispatch]);
return undefined;
}, [parsedQueryParams, options, allNodes]);

useEffect(() => {
if (indicator && indicatorName) {
dispatch(
setFilter({
id: 'indicator',
value: {
label: indicatorName,
value: indicator,
},
}),
);
}
}, [dispatch, indicator, indicatorName]);
setSelectedOptions(initialSelectedOptions);
}, [initialSelectedOptions]);

return (
<Select value={value} onValueChange={handleChange} disabled={!isFetched && isFetching}>
<SelectTrigger
className="h-full w-[325px] overflow-ellipsis text-left"
data-testid="indicators-filter"
>
<SelectValue placeholder="Select an indicator" />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<Fragment key={option.value}>
{!option?.options?.length && (
<>
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
<SelectSeparator />
</>
)}
{option?.options?.length > 0 && (
<SelectGroup>
<SelectLabel>{option.label}</SelectLabel>
{option.options.map((indicator) => (
<SelectItem key={indicator.value} value={indicator.value}>
{indicator.label}
</SelectItem>
))}
</SelectGroup>
)}
</Fragment>
))}
</SelectContent>
</Select>
<div className="h-full w-[235px]">
<TreeSelect
multiple
showSearch
loading={isFetching}
options={options}
onChange={handleChange}
current={selectedOptions}
placeholder="All indicators"
/>
</div>
);
};

Expand Down
Loading

0 comments on commit 8b3de56

Please sign in to comment.