Skip to content

Commit 3fde794

Browse files
migrate to ConsoleDataView for PL and PLR list
1 parent c64fba5 commit 3fde794

27 files changed

Lines changed: 841 additions & 1101 deletions

locales/en/plugin__pipelines-console-plugin.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,10 @@
200200
"File contains non-printable characters. Preview is not available.": "File contains non-printable characters. Preview is not available.",
201201
"Filename": "Filename",
202202
"Filesystem": "Filesystem",
203+
"Filter by data source": "Filter by data source",
203204
"Filter by label": "Filter by label",
204205
"Filter by name": "Filter by name",
206+
"Filter by status": "Filter by status",
205207
"Finally task": "Finally task",
206208
"Finally tasks": "Finally tasks",
207209
"Fit to screen": "Fit to screen",
@@ -322,7 +324,6 @@
322324
"No parameters are associated with this Pipeline.": "No parameters are associated with this Pipeline.",
323325
"No parameters are associated with this PipelineRun.": "No parameters are associated with this PipelineRun.",
324326
"No PipelineRuns found": "No PipelineRuns found",
325-
"No Pipelines found": "No Pipelines found",
326327
"No Projects found": "No Projects found",
327328
"No Properties found": "No Properties found",
328329
"No Repositories found": "No Repositories found",

