Skip to content

implement and refacto use global filters #2975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
99 changes: 99 additions & 0 deletions src/components/results/common/global-filter/use-global-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* 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, useState } from 'react';
import { GlobalFilter, GlobalFilters } from './global-filter-types';
import { FilterType } from '../utils';

interface UseGlobalFiltersProps {
onFilterChange?: (filters: GlobalFilters) => void;
}

export default function useGlobalFilters({ onFilterChange }: Readonly<UseGlobalFiltersProps>) {
const [globalFilters, setGlobalFilters] = useState<GlobalFilters>();

const handleGlobalFilterChange = useCallback(
(value: GlobalFilter[]) => {
let newGlobalFilter: GlobalFilters = {};

const nominalVs = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
.map((filter: GlobalFilter) => filter.label)
);

const genericFilters: Set<string> = new Set(
value
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
.map((filter: GlobalFilter) => filter.uuid ?? '')
.filter((uuid: string): boolean => uuid !== '')
);

const countryCodes = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
.map((filter: GlobalFilter) => filter.label)
);

const substationProperties: Map<string, string[]> = new Map();
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
.forEach((filter: GlobalFilter) => {
if (filter.filterSubtype) {
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
if (subtypeSubstationProperties) {
subtypeSubstationProperties.push(filter.label);
} else {
substationProperties.set(filter.filterSubtype, [filter.label]);
}
}
});

newGlobalFilter.nominalV = [...nominalVs];
newGlobalFilter.countryCode = [...countryCodes];
newGlobalFilter.genericFilter = [...genericFilters];

if (substationProperties.size > 0) {
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
}

setGlobalFilters(newGlobalFilter);
onFilterChange?.(newGlobalFilter);
},
[onFilterChange]
);

const getGlobalFilterParameter = useCallback((globalFilters: GlobalFilters | undefined) => {
let shouldSentParameter = false;

if (globalFilters) {
if (
(globalFilters.countryCode && globalFilters.countryCode.length > 0) ||
(globalFilters.nominalV && globalFilters.nominalV.length > 0) ||
(globalFilters.genericFilter && globalFilters.genericFilter.length > 0) ||
globalFilters.substationProperty
) {
shouldSentParameter = true;
}
}

if (!shouldSentParameter) {
return undefined;
}

return {
...globalFilters,
};
}, []);

return {
globalFilters,
setGlobalFilters,
handleGlobalFilterChange,
getGlobalFilterParameter,
};
}
86 changes: 10 additions & 76 deletions src/components/results/loadflow/load-flow-result-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ import { FilterType as AgGridFilterType } from '../../../types/custom-aggrid-typ
import { useFilterSelector } from '../../../hooks/use-filter-selector';
import { mapFieldsToColumnsFilter } from '../../../utils/aggrid-headers-utils';
import { loadflowResultInvalidations } from '../../computing-status/use-all-computing-status';
import { FilterType } from '../common/utils';
import { useNodeData } from 'components/use-node-data';
import {
FILTER_DATA_TYPES,
FILTER_TEXT_COMPARATORS,
} from '../../custom-aggrid/custom-aggrid-filters/custom-aggrid-filter.type';
import { GlobalFilter, GlobalFilters } from '../common/global-filter/global-filter-types';
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import { UUID } from 'crypto';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import { useGlobalFilterData } from '../common/global-filter/use-global-filter-data';
import useGlobalFilters from '../common/global-filter/use-global-filters';

const styles = {
flexWrapper: {
Expand Down Expand Up @@ -84,35 +83,10 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
const { filters } = useFilterSelector(AgGridFilterType.Loadflow, mappingTabs(tabIndex));

const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterData();
const [globalFilter, setGlobalFilter] = useState<GlobalFilters>();
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters({});

const { loading: filterEnumsLoading, result: filterEnums } = useFetchFiltersEnums();

const getGlobalFilterParameter = useCallback(
(globalFilter: GlobalFilters | undefined) => {
let shouldSentParameter = false;
if (globalFilter) {
if (
(globalFilter.countryCode && globalFilter.countryCode.length > 0) ||
(globalFilter.nominalV && globalFilter.nominalV.length > 0) ||
(globalFilter.genericFilter && globalFilter.genericFilter.length > 0) ||
globalFilter.substationProperty
) {
shouldSentParameter = true;
}
}
if (!shouldSentParameter) {
return undefined;
}
return {
...globalFilter,
limitViolationsTypes:
tabIndex === 0 ? [LimitTypes.CURRENT] : [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
};
},
[tabIndex]
);

const getEnumLabel = useCallback(
(value: string) =>
intl.formatMessage({
Expand Down Expand Up @@ -145,10 +119,14 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
colId: FROM_COLUMN_TO_FIELD_LIMIT_VIOLATION_RESULT[sort.colId],
})),
filters: mapFieldsToColumnsFilter(updatedFilters, mappingFields(tabIndex)),
globalFilters: getGlobalFilterParameter(globalFilter),
globalFilters: {
...globalFilters,
limitViolationsTypes:
tabIndex === 0 ? [LimitTypes.CURRENT] : [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
},
});
},
[tabIndex, filters, intl, sortConfig, getGlobalFilterParameter, globalFilter]
[tabIndex, filters, intl, sortConfig, globalFilters]
);

