From a932c0fc4c473f91a122c827529bc733e1f76a70 Mon Sep 17 00:00:00 2001 From: Paulo Rego <832830+paulovmr@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:59:55 -0300 Subject: [PATCH] fix(ws): fixed filter by labels during workspace creation (#524) Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com> --- .../Form/image/WorkspaceFormImageList.tsx | 21 +------- .../image/WorkspaceFormImageSelection.tsx | 12 ++--- .../Form/labelFilter/FilterByLabels.tsx | 52 +++++++++++++------ .../podConfig/WorkspaceFormPodConfigList.tsx | 22 ++------ .../WorkspaceFormPodConfigSelection.tsx | 13 +++-- 5 files changed, 53 insertions(+), 67 deletions(-) diff --git a/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageList.tsx b/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageList.tsx index e36d1d5f..ddf5c4aa 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageList.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageList.tsx @@ -22,34 +22,18 @@ type FilterableDataFieldKeys = FilterableDataFieldKey; type WorkspaceFormImageListProps = { images: WorkspacekindsImageConfigValue[]; - selectedLabels: Map>; selectedImage: WorkspacekindsImageConfigValue | undefined; onSelect: (workspaceImage: WorkspacekindsImageConfigValue | undefined) => void; }; export const WorkspaceFormImageList: React.FunctionComponent = ({ images, - selectedLabels, selectedImage, onSelect, }) => { const [filters, setFilters] = useState([]); const filterRef = useRef(null); - const getFilteredWorkspaceImagesByLabels = useCallback( - (unfilteredImages: WorkspacekindsImageConfigValue[]) => - unfilteredImages.filter((image) => - image.labels.reduce((accumulator, label) => { - if (selectedLabels.has(label.key)) { - const labelValues: Set | undefined = selectedLabels.get(label.key); - return accumulator && labelValues !== undefined && labelValues.has(label.value); - } - return accumulator; - }, true), - ), - [selectedLabels], - ); - const clearAllFilters = useCallback(() => { filterRef.current?.clearAll(); }, []); @@ -67,7 +51,7 @@ export const WorkspaceFormImageList: React.FunctionComponent { + return result.filter((image) => { if (filter.value === '') { return true; } @@ -82,9 +66,8 @@ export const WorkspaceFormImageList: React.FunctionComponent) => { diff --git a/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageSelection.tsx index 6123161f..fc43e378 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Form/image/WorkspaceFormImageSelection.tsx @@ -16,17 +16,16 @@ const WorkspaceFormImageSelection: React.FunctionComponent { - const [selectedLabels, setSelectedLabels] = useState>>(new Map()); + const [filteredImages, setFilteredImages] = useState(images); const imageFilterContent = useMemo( () => ( image.labels)} - selectedLabels={selectedLabels} - onSelect={setSelectedLabels} + labelledObjects={images} + setLabelledObjects={(obj) => setFilteredImages(obj as WorkspacekindsImageConfigValue[])} /> ), - [images, selectedLabels, setSelectedLabels], + [images, setFilteredImages], ); return ( @@ -35,8 +34,7 @@ const WorkspaceFormImageSelection: React.FunctionComponent{imageFilterContent} diff --git a/workspaces/frontend/src/app/pages/Workspaces/Form/labelFilter/FilterByLabels.tsx b/workspaces/frontend/src/app/pages/Workspaces/Form/labelFilter/FilterByLabels.tsx index 8ab9586e..bfe31b1f 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Form/labelFilter/FilterByLabels.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Form/labelFilter/FilterByLabels.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { FilterSidePanel, FilterSidePanelCategory, @@ -6,27 +6,28 @@ import { } from '@patternfly/react-catalog-view-extension'; import '@patternfly/react-catalog-view-extension/dist/css/react-catalog-view-extension.css'; import { formatLabelKey } from '~/shared/utilities/WorkspaceUtils'; -import { WorkspacesOptionLabel } from '~/generated/data-contracts'; type FilterByLabelsProps = { - labelledObjects: WorkspacesOptionLabel[]; - selectedLabels: Map>; - onSelect: (labels: Map>) => void; + labelledObjects: { labels: { key: string; value: string }[] }[]; + setLabelledObjects: (labelledObjects: { labels: { key: string; value: string }[] }[]) => void; }; export const FilterByLabels: React.FunctionComponent = ({ labelledObjects, - selectedLabels, - onSelect, + setLabelledObjects, }) => { + const [selectedLabels, setSelectedLabels] = useState>>(new Map()); + const filterMap = useMemo(() => { const labelsMap = new Map>(); - labelledObjects.forEach((labelledObject) => { - if (!labelsMap.has(labelledObject.key)) { - labelsMap.set(labelledObject.key, new Set()); - } - labelsMap.get(labelledObject.key)?.add(labelledObject.value); - }); + labelledObjects + .flatMap((labelledObject) => labelledObject.labels) + .forEach((label) => { + if (!labelsMap.has(label.key)) { + labelsMap.set(label.key, new Set()); + } + labelsMap.get(label.key)?.add(label.value); + }); return labelsMap; }, [labelledObjects]); @@ -53,9 +54,30 @@ export const FilterByLabels: React.FunctionComponent = ({ } } - onSelect(newSelectedLabels); + setLabelledObjects( + labelledObjects.filter((labelledObject) => + [...newSelectedLabels.entries()].reduce( + (accumulator, [selectedLabelKey, selectedLabelValues]) => { + if (selectedLabelValues.size > 0) { + return ( + accumulator && + labelledObject.labels.some( + (imageLabel) => + imageLabel.key === selectedLabelKey && + selectedLabelValues.has(imageLabel.value), + ) + ); + } + return accumulator; + }, + true, + ), + ), + ); + + setSelectedLabels(newSelectedLabels); }, - [selectedLabels, onSelect], + [selectedLabels, labelledObjects, setLabelledObjects], ); return ( diff --git a/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigList.tsx b/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigList.tsx index 7c200ec7..87a36fec 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigList.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigList.tsx @@ -22,31 +22,16 @@ type FilterableDataFieldKeys = FilterableDataFieldKey; type WorkspaceFormPodConfigListProps = { podConfigs: WorkspacekindsPodConfigValue[]; - selectedLabels: Map>; selectedPodConfig: WorkspacekindsPodConfigValue | undefined; onSelect: (workspacePodConfig: WorkspacekindsPodConfigValue | undefined) => void; }; export const WorkspaceFormPodConfigList: React.FunctionComponent< WorkspaceFormPodConfigListProps -> = ({ podConfigs, selectedLabels, selectedPodConfig, onSelect }) => { +> = ({ podConfigs, selectedPodConfig, onSelect }) => { const [filters, setFilters] = useState([]); const filterRef = useRef(null); - const getFilteredWorkspacePodConfigsByLabels = useCallback( - (unfilteredPodConfigs: WorkspacekindsPodConfigValue[]) => - unfilteredPodConfigs.filter((podConfig) => - podConfig.labels.reduce((accumulator, label) => { - if (selectedLabels.has(label.key)) { - const labelValues: Set | undefined = selectedLabels.get(label.key); - return accumulator && labelValues !== undefined && labelValues.has(label.value); - } - return accumulator; - }, true), - ), - [selectedLabels], - ); - const clearAllFilters = useCallback(() => { filterRef.current?.clearAll(); }, []); @@ -62,7 +47,7 @@ export const WorkspaceFormPodConfigList: React.FunctionComponent< } catch { searchValueInput = new RegExp(filter.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); } - const filterResult = result.filter((podConfig) => { + return result.filter((podConfig) => { if (filter.value === '') { return true; } @@ -77,9 +62,8 @@ export const WorkspaceFormPodConfigList: React.FunctionComponent< return true; } }); - return getFilteredWorkspacePodConfigsByLabels(filterResult); }, podConfigs); - }, [filters, getFilteredWorkspacePodConfigsByLabels, podConfigs]); + }, [filters, podConfigs]); const onChange = useCallback( (event: React.FormEvent) => { diff --git a/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigSelection.tsx index a82a2870..f11b4b09 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigSelection.tsx @@ -14,17 +14,17 @@ interface WorkspaceFormPodConfigSelectionProps { const WorkspaceFormPodConfigSelection: React.FunctionComponent< WorkspaceFormPodConfigSelectionProps > = ({ podConfigs, selectedPodConfig, onSelect }) => { - const [selectedLabels, setSelectedLabels] = useState>>(new Map()); + const [filteredPodConfigs, setFilteredPodConfigs] = + useState(podConfigs); const podConfigFilterContent = useMemo( () => ( podConfig.labels)} - selectedLabels={selectedLabels} - onSelect={setSelectedLabels} + labelledObjects={podConfigs} + setLabelledObjects={(obj) => setFilteredPodConfigs(obj as WorkspacekindsPodConfigValue[])} /> ), - [podConfigs, selectedLabels, setSelectedLabels], + [podConfigs, setFilteredPodConfigs], ); return ( @@ -33,8 +33,7 @@ const WorkspaceFormPodConfigSelection: React.FunctionComponent< {podConfigFilterContent}