From 92a7ff5b202100996f9e995c18b1dcc388c03b40 Mon Sep 17 00:00:00 2001 From: Keita Fish Date: Tue, 13 Aug 2024 09:44:01 -0700 Subject: [PATCH] feat: support filter by metadata with string type (#9810) --- .../src/components/FilterForm/TableFilter.tsx | 9 +- .../FilterForm/components/FilterField.tsx | 99 +++++++++++++++---- .../FilterForm/components/FilterForm.tsx | 4 +- .../FilterForm/components/FilterGroup.tsx | 3 + webui/react/src/pages/FlatRuns/FlatRuns.tsx | 1 + webui/react/src/services/api.ts | 6 ++ webui/react/src/services/apiConfig.ts | 10 ++ webui/react/src/services/types.ts | 5 + 8 files changed, 116 insertions(+), 21 deletions(-) diff --git a/webui/react/src/components/FilterForm/TableFilter.tsx b/webui/react/src/components/FilterForm/TableFilter.tsx index 6eb725f9ea7..54fbb3b33e9 100644 --- a/webui/react/src/components/FilterForm/TableFilter.tsx +++ b/webui/react/src/components/FilterForm/TableFilter.tsx @@ -16,6 +16,7 @@ interface Props { formStore: FilterFormStore; isMobile?: boolean; isOpenFilter: boolean; + projectId?: number; onIsOpenFilterChange?: (value: boolean) => void; } @@ -25,6 +26,7 @@ const TableFilter = ({ formStore, isMobile = false, isOpenFilter, + projectId, onIsOpenFilterChange, }: Props): JSX.Element => { const columns: V1ProjectColumn[] = Loadable.getOrElse([], loadableColumns).filter( @@ -66,7 +68,12 @@ const TableFilter = ({ } }} onMouseMove={(e) => e.stopPropagation()}> - + } open={isOpenFilter} diff --git a/webui/react/src/components/FilterForm/components/FilterField.tsx b/webui/react/src/components/FilterForm/components/FilterField.tsx index 4a4418d53d1..c9397da018b 100644 --- a/webui/react/src/components/FilterForm/components/FilterField.tsx +++ b/webui/react/src/components/FilterForm/components/FilterField.tsx @@ -4,10 +4,11 @@ import DatePicker, { DatePickerProps } from 'hew/DatePicker'; import Icon from 'hew/Icon'; import Input from 'hew/Input'; import InputNumber from 'hew/InputNumber'; +import InputSelect from 'hew/InputSelect'; import Select, { SelectProps, SelectValue } from 'hew/Select'; -import { Loadable } from 'hew/utils/loadable'; +import { Loadable, NotLoaded } from 'hew/utils/loadable'; import { Observable, useObservable } from 'micro-observables'; -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useDrag, useDrop } from 'react-dnd'; import { debounce } from 'throttle-debounce'; @@ -26,7 +27,9 @@ import { SEARCHER_TYPE, SpecialColumnNames, } from 'components/FilterForm/components/type'; -import { V1ColumnType, V1ProjectColumn } from 'services/api-ts-sdk'; +import { useAsync } from 'hooks/useAsync'; +import { getMetadataValues } from 'services/api'; +import { V1ColumnType, V1LocationType, V1ProjectColumn } from 'services/api-ts-sdk'; import clusterStore from 'stores/cluster'; import userStore from 'stores/users'; import { alphaNumericSorter } from 'utils/sort'; @@ -37,6 +40,12 @@ const debounceFunc = debounce(1000, (func: () => void) => { func(); }); +const COLUMN_TYPE = { + NormalColumnType: 'NormalColumnType', + SpecialColumnType: 'SpecialColumnType', + StringMetadataColumnType: 'StringMetadataColumn', +} as const; + interface Props { index: number; // start from 0 field: FormField; @@ -45,6 +54,7 @@ interface Props { formStore: FilterFormStore; level: number; // start from 0 columns: V1ProjectColumn[]; + projectId?: number; } const FilterField = ({ @@ -55,12 +65,20 @@ const FilterField = ({ parentId, level, columns, + projectId, }: Props): JSX.Element => { const users = Loadable.getOrElse([], useObservable(userStore.getUsers())); const resourcePools = Loadable.getOrElse([], useObservable(clusterStore.resourcePools)); - const currentColumn = columns.find((c) => c.column === field.columnName); - const isSpecialColumn = (SpecialColumnNames as ReadonlyArray).includes(field.columnName); + + const columnType = useMemo(() => { + if (field.location === V1LocationType.RUNMETADATA && field.type === V1ColumnType.TEXT) { + return COLUMN_TYPE.StringMetadataColumnType; + } else if ((SpecialColumnNames as ReadonlyArray).includes(field.columnName)) { + return COLUMN_TYPE.SpecialColumnType; + } + return COLUMN_TYPE.NormalColumnType; + }, [field.columnName, field.location, field.type]); const [inputOpen, setInputOpen] = useState(false); const [fieldValue, setFieldValue] = useState(field.value); @@ -98,6 +116,21 @@ const FilterField = ({ } }; + const metadataValues = useAsync(async () => { + try { + if (projectId !== undefined && columnType === COLUMN_TYPE.StringMetadataColumnType) { + const metadataValues = await getMetadataValues({ + key: field.columnName.replace(/^metadata\./, ''), + projectId, + }); + return metadataValues; + } + return []; + } catch (e) { + return NotLoaded; + } + }, [columnType, field.columnName, projectId]); + const getSpecialOptions = (columnName: SpecialColumnNames): SelectProps['options'] => { switch (columnName) { case 'resourcePool': @@ -170,12 +203,16 @@ const FilterField = ({ if (e.key === 'Enter' && !inputOpen && !e.nativeEvent.isComposing && e.keyCode !== 229) { formStore.addChild(parentId, FormKind.Field, { index: index + 1, item: getInitField() }); // stop panel flashing for selects and dates - if (field.type === 'COLUMN_TYPE_DATE' || isSpecialColumn) { + if ( + field.type === 'COLUMN_TYPE_DATE' || + columnType === COLUMN_TYPE.SpecialColumnType || + columnType === COLUMN_TYPE.StringMetadataColumnType + ) { e.stopPropagation(); } } }, - [field.type, formStore, index, inputOpen, isSpecialColumn, parentId], + [columnType, field.type, formStore, index, inputOpen, parentId], ); return ( @@ -203,10 +240,16 @@ const FilterField = ({ /> + options.filter((opt) => opt.includes(filterValue)) + } data-test="special" - options={getSpecialOptions(field.columnName as SpecialColumnNames)} - value={fieldValue ?? undefined} + options={metadataValues.getOrElse([])} + value={typeof fieldValue === 'string' ? fieldValue : undefined} width={'100%'} onChange={(value) => { - const val = value?.toString() ?? null; - updateFieldValue(field.id, val); + updateFieldValue(field.id, value); }} onDropdownVisibleChange={setInputOpen} /> - + ) : ( + // SpecialColumnType +
+