fix(ws): fixed filter by labels during workspace creation (#524)

Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com>
This commit is contained in:
Paulo Rego 2025-08-07 16:59:55 -03:00 committed by Bhakti Narvekar
parent fd2dc790ff
commit a932c0fc4c
5 changed files with 53 additions and 67 deletions

View File

@ -22,34 +22,18 @@ type FilterableDataFieldKeys = FilterableDataFieldKey<typeof fields>;
type WorkspaceFormImageListProps = {
images: WorkspacekindsImageConfigValue[];
selectedLabels: Map<string, Set<string>>;
selectedImage: WorkspacekindsImageConfigValue | undefined;
onSelect: (workspaceImage: WorkspacekindsImageConfigValue | undefined) => void;
};
export const WorkspaceFormImageList: React.FunctionComponent<WorkspaceFormImageListProps> = ({
images,
selectedLabels,
selectedImage,
onSelect,
}) => {
const [filters, setFilters] = useState<FilteredColumn[]>([]);
const filterRef = useRef<FilterRef>(null);
const getFilteredWorkspaceImagesByLabels = useCallback(
(unfilteredImages: WorkspacekindsImageConfigValue[]) =>
unfilteredImages.filter((image) =>
image.labels.reduce((accumulator, label) => {
if (selectedLabels.has(label.key)) {
const labelValues: Set<string> | 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<WorkspaceFormImageL
searchValueInput = new RegExp(filter.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
}
const filterResult = result.filter((image) => {
return result.filter((image) => {
if (filter.value === '') {
return true;
}
@ -82,9 +66,8 @@ export const WorkspaceFormImageList: React.FunctionComponent<WorkspaceFormImageL
return true;
}
});
return getFilteredWorkspaceImagesByLabels(filterResult);
}, images);
}, [filters, getFilteredWorkspaceImagesByLabels, images]);
}, [filters, images]);
const onChange = useCallback(
(event: React.FormEvent<HTMLInputElement>) => {

View File

@ -16,17 +16,16 @@ const WorkspaceFormImageSelection: React.FunctionComponent<WorkspaceFormImageSel
selectedImage,
onSelect,
}) => {
const [selectedLabels, setSelectedLabels] = useState<Map<string, Set<string>>>(new Map());
const [filteredImages, setFilteredImages] = useState<WorkspacekindsImageConfigValue[]>(images);
const imageFilterContent = useMemo(
() => (
<FilterByLabels
labelledObjects={images.flatMap((image) => 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<WorkspaceFormImageSel
<SplitItem style={{ minWidth: '200px' }}>{imageFilterContent}</SplitItem>
<SplitItem isFilled>
<WorkspaceFormImageList
images={images}
selectedLabels={selectedLabels}
images={filteredImages}
selectedImage={selectedImage}
onSelect={onSelect}
/>

View File

@ -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<string, Set<string>>;
onSelect: (labels: Map<string, Set<string>>) => void;
labelledObjects: { labels: { key: string; value: string }[] }[];
setLabelledObjects: (labelledObjects: { labels: { key: string; value: string }[] }[]) => void;
};
export const FilterByLabels: React.FunctionComponent<FilterByLabelsProps> = ({
labelledObjects,
selectedLabels,
onSelect,
setLabelledObjects,
}) => {
const [selectedLabels, setSelectedLabels] = useState<Map<string, Set<string>>>(new Map());
const filterMap = useMemo(() => {
const labelsMap = new Map<string, Set<string>>();
labelledObjects.forEach((labelledObject) => {
if (!labelsMap.has(labelledObject.key)) {
labelsMap.set(labelledObject.key, new Set<string>());
}
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<string>());
}
labelsMap.get(label.key)?.add(label.value);
});
return labelsMap;
}, [labelledObjects]);
@ -53,9 +54,30 @@ export const FilterByLabels: React.FunctionComponent<FilterByLabelsProps> = ({
}
}
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 (

View File

@ -22,31 +22,16 @@ type FilterableDataFieldKeys = FilterableDataFieldKey<typeof fields>;
type WorkspaceFormPodConfigListProps = {
podConfigs: WorkspacekindsPodConfigValue[];
selectedLabels: Map<string, Set<string>>;
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<FilteredColumn[]>([]);
const filterRef = useRef<FilterRef>(null);
const getFilteredWorkspacePodConfigsByLabels = useCallback(
(unfilteredPodConfigs: WorkspacekindsPodConfigValue[]) =>
unfilteredPodConfigs.filter((podConfig) =>
podConfig.labels.reduce((accumulator, label) => {
if (selectedLabels.has(label.key)) {
const labelValues: Set<string> | 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<HTMLInputElement>) => {

View File

@ -14,17 +14,17 @@ interface WorkspaceFormPodConfigSelectionProps {
const WorkspaceFormPodConfigSelection: React.FunctionComponent<
WorkspaceFormPodConfigSelectionProps
> = ({ podConfigs, selectedPodConfig, onSelect }) => {
const [selectedLabels, setSelectedLabels] = useState<Map<string, Set<string>>>(new Map());
const [filteredPodConfigs, setFilteredPodConfigs] =
useState<WorkspacekindsPodConfigValue[]>(podConfigs);
const podConfigFilterContent = useMemo(
() => (
<FilterByLabels
labelledObjects={podConfigs.flatMap((podConfig) => 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<
<SplitItem style={{ minWidth: '200px' }}>{podConfigFilterContent}</SplitItem>
<SplitItem isFilled>
<WorkspaceFormPodConfigList
podConfigs={podConfigs}
selectedLabels={selectedLabels}
podConfigs={filteredPodConfigs}
selectedPodConfig={selectedPodConfig}
onSelect={onSelect}
/>