Skip to content

Commit 9b14036

Browse files
favorite countries in the filter dialog (#532)
Signed-off-by: Mathieu DEHARBE <[email protected]>
1 parent 93111f1 commit 9b14036

File tree

8 files changed

+122
-33
lines changed

8 files changed

+122
-33
lines changed

src/components/filter/expert/expert-filter-constants.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ export const FIELDS_OPTIONS = {
255255
label: 'country',
256256
dataType: DataType.ENUM,
257257
valueEditorType: 'select',
258-
defaultValue: 'AF',
259258
},
260259
VOLTAGE_REGULATOR_ON: {
261260
name: FieldType.VOLTAGE_REGULATOR_ON,
@@ -493,14 +492,12 @@ export const FIELDS_OPTIONS = {
493492
label: 'country1',
494493
dataType: DataType.ENUM,
495494
valueEditorType: 'select',
496-
defaultValue: 'AF',
497495
},
498496
COUNTRY_2: {
499497
name: FieldType.COUNTRY_2,
500498
label: 'country2',
501499
dataType: DataType.ENUM,
502500
valueEditorType: 'select',
503-
defaultValue: 'AF',
504501
},
505502
SERIE_RESISTANCE: {
506503
name: FieldType.SERIE_RESISTANCE,

src/components/filter/expert/expert-filter-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ export const queryValidator: QueryValidator = (query) => {
409409
const dataType = getDataType(rule.field, rule.operator);
410410

411411
const isNumberInput = dataType === DataType.NUMBER && !isValueAnArray;
412-
const isStringInput = dataType === DataType.STRING && !isValueAnArray;
412+
const isStringInput = (dataType === DataType.STRING || dataType === DataType.ENUM) && !isValueAnArray;
413413

414414
if (
415415
rule.id &&
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { Autocomplete, Box, TextField, Theme } from '@mui/material';
9+
import { useMemo } from 'react';
10+
import { AutocompleteProps } from '@mui/material/Autocomplete/Autocomplete';
11+
12+
const styles = {
13+
favBox: (theme: Theme) => ({
14+
borderBottom: '1px solid',
15+
borderColor: theme.palette.divider,
16+
}),
17+
};
18+
19+
interface AutocompleteWithFavoritesProps<Value>
20+
extends Omit<
21+
AutocompleteProps<Value, boolean, false, boolean>,
22+
'multiple' | 'renderInput' | 'renderGroup' | 'groupBy'
23+
> {
24+
favorites: Value[];
25+
valid: boolean;
26+
}
27+
28+
/**
29+
* Autocomplete component which allows to have a few "favorite" options always displayed
30+
* at the beginning of the selector and separated from the others options
31+
*/
32+
function AutocompleteWithFavorites<Value>({
33+
favorites,
34+
valid,
35+
options,
36+
value,
37+
...otherProps
38+
}: AutocompleteWithFavoritesProps<Value>) {
39+
const optionsWithFavorites = useMemo(() => {
40+
if (favorites) {
41+
// remove favorites from standardOptions to avoid duplicate keys
42+
const optionsWithoutFavorites = options.filter((option: Value) => !favorites.includes(option));
43+
return [...favorites, ...optionsWithoutFavorites];
44+
}
45+
return options;
46+
}, [options, favorites]);
47+
48+
return (
49+
<Autocomplete
50+
size="small"
51+
value={value}
52+
options={optionsWithFavorites}
53+
{...otherProps}
54+
/* props should not be overridden */
55+
groupBy={(option: Value) => (favorites.includes(option) ? `fav` : 'not_fav')}
56+
multiple={Array.isArray(value)}
57+
renderInput={(params) => <TextField {...params} error={!valid} />}
58+
renderGroup={(item) => {
59+
const { group, children } = item;
60+
return (
61+
<Box key={`keyBoxGroup_${group}`} sx={styles.favBox}>
62+
{children}
63+
</Box>
64+
);
65+
}}
66+
/>
67+
);
68+
}
69+
70+
export default AutocompleteWithFavorites;

src/components/inputs/react-query-builder/country-value-editor.tsx

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,51 @@
66
*/
77

88
import { ValueEditorProps } from 'react-querybuilder';
9-
import { MaterialValueEditor } from '@react-querybuilder/material';
10-
import { Autocomplete, TextField } from '@mui/material';
11-
import { useMemo } from 'react';
12-
import useConvertValue from './use-convert-value';
13-
import useValid from './use-valid';
9+
import { SyntheticEvent, useEffect, useState } from 'react';
1410
import { useLocalizedCountries } from '../../../hooks/localized-countries-hook';
1511
import useCustomFormContext from '../react-hook-form/provider/use-custom-form-context';
12+
import { fetchFavoriteAndDefaultCountries } from '../../../services';
13+
import AutocompleteWithFavorites from './autocomplete-with-favorites';
14+
import useConvertValue from './use-convert-value';
15+
import useValid from './use-valid';
1616

1717
function CountryValueEditor(props: ValueEditorProps) {
18+
const { value, handleOnChange } = props;
19+
1820
const { language } = useCustomFormContext();
1921
const { translate, countryCodes } = useLocalizedCountries(language);
20-
const { value, handleOnChange } = props;
22+
const [favoriteCountryCodes, setFavoriteCountryCodes] = useState<string[]>([]);
23+
const [initialized, setInitialized] = useState<boolean>(false);
24+
25+
// fetch favorite countries and default country
26+
useEffect(() => {
27+
if (!initialized) {
28+
fetchFavoriteAndDefaultCountries().then(({ favoriteCountries, defaultCountry }) => {
29+
setFavoriteCountryCodes(favoriteCountries);
30+
if (defaultCountry && !value) {
31+
handleOnChange(defaultCountry);
32+
}
33+
setInitialized(true);
34+
});
35+
}
36+
}, [initialized, setInitialized, handleOnChange, value]);
2137

22-
const countriesList = useMemo(
23-
() =>
24-
countryCodes.map((country: string) => {
25-
return { name: country, label: translate(country) };
26-
}),
27-
[countryCodes, translate]
28-
);
2938
// When we switch to 'in' operator, we need to switch the input value to an array and vice versa
3039
useConvertValue(props);
3140

3241
const valid = useValid(props);
3342

34-
// The displayed component totally depends on the value type and not the operator. This way, we have smoother transition.
35-
if (!Array.isArray(value)) {
36-
return (
37-
<MaterialValueEditor
38-
{...props}
39-
values={countriesList}
40-
title={undefined} // disable the tooltip
41-
/>
42-
);
43-
}
4443
return (
45-
<Autocomplete
44+
<AutocompleteWithFavorites
4645
value={value}
4746
options={countryCodes}
48-
getOptionLabel={(code: string) => translate(code)}
49-
onChange={(event, newValue: any) => handleOnChange(newValue)}
50-
multiple
47+
favorites={favoriteCountryCodes}
48+
getOptionLabel={(code: string) => (code ? translate(code) : '')}
49+
valid={valid}
50+
onChange={(event: SyntheticEvent, newValue: any) => {
51+
handleOnChange(newValue);
52+
}}
5153
fullWidth
52-
renderInput={(params) => <TextField {...params} error={!valid} />}
5354
/>
5455
);
5556
}

src/components/inputs/react-query-builder/property-value-editor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@ function PropertyValueEditor(props: ExpertFilterPropertyProps) {
8282
onChange={(event, value: any) => {
8383
onChange(FieldConstants.PROPERTY_NAME, value);
8484
}}
85+
size="small"
8586
/>
8687
</Grid>
8788
<Grid item xs={2.5}>
8889
<Select
8990
value={propertyOperator ?? PROPERTY_VALUE_OPERATORS[0].customName}
90-
size="medium"
91+
size="small"
9192
error={!valid}
9293
onChange={(event, value: any) => {
9394
onChange(FieldConstants.PROPERTY_OPERATOR, value);
@@ -111,6 +112,7 @@ function PropertyValueEditor(props: ExpertFilterPropertyProps) {
111112
onChange={(event, value: any) => {
112113
onChange(FieldConstants.PROPERTY_VALUES, value);
113114
}}
115+
size="small"
114116
/>
115117
</Grid>
116118
</Grid>

src/components/inputs/react-query-builder/text-value-editor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function TextValueEditor(props: ValueEditorProps) {
3535
multiple
3636
fullWidth
3737
renderInput={(params) => <TextField {...params} error={!valid} />}
38+
size="small"
3839
/>
3940
);
4041
}

src/components/inputs/react-query-builder/translated-value-editor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function TranslatedValueEditor(props: ValueEditorProps) {
5656
multiple
5757
fullWidth
5858
renderInput={(params) => <TextField {...params} error={!valid} />}
59+
size="small"
5960
/>
6061
);
6162
}

src/services/apps-metadata.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export type StudyMetadata = CommonMetadata & {
4646
mapManualRefresh?: string; // maybe 'true'|'false' type?
4747
};
4848
defaultCountry?: string;
49+
favoriteCountries?: string[];
4950
};
5051

5152
export async function fetchAppsMetadata(): Promise<CommonMetadata[]> {
@@ -68,3 +69,19 @@ export async function fetchStudyMetadata(): Promise<StudyMetadata> {
6869
return studyMetadata[0]; // There should be only one study metadata
6970
}
7071
}
72+
73+
export async function fetchFavoriteAndDefaultCountries(): Promise<{
74+
favoriteCountries: string[];
75+
defaultCountry?: string;
76+
}> {
77+
const { favoriteCountries = [], defaultCountry } = await fetchStudyMetadata();
78+
return {
79+
favoriteCountries,
80+
defaultCountry,
81+
};
82+
}
83+
84+
export const fetchDefaultCountry = async (): Promise<string | undefined> => {
85+
const studyMetadata = await fetchStudyMetadata();
86+
return studyMetadata.defaultCountry;
87+
};

0 commit comments

Comments
 (0)