src/components/approval-tasks/ApprovalTasksList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ const ApprovalTasksList: FC<ApprovalTasksListProps> = ({
4444
const { t } = useTranslation('plugin__pipelines-console-plugin');
4545
const { ns, name: pipelineRunName } = useParams();
4646
namespace = namespace || ns;
47-
const [pipelineRuns, pipelineRunsLoaded] = usePipelineRuns(namespace);
47+
const [pipelineRuns, k8sLoaded, trLoaded] = usePipelineRuns(namespace);
48+
const pipelineRunsLoaded = k8sLoaded && trLoaded;
4849
const [approvalTasks, approvalTasksLoaded, approvalTasksLoadError] =
4950
useApprovalTasks(namespace, pipelineRunName);
5051
const columns = useApprovalsColumns(namespace);

src/components/common/DataViewFilterToolbar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface CheckboxFilterConfig {
3131
title: string;
3232
placeholder?: string;
3333
options: FilterOption[];
34+
defaultValues?: string[];
3435
}
3536

3637
export interface FilterValues {
Lines changed: 235 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1+
import {
2+
K8sResourceCommon,
3+
useFlag,
4+
} from '@openshift-console/dynamic-plugin-sdk';
15
import { useCallback, useMemo, useState } from 'react';
6+
import { useTranslation } from 'react-i18next';
7+
import { useSearchParams } from 'react-router-dom-v5-compat';
8+
import { FLAG_PIPELINE_TEKTON_RESULT_INSTALLED } from '../../consts';
29
import type {
310
CheckboxFilterConfig,
411
FilterValues,
512
} from '../common/DataViewFilterToolbar';
13+
import {
14+
isPipelineRunLoadedFromTektonResults,
15+
pipelineFilterReducer,
16+
pipelineRunDataSourceFilter,
17+
pipelineRunFilterReducer,
18+
pipelineRunStatusFilter,
19+
pipelineStatusFilter,
20+
} from '../utils/pipeline-filter-reducer';
21+
import { ListFilterId, ListFilterLabels } from '../utils/pipeline-utils';
622

7-
interface UseDataViewFilterOptions<T> {
23+
export type ResourceType = 'Pipeline' | 'PipelineRun' | 'TaskRun';
24+
25+
interface UseDataViewFilterOptions<T extends K8sResourceCommon> {
826
data: T[];
9-
getName: (obj: T) => string;
27+
getName?: (obj: T) => string;
1028
getLabels?: (obj: T) => Record<string, string> | undefined;
11-
checkboxFilters?: CheckboxFilterConfig[];
12-
matchesCheckboxFilter?: (
13-
obj: T,
14-
filterId: string,
15-
selectedValues: string[],
16-
) => boolean;
29+
resourceType?: ResourceType;
30+
defaultStatusValues?: string[];
31+
defaultDataSourceValues?: string[];
1732
}
1833

1934
const matchesLabels = (
@@ -31,74 +46,256 @@ const matchesLabels = (
3146
});
3247
};
3348

34-
export const useDataViewFilter = <T>({
49+
interface ResourceFilterConfig {
50+
statusOptions: ListFilterId[];
51+
statusFilter: (phases: any, obj: any) => boolean;
52+
statusReducer: (obj: any) => string;
53+
hasDataSourceFilter: boolean;
54+
defaultStatusValues?: string[];
55+
defaultDataSourceValues?: string[];
56+
}
57+
58+
const RESOURCE_FILTER_CONFIG: Record<ResourceType, ResourceFilterConfig> = {
59+
Pipeline: {
60+
statusOptions: [
61+
ListFilterId.Succeeded,
62+
ListFilterId.Running,
63+
ListFilterId.Failed,
64+
ListFilterId.Cancelled,
65+
ListFilterId.Other,
66+
],
67+
statusFilter: pipelineStatusFilter,
68+
statusReducer: pipelineFilterReducer,
69+
hasDataSourceFilter: false,
70+
},
71+
PipelineRun: {
72+
statusOptions: [
73+
ListFilterId.Succeeded,
74+
ListFilterId.Running,
75+
ListFilterId.Failed,
76+
ListFilterId.Cancelled,
77+
ListFilterId.Other,
78+
],
79+
statusFilter: pipelineRunStatusFilter,
80+
statusReducer: pipelineRunFilterReducer,
81+
hasDataSourceFilter: true,
82+
defaultDataSourceValues: ['cluster-data'],
83+
},
84+
TaskRun: {
85+
statusOptions: [
86+
ListFilterId.Succeeded,
87+
ListFilterId.Running,
88+
ListFilterId.Failed,
89+
ListFilterId.Cancelled,
90+
],
91+
statusFilter: pipelineRunStatusFilter,
92+
statusReducer: pipelineRunFilterReducer,
93+
hasDataSourceFilter: true,
94+
defaultDataSourceValues: ['cluster-data'],
95+
},
96+
};
97+
98+
const defaultGetName = <T extends K8sResourceCommon>(obj: T): string =>
99+
obj.metadata?.name || '';
100+
101+
const defaultGetLabels = <T extends K8sResourceCommon>(
102+
obj: T,
103+
): Record<string, string> | undefined => obj.metadata?.labels;
104+
105+
export const useDataViewFilter = <T extends K8sResourceCommon>({
35106
data,
36-
getName,
37-
getLabels,
38-
checkboxFilters = [],
39-
matchesCheckboxFilter,
107+
getName = defaultGetName,
108+
getLabels = defaultGetLabels,
109+
resourceType,
110+
defaultStatusValues,
111+
defaultDataSourceValues,
40112
}: UseDataViewFilterOptions<T>) => {
113+
const { t } = useTranslation('plugin__pipelines-console-plugin');
114+
const isTektonResultEnabled = useFlag(FLAG_PIPELINE_TEKTON_RESULT_INSTALLED);
115+
const allStatusIds = useMemo(() => Object.values(ListFilterId), []);
116+
const resetFilterState = { name: '', labels: [] };
117+
118+
const checkboxFilters = useMemo<CheckboxFilterConfig[]>(() => {
119+
if (!resourceType) return [];
120+
const config = RESOURCE_FILTER_CONFIG[resourceType];
121+
if (!config?.statusOptions?.length) return [];
122+
const filters: CheckboxFilterConfig[] = [
123+
{
124+
id: 'status',
125+
title: t('Status'),
126+
placeholder: t('Filter by status'),
127+
defaultValues: defaultStatusValues ?? config.defaultStatusValues,
128+
options: config.statusOptions.map((id) => ({
129+
value: id,
130+
label: ListFilterLabels[id],
131+
count: 0,
132+
})),
133+
},
134+
];
135+
if (config.hasDataSourceFilter && isTektonResultEnabled) {
136+
filters.push({
137+
id: 'dataSource',
138+
title: t('Data source'),
139+
placeholder: t('Filter by data source'),
140+
defaultValues:
141+
defaultDataSourceValues ?? config.defaultDataSourceValues,
142+
options: [
143+
{ value: 'cluster-data', label: t('Cluster'), count: 0 },
144+
{ value: 'archived-data', label: t('Archived'), count: 0 },
145+
],
146+
});
147+
}
148+
return filters;
149+
}, [
150+
resourceType,
151+
isTektonResultEnabled,
152+
t,
153+
defaultStatusValues,
154+
defaultDataSourceValues,
155+
]);
156+
157+
const config = resourceType
158+
? RESOURCE_FILTER_CONFIG[resourceType]
159+
: undefined;
160+
161+
const matchesCheckboxFilter = useCallback(
162+
(obj: T, filterId: string, selectedValues: string[]) => {
163+
if (filterId === 'status' && config) {
164+
return config.statusFilter(
165+
{ selected: selectedValues, all: allStatusIds },
166+
obj,
167+
);
168+
}
169+
if (filterId === 'dataSource') {
170+
return pipelineRunDataSourceFilter({ selected: selectedValues }, obj);
171+
}
172+
return true;
173+
},
174+
[allStatusIds, config],
175+
);
176+
177+
const getCheckboxFilterValue = useCallback(
178+
(obj: T, filterId: string): string | undefined => {
179+
if (filterId === 'status' && config) return config.statusReducer(obj);
180+
if (filterId === 'dataSource') {
181+
return isPipelineRunLoadedFromTektonResults(obj)
182+
? 'archived-data'
183+
: 'cluster-data';
184+
}
185+
return undefined;
186+
},
187+
[config],
188+
);
189+
41190
const initialValues = useMemo(() => {
42191
const values: FilterValues = { name: '', labels: [] };
43192
checkboxFilters.forEach((f) => {
44-
values[f.id] = [];
193+
values[f.id] = f.defaultValues ?? [];
45194
});
46195
return values;
47196
}, [checkboxFilters]);
48197

49198
const [filterValues, setFilterValues] = useState<FilterValues>(initialValues);
199+
const [, setSearchParams] = useSearchParams();
200+
201+
const resetPage = useCallback(() => {
202+
setSearchParams((prev) => {
203+
const next = new URLSearchParams(prev);
204+
next.set('page', '1');
205+
return next;
206+
});
207+
}, [setSearchParams]);
50208

51209
const onFilterChange = useCallback(
52210
(key: string, value: string | string[]) => {
53211
setFilterValues((prev) => ({ ...prev, [key]: value }));
212+
resetPage();
54213
},
55-
[],
214+
[resetPage],
56215
);
57216

58217
const onClearAll = useCallback(() => {
59-
setFilterValues(initialValues);
60-
}, [initialValues]);
218+
setFilterValues(resetFilterState);
219+
resetPage();
220+
}, [resetPage]);
61221

62-
const filteredData = useMemo(() => {
63-
if (!data) return [];
64-
return data.filter((obj) => {
222+
const passesNameAndLabelFilters = useCallback(
223+
(obj: T): boolean => {
65224
const name = getName(obj);
66225
if (
67226
filterValues.name &&
68227
!name?.toLowerCase().includes(filterValues.name.toLowerCase())
69228
) {
70229
return false;
71230
}
72-
73231
const labelFilters = filterValues.labels || [];
74232
if (labelFilters.length > 0 && getLabels) {
75233
if (!matchesLabels(getLabels(obj), labelFilters)) {
76234
return false;
77235
}
78236
}
237+
return true;
238+
},
239+
[filterValues.name, filterValues.labels, getName, getLabels],
240+
);
79241

80-
if (matchesCheckboxFilter) {
81-
for (const filter of checkboxFilters) {
82-
const selected = (filterValues[filter.id] as string[]) || [];
83-
if (
84-
selected.length > 0 &&
85-
!matchesCheckboxFilter(obj, filter.id, selected)
86-
) {
87-
return false;
88-
}
242+
const passesCheckboxFilter = useCallback(
243+
(obj: T, excludeFilterId?: string): boolean => {
244+
if (!resourceType) return true;
245+
for (const filter of checkboxFilters) {
246+
if (filter.id === excludeFilterId) continue;
247+
const selected = (filterValues[filter.id] as string[]) || [];
248+
if (
249+
selected.length > 0 &&
250+
!matchesCheckboxFilter(obj, filter.id, selected)
251+
) {
252+
return false;
89253
}
90254
}
91-
92255
return true;
93-
});
256+
},
257+
[resourceType, checkboxFilters, filterValues, matchesCheckboxFilter],
258+
);
259+
260+
const filteredData = useMemo(() => {
261+
if (!data) return [];
262+
return data.filter(
263+
(obj) => passesNameAndLabelFilters(obj) && passesCheckboxFilter(obj),
264+
);
265+
}, [data, passesNameAndLabelFilters, passesCheckboxFilter]);
266+
267+
const updatedCheckboxFilters = useMemo(() => {
268+
if (!resourceType || !data) return checkboxFilters;
269+
return checkboxFilters.map((filter) => ({
270+
...filter,
271+
options: filter.options.map((opt) => {
272+
let count = 0;
273+
data.forEach((obj) => {
274+
if (
275+
passesNameAndLabelFilters(obj) &&
276+
passesCheckboxFilter(obj, filter.id) &&
277+
getCheckboxFilterValue(obj, filter.id) === opt.value
278+
) {
279+
count++;
280+
}
281+
});
282+
return { ...opt, count };
283+
}),
284+
}));
94285
}, [
95-
data,
96-
filterValues,
97-
getName,
98-
getLabels,
286+
resourceType,
99287
checkboxFilters,
100-
matchesCheckboxFilter,
288+
data,
289+
getCheckboxFilterValue,
290+
passesNameAndLabelFilters,
291+
passesCheckboxFilter,
101292
]);
102293

103-
return { filterValues, onFilterChange, onClearAll, filteredData };
294+
return {
295+
filterValues,
296+
onFilterChange,
297+
onClearAll,
298+
filteredData,
299+
updatedCheckboxFilters,
300+
};
104301
};

0 commit comments

Comments
 (0)