const fetchloadflowResultWithParameters = useMemo(() => {
Expand Down Expand Up @@ -209,50 +187,6 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
setTabIndex(newTabIndex);
};

const handleGlobalFilterChange = useCallback((value: GlobalFilter[]) => {
let newGlobalFilter: GlobalFilters = {};
if (value) {
const nominalVs = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
.map((filter: GlobalFilter) => filter.label)
);
const genericFilters: Set<string> = new Set(
value
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
.map((filter: GlobalFilter) => filter.uuid ?? '')
.filter((uuid: string): boolean => uuid !== '')
);
const countryCodes = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
.map((filter: GlobalFilter) => filter.label)
);
// extract the substation properties and sort them by property name (ie filterSubtype)
const substationProperties: Map<string, string[]> = new Map();
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
.forEach((filter: GlobalFilter) => {
if (filter.filterSubtype) {
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
if (subtypeSubstationProperties) {
subtypeSubstationProperties.push(filter.label);
} else {
substationProperties.set(filter.filterSubtype, [filter.label]);
}
}
});

newGlobalFilter.nominalV = [...nominalVs];
newGlobalFilter.countryCode = [...countryCodes];
newGlobalFilter.genericFilter = [...genericFilters];
if (substationProperties.size > 0) {
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
}
}
setGlobalFilter(newGlobalFilter);
}, []);

const result = useMemo(() => {
if (!loadflowResult) {
return [];
Expand All @@ -273,7 +207,7 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
return [];
}, [tabIndex]);

const globalFilters = useMemo(
const globalFiltersComponent = useMemo(
() => (
<GlobalFilterSelector
onChange={handleGlobalFilterChange}
Expand All @@ -294,7 +228,7 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
<Tab label={<FormattedMessage id={'ComputationResultsLogs'} />} />
</Tabs>
<Box sx={mergeSx(styles.flexElement, tabIndex === 0 || tabIndex === 1 ? styles.show : styles.hide)}>
{globalFilters}
{globalFiltersComponent}
</Box>
<Box sx={styles.emptySpace}></Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { UUID } from 'crypto';
import { SensiKind, SENSITIVITY_AT_NODE } from './sensitivity-analysis-result.type';
import { AppState } from '../../../redux/reducer';
import { SensitivityResult, SensitivityResultFilterOptions } from '../../../services/study/sensitivity-analysis.type';
import { GlobalFilters } from '../common/global-filter/global-filter-types';

export type PagedSensitivityAnalysisResultProps = {
studyUuid: UUID;
Expand All @@ -44,6 +45,7 @@ export type PagedSensitivityAnalysisResultProps = {
setPage: (newPage: number) => void;
setCsvHeaders: (newHeaders: string[]) => void;
setIsCsvButtonDisabled: (newIsCsv: boolean) => void;
globalFilters?: GlobalFilters;
};

function PagedSensitivityAnalysisResult({
Expand All @@ -56,6 +58,7 @@ function PagedSensitivityAnalysisResult({
setPage,
setCsvHeaders,
setIsCsvButtonDisabled,
globalFilters,
}: Readonly<PagedSensitivityAnalysisResultProps>) {
const intl = useIntl();

Expand Down Expand Up @@ -138,7 +141,7 @@ function PagedSensitivityAnalysisResult({
}),
});
});
}, [nOrNkIndex, sensiKind, studyUuid, currentRootNetworkUuid, nodeUuid, snackError, intl]);
}, [nOrNkIndex, sensiKind, studyUuid, nodeUuid, currentRootNetworkUuid, snackError, intl]);

