Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import { StudyContainer } from './study-container';
import { fetchDefaultParametersValues, fetchIdpSettings } from '../services/utils';
import { getOptionalServices } from '../services/study/index';
import {
addFilterForNewSpreadsheet,
addSortForNewSpreadsheet,
initOrUpdateGlobalFilters,
initTableDefinitions,
Expand All @@ -49,8 +48,10 @@ import {
setOptionalServices,
setParamsLoaded,
setUpdateNetworkVisualizationParameters,
updateColumnFiltersAction,
updateTableColumns,
} from '../redux/actions';
import { TableType } from '../types/custom-aggrid-types';
import { getNetworkVisualizationParameters, getSpreadsheetConfigCollection } from '../services/study/study-config';
import {
isComputationResultColumnFilterUpdatedNotification,
Expand Down Expand Up @@ -208,7 +209,7 @@ const App = () => {
const formattedGlobalFilters = model.globalFilters ?? [];
dispatch(renameTableDefinition(tabUuid, model.name));
dispatch(updateTableColumns(tabUuid, formattedColumns));
dispatch(addFilterForNewSpreadsheet(tabUuid, columnsFilters));
dispatch(updateColumnFiltersAction(TableType.Spreadsheet, tabUuid, columnsFilters));
dispatch(initOrUpdateGlobalFilters(tabUuid, formattedGlobalFilters));
dispatch(
addSortForNewSpreadsheet(tabUuid, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React, { FunctionComponent, SyntheticEvent, useCallback, useEffect, useState } from 'react';
import { Autocomplete, TextField } from '@mui/material';
import { useIntl } from 'react-intl';
import { useCustomAggridFilter } from './hooks/use-custom-aggrid-filter';
import { useCustomAggridColumnFilter } from './hooks/use-custom-aggrid-column-filter';
import { isNonEmptyStringOrArray } from '../../../utils/types-utils';
import { CustomAggridFilterParams, FILTER_TEXT_COMPARATORS } from '../../../types/custom-aggrid-types';

Expand All @@ -24,7 +24,7 @@ export const CustomAggridAutocompleteFilter: FunctionComponent<CustomAggridAutoc
options,
}) => {
const intl = useIntl();
const { selectedFilterData, handleChangeFilterValue } = useCustomAggridFilter(api, colId, filterParams);
const { selectedFilterData, handleChangeFilterValue } = useCustomAggridColumnFilter(colId, filterParams);
const [computedFilterOptions, setComputedFilterOptions] = useState<string[]>(options ?? []);

const getUniqueValues = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IconButton, MenuItem, Select } from '@mui/material';
import ClearIcon from '@mui/icons-material/Clear';
import { useIntl } from 'react-intl';
import { SelectChangeEvent } from '@mui/material/Select/SelectInput';
import { useCustomAggridFilter } from './hooks/use-custom-aggrid-filter';
import { useCustomAggridColumnFilter } from './hooks/use-custom-aggrid-column-filter';
import { isNonEmptyStringOrArray } from '../../../utils/types-utils';
import { mergeSx, type MuiStyles } from '@gridsuite/commons-ui';
import { BooleanFilterValue } from './utils/aggrid-filters-utils';
Expand All @@ -28,14 +28,10 @@ const styles = {
},
} as const satisfies MuiStyles;

export const CustomAggridBooleanFilter: FunctionComponent<CustomAggridFilterParams> = ({
api,
colId,
filterParams,
}) => {
export const CustomAggridBooleanFilter: FunctionComponent<CustomAggridFilterParams> = ({ colId, filterParams }) => {
const intl = useIntl();

const { selectedFilterData, handleChangeFilterValue } = useCustomAggridFilter(api, colId, filterParams);
const { selectedFilterData, handleChangeFilterValue } = useCustomAggridColumnFilter(colId, filterParams);

const handleValueChange = (event: SelectChangeEvent) => {
const newValue = event.target.value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useCustomAggridComparatorFilter } from './hooks/use-custom-aggrid-compa

import { CustomAggridFilterParams, FILTER_TEXT_COMPARATORS } from '../../../types/custom-aggrid-types';

export const CustomAggridComparatorFilter = ({ api, colId, filterParams }: CustomAggridFilterParams) => {
export const CustomAggridComparatorFilter = ({ colId, filterParams }: CustomAggridFilterParams) => {
const {
selectedFilterData,
selectedFilterComparator,
Expand All @@ -21,7 +21,7 @@ export const CustomAggridComparatorFilter = ({ api, colId, filterParams }: Custo
handleFilterComparatorChange,
handleFilterTextChange,
handleClearFilter,
} = useCustomAggridComparatorFilter(api, colId, filterParams);
} = useCustomAggridComparatorFilter(colId, filterParams);

const {
comparators = [], // used for text filter as a UI type (examples: contains, startsWith..)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ClearIcon from '@mui/icons-material/Clear';
import { type MuiStyles } from '@gridsuite/commons-ui';
import { CustomAggridComparatorSelector } from './custom-aggrid-comparator-selector';
import { SelectChangeEvent } from '@mui/material/Select/SelectInput';
import { useCustomAggridFilter } from './hooks/use-custom-aggrid-filter';
import { useCustomAggridColumnFilter } from './hooks/use-custom-aggrid-column-filter';

import { CustomAggridFilterParams } from '../../../types/custom-aggrid-types';

Expand All @@ -37,11 +37,11 @@ const styles = {
},
} as const satisfies MuiStyles;

const CustomAggridDurationFilter: FunctionComponent<CustomAggridFilterParams> = ({ api, colId, filterParams }) => {
const CustomAggridDurationFilter: FunctionComponent<CustomAggridFilterParams> = ({ colId, filterParams }) => {
const intl = useIntl();

const { selectedFilterData, selectedFilterComparator, handleChangeFilterValue, handleChangeComparator } =
useCustomAggridFilter(api, colId, filterParams);
useCustomAggridColumnFilter(colId, filterParams);

const {
comparators = [], // used for text filter as a UI type (examples: contains, startsWith..)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, { ComponentType, MouseEvent, useMemo, useState } from 'react';
import { Popover } from '@mui/material';
import { type MuiStyles } from '@gridsuite/commons-ui';
import { CustomFilterIcon } from './custom-filter-icon';
import { useCustomAggridFilter } from './hooks/use-custom-aggrid-filter';
import { useCustomAggridColumnFilter } from './hooks/use-custom-aggrid-column-filter';
import { CustomAggridAutocompleteFilterParams } from './custom-aggrid-autocomplete-filter';
import { CustomAggridFilterParams, FILTER_TEXT_COMPARATORS } from '../../../types/custom-aggrid-types';

Expand Down Expand Up @@ -39,8 +39,7 @@ export const CustomAggridFilter = <F extends CustomAggridFilterParams>({
}: CustomAggridFilterWrapperParams<F>) => {
const [filterAnchorElement, setFilterAnchorElement] = useState<HTMLElement | null>(null);

const { selectedFilterData, selectedFilterComparator } = useCustomAggridFilter(
filterComponentParams.api,
const { selectedFilterData, selectedFilterComparator } = useCustomAggridColumnFilter(
filterComponentParams.colId,
filterComponentParams.filterParams
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { debounce } from '@mui/material';
import { GridApi } from 'ag-grid-community';
import { useFilterSelector } from '../../../../hooks/use-filter-selector';
import { computeTolerance } from '../utils/filter-tolerance-utils';
import {
FILTER_DATA_TYPES,
FILTER_TEXT_COMPARATORS,
FilterConfig,
FilterData,
FilterParams,
TableType,
} from '../../../../types/custom-aggrid-types';
import { updateAgGridFilters } from '../utils/aggrid-filters-utils';
import { useSelector } from 'react-redux';
import { snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui';
import { AppState } from '../../../../redux/reducer.type';
import { getColumnFiltersFromState } from '../../../../redux/selectors/filter-selectors';
import { persistComputationColumnFilters } from '../../../results/common/column-filter/update-computation-columns-filters';
import { persistSpreadsheetColumnFilters } from '../../../spreadsheet-view/columns/persist-spreadsheet-column-filters';
import type { UUID } from 'node:crypto';

const removeElementFromArrayWithFieldValue = (filtersArrayToRemoveFieldValueFrom: FilterConfig[], field: string) => {
return filtersArrayToRemoveFieldValueFrom.filter((f) => f.column !== field);
Expand All @@ -38,24 +42,39 @@ const changeValueFromArrayWithFieldValue = (
}
};

export const useCustomAggridFilter = (
api: GridApi,
const EMPTY_ARRAY: FilterConfig[] = [];

export const useCustomAggridColumnFilter = (
colId: string,
{ type, tab, dataType, comparators = [], debounceMs = 1000, updateFilterCallback }: FilterParams
{ type, tab, dataType, comparators = [], debounceMs = 1000 }: FilterParams
) => {
const [selectedFilterComparator, setSelectedFilterComparator] = useState<string>('');
const [selectedFilterData, setSelectedFilterData] = useState<unknown>();
const [tolerance, setTolerance] = useState<number | undefined>();

// Track if user is currently editing to prevent external sync from overriding input
const isEditingRef = useRef(false);
const editingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const { filters, dispatchFilters } = useFilterSelector(type, tab);
const studyUuid = useSelector((state: any) => state.studyUuid);
const { snackError } = useSnackMessage();
const filters = useSelector<AppState, FilterConfig[]>(
(state) => getColumnFiltersFromState(state, type, tab) ?? EMPTY_ARRAY
);
const studyUuid = useSelector<AppState, AppState['studyUuid']>((state) => state.studyUuid);
const tableDefinitions = useSelector<AppState, AppState['tables']['definitions']>(
(state) => state.tables.definitions
);
const colDef = useMemo(
() => tableDefinitions.find((t) => t.uuid === tab)?.columns?.find((col) => col.id === colId),
[tableDefinitions, tab, colId]
);

// data flow is: update backend database -> notification -> update redux state -> useEffect -> update hook state
const updateFilter = useCallback(
(colId: string, data: FilterData): void => {
(data: FilterData): void => {
if (!studyUuid || type === TableType.Logs) {
return;
}

const newFilter = {
column: colId,
dataType: data.dataType,
Expand All @@ -77,43 +96,35 @@ export const useCustomAggridFilter = (
} else {
updatedFilters = changeValueFromArrayWithFieldValue(filters, colId, newFilter);
}
updateAgGridFilters(api, updatedFilters);
updateFilterCallback?.(api, updatedFilters, colId, studyUuid, type, tab);
dispatchFilters(updatedFilters);

const onError = (error: unknown) => snackWithFallback(snackError, error);

if (type === TableType.Spreadsheet) {
persistSpreadsheetColumnFilters(studyUuid, tab as UUID, updatedFilters, colDef, onError);
} else {
persistComputationColumnFilters(updatedFilters, colId, studyUuid, type, tab, onError);
}
},
[updateFilterCallback, api, dispatchFilters, filters, studyUuid, type, tab]
[filters, studyUuid, type, tab, colId, colDef, snackError]
);

// We intentionally exclude `updateFilter` from dependencies.
// `updateFilter` depends on `filters`, which changes on every filter update.
// Including it would recreate the debounced function on each change,
// canceling any pending debounced call and preventing updates from being sent.
// PS: it only works because most of the props never change...
// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedUpdateFilter = useCallback(
debounce((data) => {
updateFilter(colId, data);
updateFilter(data);
isEditingRef.current = false;
}, debounceMs),
[colId, debounceMs]
[debounceMs]
);

// Cleanup debounce on unmount
useEffect(() => {
return () => {
debouncedUpdateFilter.clear();
if (editingTimeoutRef.current) {
clearTimeout(editingTimeoutRef.current);
}
};
}, [debouncedUpdateFilter]);

const handleChangeFilterValue = useCallback(
(filterData: FilterData) => {
isEditingRef.current = true;
if (editingTimeoutRef.current) clearTimeout(editingTimeoutRef.current);
editingTimeoutRef.current = setTimeout(() => {
isEditingRef.current = false;
}, debounceMs);

setSelectedFilterData(filterData.value);
setTolerance(filterData.tolerance);
Expand All @@ -124,23 +135,24 @@ export const useCustomAggridFilter = (
tolerance: filterData.tolerance,
});
},
[dataType, debouncedUpdateFilter, selectedFilterComparator, debounceMs]
[dataType, debouncedUpdateFilter, selectedFilterComparator]
);

const handleChangeComparator = useCallback(
(newType: string) => {
isEditingRef.current = true;
setSelectedFilterComparator(newType);
const filterWithoutValue =
newType === FILTER_TEXT_COMPARATORS.IS_EMPTY || newType === FILTER_TEXT_COMPARATORS.IS_NOT_EMPTY;
if (filterWithoutValue) {
updateFilter(colId, {
debouncedUpdateFilter({
value: true,
type: newType,
dataType,
tolerance: tolerance,
});
} else if (selectedFilterData && selectedFilterData !== true) {
updateFilter(colId, {
debouncedUpdateFilter({
value: selectedFilterData,
type: newType,
dataType,
Expand All @@ -149,22 +161,15 @@ export const useCustomAggridFilter = (
} else {
// We switch from IS_EMPTY or IS_NOT_EMPTY comparator to a comparator with a value
setSelectedFilterData(undefined);
const updatedFilters = removeElementFromArrayWithFieldValue(filters, colId);
updateFilterCallback?.(api, updatedFilters);
dispatchFilters(updatedFilters);
debouncedUpdateFilter({
value: undefined,
type: newType,
dataType,
tolerance: tolerance,
});
}
},
[
colId,
dataType,
selectedFilterData,
tolerance,
updateFilter,
filters,
updateFilterCallback,
api,
dispatchFilters,
]
[dataType, selectedFilterData, tolerance, debouncedUpdateFilter]
);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ import { ChangeEvent, useMemo } from 'react';
import { useSnackMessage } from '@gridsuite/commons-ui';
import { SelectChangeEvent } from '@mui/material/Select/SelectInput';
import { countDecimalPlacesFromString } from '../../../../utils/rounding';
import { useCustomAggridFilter } from './use-custom-aggrid-filter';
import { GridApi } from 'ag-grid-community';
import { useCustomAggridColumnFilter } from './use-custom-aggrid-column-filter';
import { computeTolerance } from '../utils/filter-tolerance-utils';
import { FILTER_DATA_TYPES, FILTER_TEXT_COMPARATORS, FilterParams } from '../../../../types/custom-aggrid-types';

export const useCustomAggridComparatorFilter = (api: GridApi, colId: string, filterParams: FilterParams) => {
export const useCustomAggridComparatorFilter = (colId: string, filterParams: FilterParams) => {
const { dataType = FILTER_DATA_TYPES.TEXT, comparators = [] } = filterParams;

const isNumberInput = dataType === FILTER_DATA_TYPES.NUMBER;

const { selectedFilterData, selectedFilterComparator, handleChangeFilterValue, handleChangeComparator } =
useCustomAggridFilter(api, colId, filterParams);
useCustomAggridColumnFilter(colId, filterParams);

const { snackWarning } = useSnackMessage();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ import { updateSwitchState } from '../../../../../services/study/network-modific
import { BusMenu } from 'components/menus/bus-menu';
import { startShortCircuitAnalysis } from '../../../../../services/study/short-circuit-analysis';
import { useOneBusShortcircuitAnalysisLoader } from './hooks/use-one-bus-shortcircuit-analysis-loader';
import { setComputationStarting, setComputingStatus, setLogsFilter } from '../../../../../redux/actions';
import { setComputationStarting, setComputingStatus, updateColumnFiltersAction } from '../../../../../redux/actions';
import { TableType } from '../../../../../types/custom-aggrid-types';
import { AppState } from 'redux/reducer.type';
import type { UUID } from 'node:crypto';
import { useParameterState } from 'components/dialogs/parameters/use-parameters-state';
Expand Down Expand Up @@ -243,7 +244,7 @@ const SingleLineDiagramContent = memo(function SingleLineDiagramContent(props: S
.finally(() => {
dispatch(setComputationStarting(false));
// we clear the computation logs filter when a new computation is started
dispatch(setLogsFilter(ComputingType.SHORT_CIRCUIT_ONE_BUS, []));
dispatch(updateColumnFiltersAction(TableType.Logs, ComputingType.SHORT_CIRCUIT_ONE_BUS, []));
});
},
[
Expand Down
Loading