Skip to content

Commit 98fc29b

Browse files
authored
[UI]: Implement dynamic property filter options (#3621)
* [UI]: Implemented dynamic property filter options on run list page #3473 * [UI]: Implemented dynamic property filter options on offer, fleet, instance, volume, event, model list pages #3473
1 parent 4b4d1f6 commit 98fc29b

17 files changed

Lines changed: 444 additions & 241 deletions

File tree

frontend/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { useConfirmationDialog } from './useConfirmationDialog';
66
export { useHelpPanel } from './useHelpPanel';
77
export { usePermissionGuard } from './usePermissionGuard';
88
export { useInfiniteScroll } from './useInfiniteScroll';
9+
export { useLocalStorageState } from './useLocalStorageState';
910

1011
// cloudscape
1112
export { useCollection } from '@cloudscape-design/collection-hooks';

frontend/src/pages/Events/List/hooks/useFilters.ts

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { omit } from 'lodash';
55
import type { PropertyFilterProps } from 'components';
66

77
import { EMPTY_QUERY, requestParamsToTokens, tokensToRequestParams, tokensToSearchParams } from 'libs/filters';
8-
import { useGetProjectsQuery } from 'services/project';
9-
import { useGetUserListQuery } from 'services/user';
8+
import { useLazyGetProjectsQuery } from 'services/project';
9+
import { useLazyGetUserListQuery } from 'services/user';
1010

1111
import { filterLastElementByPrefix } from '../helpers';
1212

@@ -71,42 +71,49 @@ const baseFilteringProperties = [
7171
key: filterKeys.TARGET_USERS,
7272
operators: ['='],
7373
propertyLabel: 'Target users',
74-
groupValuesLabel: 'Project ids',
74+
groupValuesLabel: 'User ids',
7575
},
7676
{
7777
key: filterKeys.TARGET_FLEETS,
7878
operators: ['='],
7979
propertyLabel: 'Target fleet IDs',
80+
groupValuesLabel: 'Fleet ids',
8081
},
8182
{
8283
key: filterKeys.TARGET_INSTANCES,
8384
operators: ['='],
8485
propertyLabel: 'Target instance IDs',
86+
groupValuesLabel: 'Instance ids',
8587
},
8688
{
8789
key: filterKeys.TARGET_RUNS,
8890
operators: ['='],
8991
propertyLabel: 'Target run IDs',
92+
groupValuesLabel: 'Run ids',
9093
},
9194
{
9295
key: filterKeys.TARGET_JOBS,
9396
operators: ['='],
9497
propertyLabel: 'Target job IDs',
98+
groupValuesLabel: 'Job ids',
9599
},
96100
{
97101
key: filterKeys.TARGET_VOLUMES,
98102
operators: ['='],
99103
propertyLabel: 'Target volume IDs',
104+
groupValuesLabel: 'Volume ids',
100105
},
101106
{
102107
key: filterKeys.TARGET_GATEWAYS,
103108
operators: ['='],
104109
propertyLabel: 'Target gateway IDs',
110+
groupValuesLabel: 'Gateway ids',
105111
},
106112
{
107113
key: filterKeys.TARGET_SECRETS,
108114
operators: ['='],
109115
propertyLabel: 'Target secret IDs',
116+
groupValuesLabel: 'Secret ids',
110117
},
111118

112119
{
@@ -120,12 +127,14 @@ const baseFilteringProperties = [
120127
key: filterKeys.WITHIN_FLEETS,
121128
operators: ['='],
122129
propertyLabel: 'Within fleet IDs',
130+
groupValuesLabel: 'Fleet ids',
123131
},
124132

125133
{
126134
key: filterKeys.WITHIN_RUNS,
127135
operators: ['='],
128136
propertyLabel: 'Within run IDs',
137+
groupValuesLabel: 'Run ids',
129138
},
130139

131140
{
@@ -139,9 +148,12 @@ const baseFilteringProperties = [
139148
key: filterKeys.ACTORS,
140149
operators: ['='],
141150
propertyLabel: 'Actors',
151+
groupValuesLabel: 'User names',
142152
},
143153
];
144154

155+
const limit = 100;
156+
145157
export const useFilters = ({
146158
permanentFilters,
147159
withSearchParams,
@@ -150,8 +162,10 @@ export const useFilters = ({
150162
withSearchParams?: boolean;
151163
}) => {
152164
const [searchParams, setSearchParams] = useSearchParams();
153-
const { data: projectsData, isLoading: isLoadingProjects } = useGetProjectsQuery({});
154-
const { data: usersData, isLoading: isLoadingUsers } = useGetUserListQuery({});
165+
const [dynamicFilteringOptions, setDynamicFilteringOptions] = useState<PropertyFilterProps.FilteringOption[]>([]);
166+
const [filteringStatusType, setFilteringStatusType] = useState<PropertyFilterProps.StatusType | undefined>();
167+
const [getProjects] = useLazyGetProjectsQuery();
168+
const [getUsers] = useLazyGetUserListQuery();
155169

156170
const [propertyFilterQuery, setPropertyFilterQuery] = useState<PropertyFilterProps.Query>(() =>
157171
requestParamsToTokens<RequestParamsKeys>({ searchParams, filterKeys }),
@@ -165,31 +179,7 @@ export const useFilters = ({
165179
};
166180

167181
const filteringOptions = useMemo(() => {
168-
const options: PropertyFilterProps.FilteringOption[] = [];
169-
170-
projectsData?.data?.forEach(({ project_name }) => {
171-
options.push({
172-
propertyKey: filterKeys.TARGET_PROJECTS,
173-
value: project_name,
174-
});
175-
176-
options.push({
177-
propertyKey: filterKeys.WITHIN_PROJECTS,
178-
value: project_name,
179-
});
180-
});
181-
182-
usersData?.data?.forEach(({ username }) => {
183-
options.push({
184-
propertyKey: filterKeys.TARGET_USERS,
185-
value: username,
186-
});
187-
188-
options.push({
189-
propertyKey: filterKeys.ACTORS,
190-
value: username,
191-
});
192-
});
182+
const options: PropertyFilterProps.FilteringOption[] = [...dynamicFilteringOptions];
193183

194184
targetTypes?.forEach((targetType) => {
195185
options.push({
@@ -199,7 +189,7 @@ export const useFilters = ({
199189
});
200190

201191
return options;
202-
}, [projectsData, usersData]);
192+
}, [dynamicFilteringOptions]);
203193

204194
const setSearchParamsHandle = ({ tokens }: { tokens: PropertyFilterProps.Query['tokens'] }) => {
205195
const searchParams = tokensToSearchParams<RequestParamsKeys>(tokens);
@@ -293,25 +283,15 @@ export const useFilters = ({
293283
return [paramsFilter, permanentFilter];
294284
};
295285

296-
const targetProjects = filterParamsWithPermanentFitters(filterKeys.TARGET_PROJECTS)
297-
.map((name: string) => projectsData?.data?.find(({ project_name }) => project_name === name)?.['project_id'])
298-
.filter(Boolean);
286+
const targetProjects = filterParamsWithPermanentFitters(filterKeys.TARGET_PROJECTS).filter(Boolean);
299287

300-
const withInProjects = filterParamsWithPermanentFitters(filterKeys.WITHIN_PROJECTS)
301-
.map((name: string) => projectsData?.data?.find(({ project_name }) => project_name === name)?.['project_id'])
302-
.filter(Boolean);
288+
const withInProjects = filterParamsWithPermanentFitters(filterKeys.WITHIN_PROJECTS).filter(Boolean);
303289

304-
const targetUsers = filterParamsWithPermanentFitters(filterKeys.TARGET_USERS)
305-
.map((name: string) => usersData?.data?.find(({ username }) => username === name)?.['id'])
306-
.filter(Boolean);
290+
const targetUsers = filterParamsWithPermanentFitters(filterKeys.TARGET_USERS).filter(Boolean);
307291

308-
const actors = filterParamsWithPermanentFitters(filterKeys.ACTORS)
309-
.map((name: string) => usersData?.data?.find(({ username }) => username === name)?.['id'])
310-
.filter(Boolean);
292+
const actors = filterParamsWithPermanentFitters(filterKeys.ACTORS).filter(Boolean);
311293

312-
const includeTargetTypes = filterParamsWithPermanentFitters(filterKeys.INCLUDE_TARGET_TYPES)
313-
.map((selectedLabel: string) => targetTypes?.find(({ label }) => label === selectedLabel)?.['value'])
314-
.filter(Boolean);
294+
const includeTargetTypes = filterParamsWithPermanentFitters(filterKeys.INCLUDE_TARGET_TYPES).filter(Boolean);
315295

316296
const mappedFields = {
317297
...(targetProjects?.length
@@ -355,7 +335,47 @@ export const useFilters = ({
355335
...permanentFilters,
356336
...mappedFields,
357337
} as TEventListFilters;
358-
}, [propertyFilterQuery, usersData, projectsData, permanentFilters]);
338+
}, [propertyFilterQuery, permanentFilters]);
339+
340+
const handleLoadItems: PropertyFilterProps['onLoadItems'] = async ({ detail: { filteringProperty, filteringText } }) => {
341+
setDynamicFilteringOptions([]);
342+
343+
if (!filteringText.length) {
344+
return Promise.resolve();
345+
}
346+
347+
setFilteringStatusType('loading');
348+
349+
if (filteringProperty?.key === filterKeys.TARGET_PROJECTS || filteringProperty?.key === filterKeys.WITHIN_PROJECTS) {
350+
await getProjects({ name_pattern: filteringText, limit })
351+
.unwrap()
352+
.then(({ data }) =>
353+
data.map(({ project_name, project_id }) => ({
354+
propertyKey: filteringProperty?.key,
355+
label: project_name,
356+
value: project_id,
357+
hiddenValue: 'test',
358+
})),
359+
)
360+
.then(setDynamicFilteringOptions);
361+
}
362+
363+
if (filteringProperty?.key === filterKeys.TARGET_USERS || filteringProperty?.key === filterKeys.ACTORS) {
364+
await getUsers({ name_pattern: filteringText, limit })
365+
.unwrap()
366+
.then(({ data }) =>
367+
data.map(({ username, id }) => ({
368+
propertyKey: filteringProperty?.key,
369+
label: username,
370+
value: id,
371+
hiddenValue: 'test2',
372+
})),
373+
)
374+
.then(setDynamicFilteringOptions);
375+
}
376+
377+
setFilteringStatusType(undefined);
378+
};
359379

360380
return {
361381
filteringRequestParams,
@@ -364,6 +384,7 @@ export const useFilters = ({
364384
onChangePropertyFilter,
365385
filteringOptions,
366386
filteringProperties,
367-
isLoadingFilters: isLoadingProjects || isLoadingUsers,
387+
filteringStatusType,
388+
handleLoadItems,
368389
} as const;
369390
};

frontend/src/pages/Events/List/index.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { useLazyGetAllEventsQuery } from 'services/events';
1313
import { useColumnsDefinitions } from './hooks/useColumnDefinitions';
1414
import { useFilters } from './hooks/useFilters';
1515

16-
import styles from '../../Runs/List/styles.module.scss';
16+
import styles from 'pages/Runs/List/styles.module.scss';
1717

1818
type RenderHeaderArgs = {
1919
refreshAction?: () => void;
@@ -49,13 +49,13 @@ export const EventList: React.FC<EventListProps> = ({
4949
onChangePropertyFilter,
5050
filteringOptions,
5151
filteringProperties,
52-
isLoadingFilters,
52+
filteringStatusType,
53+
handleLoadItems,
5354
} = useFilters({ permanentFilters, withSearchParams });
5455

5556
const { data, isLoading, refreshList, isLoadingMore } = useInfiniteScroll<IEvent, TEventListRequestParams>({
5657
useLazyQuery: useLazyGetAllEventsQuery,
5758
args: { ...filteringRequestParams, limit: DEFAULT_TABLE_PAGE_SIZE },
58-
skip: isLoadingFilters,
5959

6060
getPaginationParams: (lastEvent) => ({
6161
prev_recorded_at: lastEvent.recorded_at,
@@ -73,7 +73,7 @@ export const EventList: React.FC<EventListProps> = ({
7373

7474
const { columns } = useColumnsDefinitions();
7575

76-
const loading = isLoadingFilters || isLoading;
76+
const loading = isLoading;
7777

7878
return (
7979
<Table
@@ -99,9 +99,12 @@ export const EventList: React.FC<EventListProps> = ({
9999
filteringAriaLabel: t('projects.run.filter_property_placeholder'),
100100
filteringPlaceholder: t('projects.run.filter_property_placeholder'),
101101
operationAndText: 'and',
102+
enteredTextLabel: (value) => `Use: ${value}`,
102103
}}
103104
filteringOptions={filteringOptions}
104105
filteringProperties={filteringProperties}
106+
filteringStatusType={filteringStatusType}
107+
onLoadItems={handleLoadItems}
105108
/>
106109
</div>
107110
</div>

0 commit comments

Comments
 (0)