const fetchResult = useCallback(() => {
const sortSelector = sortConfig?.length
Expand Down Expand Up @@ -166,7 +169,14 @@ function PagedSensitivityAnalysisResult({
return { ...elem, column: newColumn };
});
setIsLoading(true);
fetchSensitivityAnalysisResult(studyUuid, nodeUuid, currentRootNetworkUuid, selector, mappedFilters)
fetchSensitivityAnalysisResult(
studyUuid,
nodeUuid,
currentRootNetworkUuid,
selector,
mappedFilters,
globalFilters
)
.then((res) => {
const { filteredSensitivitiesCount = 0 } = res || {};

Expand All @@ -185,12 +195,13 @@ function PagedSensitivityAnalysisResult({
setIsLoading(false);
});
}, [
sortConfig,
nOrNkIndex,
sensiKind,
page,
rowsPerPage,
page,
filters,
sortConfig,
globalFilters,
studyUuid,
nodeUuid,
currentRootNetworkUuid,
Expand All @@ -206,7 +217,7 @@ function PagedSensitivityAnalysisResult({
fetchFilterOptions();
fetchResult();
}
}, [sensiStatus, fetchResult, fetchFilterOptions]);
}, [sensiStatus, fetchResult, fetchFilterOptions, globalFilters]);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { SyntheticEvent, useCallback, useState } from 'react';
import { SyntheticEvent, useCallback, useMemo, useState } from 'react';
import { Box, LinearProgress, Tab, Tabs } from '@mui/material';
import SensitivityAnalysisTabs from './sensitivity-analysis-tabs.js';
import PagedSensitivityAnalysisResult from './paged-sensitivity-analysis-result';
Expand All @@ -22,7 +22,16 @@ import { useIntl } from 'react-intl';
import { ExportButton } from '../../utils/export-button';
import { AppState } from '../../../redux/reducer';
import { UUID } from 'crypto';
import { COMPUTATION_RESULTS_LOGS, SensiTab, SENSITIVITY_IN_DELTA_MW } from './sensitivity-analysis-result.type';
import {
COMPUTATION_RESULTS_LOGS,
SensiTab,
SENSITIVITY_AT_NODE,
SENSITIVITY_IN_DELTA_MW,
} from './sensitivity-analysis-result.type';
import useGlobalFilters from '../common/global-filter/use-global-filters';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import { useGlobalFilterData } from '../common/global-filter/use-global-filter-data';
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';

export type SensitivityAnalysisResultTabProps = {
studyUuid: UUID;
Expand All @@ -46,6 +55,9 @@ function SensitivityAnalysisResultTab({
(state: AppState) => state.computingStatus[ComputingType.SENSITIVITY_ANALYSIS]
);

const { globalFilters, handleGlobalFilterChange } = useGlobalFilters({});
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterData();

const initTable = () => {
/* set page to 0 to avoid being in out of range (0 to 0, but page is > 0)
for the page prop of MUI TablePagination if was not on the first page
Expand Down Expand Up @@ -99,6 +111,23 @@ function SensitivityAnalysisResultTab({
.finally(() => setIsCsvExportLoading(false));
}, [snackError, studyUuid, nodeUuid, currentRootNetworkUuid, intl, nOrNkIndex, sensiTab, csvHeaders]);

const filterableEquipmentTypes: EQUIPMENT_TYPES[] = useMemo(() => {
return sensiTab === SENSITIVITY_AT_NODE
? [EQUIPMENT_TYPES.VOLTAGE_LEVEL]
: [EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, EQUIPMENT_TYPES.LINE];
}, [sensiTab]);

const globalFiltersComponent = useMemo(
() => (
<GlobalFilterSelector
onChange={handleGlobalFilterChange}
filters={[...voltageLevelsFilter, ...countriesFilter, ...propertiesFilter]}
filterableEquipmentTypes={filterableEquipmentTypes}
/>
),
[countriesFilter, filterableEquipmentTypes, handleGlobalFilterChange, voltageLevelsFilter, propertiesFilter]
);

return (
<>
<SensitivityAnalysisTabs sensiTab={sensiTab} setSensiTab={handleSensiTabChange} />
Expand All @@ -116,6 +145,7 @@ function SensitivityAnalysisResultTab({
<Tab key={tab.label} label={tab.label} />
))}
</Tabs>
<Box>{globalFiltersComponent}</Box>
<ExportButton
disabled={isCsvButtonDisabled}
onClick={handleExportResultAsCsv}
Expand All @@ -133,6 +163,7 @@ function SensitivityAnalysisResultTab({
setPage={setPage}
setCsvHeaders={setCsvHeaders}
setIsCsvButtonDisabled={setIsCsvButtonDisabled}
globalFilters={globalFilters}
/>
</>
)}
Expand Down
Loading
Loading