Skip to content

Commit c94d40e

Browse files
committed
add managed columns for AppProjects
Signed-off-by: Atif Ali <atali@redhat.com>
1 parent 6d86d5c commit c94d40e

3 files changed

Lines changed: 207 additions & 103 deletions

File tree

‎src/gitops/components/project/ProjectList.tsx‎

Lines changed: 116 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { ResourceLink } from '@openshift-console/dynamic-plugin-sdk';
2020
import { ErrorState } from '@patternfly/react-component-groups';
2121
import { EmptyState, EmptyStateBody } from '@patternfly/react-core';
22+
import type { DataViewTd } from '@patternfly/react-data-view/dist/esm/DataViewTable';
2223
import { DataViewTh, DataViewTr } from '@patternfly/react-data-view/dist/esm/DataViewTable';
2324
import { CubesIcon } from '@patternfly/react-icons';
2425
import { ThProps } from '@patternfly/react-table';
@@ -30,13 +31,27 @@ import {
3031
ShowOperandsInAllNamespacesRadioGroup,
3132
useShowOperandsInAllNamespaces,
3233
} from '../shared/AllNamespaces';
34+
import {
35+
type GitOpsManagedColumn,
36+
NAMESPACE_COLUMN_ID,
37+
useGitOpsColumnManagement,
38+
} from '../shared/ColumnManagement';
3339
import {
3440
GitOpsDataViewTable,
3541
useGitOpsDataViewSort,
3642
useGitOpsListPagePagination,
3743
} from '../shared/DataView';
38-
import { filterByConsoleNameAndLabels, getLabelsSortKey, parseLabelFilterParam } from '../shared/listPageTextFilters';
44+
import { GitOpsListPageToolbar } from '../shared/GitOpsListPageToolbar';
45+
import {
46+
filterByConsoleNameAndLabels,
47+
getLabelsSortKey,
48+
parseLabelFilterParam,
49+
} from '../shared/listPageTextFilters';
3950
import { MetadataLabels } from '../shared/MetadataLabels/MetadataLabels';
51+
import {
52+
getProjectManagedColumns,
53+
PROJECT_LIST_COLUMN_MANAGEMENT_ID,
54+
} from '../shared/projectListColumns';
4055

4156
import { useProjectActionsProvider } from './hooks/useProjectActionsProvider';
4257

@@ -83,17 +98,23 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
8398
namespace,
8499
});
85100

86-
const columnSortConfig = React.useMemo(() => {
87-
return [
88-
'name',
89-
...(!listAllNamespaces || !namespace || namespace === '' ? ['namespace'] : []),
90-
'description',
91-
'applications',
92-
'labels',
93-
'last-updated',
94-
'actions',
95-
].map((key) => ({ key }));
96-
}, [listAllNamespaces, namespace]);
101+
const { t } = useTranslation('plugin__gitops-plugin');
102+
const includeNamespaceColumn = !namespace || namespace === '';
103+
const managedColumns = React.useMemo(() => getProjectManagedColumns(true, t), [t]);
104+
105+
const { activeColumnIds, columnManagement, filterDataView } = useGitOpsColumnManagement({
106+
columnManagementID: PROJECT_LIST_COLUMN_MANAGEMENT_ID,
107+
columns: managedColumns,
108+
resourceType: t('AppProjects'),
109+
includeNamespaceColumn,
110+
showNamespaceHelp: includeNamespaceColumn,
111+
});
112+
113+
const columnSortConfig = React.useMemo(
114+
() =>
115+
managedColumns.filter((column) => !column.alwaysShown).map((column) => ({ key: column.id })),
116+
[managedColumns],
117+
);
97118

98119
const { searchParams, sortBy, direction, getSortParams } =
99120
useGitOpsDataViewSort(columnSortConfig);
@@ -103,9 +124,7 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
103124
const nameQuery = searchParams.get('name') || '';
104125
const labelsParam = searchParams.get('labels') || '';
105126

106-
const { t } = useTranslation('plugin__gitops-plugin');
107-
108-
const columnsDV = useColumnsDV(namespace, getSortParams);
127+
const columnsDV = useColumnsDV(managedColumns, getSortParams, columnSortConfig);
109128
const sortedProjects = React.useMemo(() => {
110129
return sortData(appProjects as AppProjectKind[], sortBy, direction, applications, appsLoaded);
111130
}, [appProjects, sortBy, direction, applications, appsLoaded]);
@@ -149,8 +168,15 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
149168
namespace,
150169
searchParams,
151170
});
152-
const rows = useProjectsRowsDV(pagedItems, namespace, applications, appsLoaded);
153-
const showNamespaceColumn = !namespace || namespace === '';
171+
const rows = useProjectsRowsDV(pagedItems, managedColumns, applications, appsLoaded);
172+
const columnIds = React.useMemo(
173+
() => managedColumns.map((column) => column.id),
174+
[managedColumns],
175+
);
176+
const { columns: visibleColumnsDV, rows: visibleRows } = React.useMemo(
177+
() => filterDataView(columnsDV, rows, columnIds, activeColumnIds),
178+
[filterDataView, columnsDV, rows, columnIds, activeColumnIds],
179+
);
154180

155181
// Check if there are projects initially (before search)
156182
const hasProjects = React.useMemo(() => {
@@ -176,7 +202,7 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
176202
const empty = (
177203
<Tbody>
178204
<Tr key="loading" ouiaId="table-tr-loading">
179-
<Td colSpan={columnsDV.length}>
205+
<Td colSpan={visibleColumnsDV.length || columnsDV.length}>
180206
<EmptyState
181207
headingLevel="h4"
182208
icon={CubesIcon}
@@ -193,7 +219,7 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
193219
const error = loadError && (
194220
<Tbody>
195221
<Tr key="loading" ouiaId={'table-tr-loading'}>
196-
<Td colSpan={columnsDV.length}>
222+
<Td colSpan={visibleColumnsDV.length || columnsDV.length}>
197223
<ErrorState
198224
titleText={t('Unable to load data')}
199225
bodyText={t(
@@ -204,7 +230,17 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
204230
</Tr>
205231
</Tbody>
206232
);
207-
const isEmptyState = !loadError && rows.length === 0;
233+
const isEmptyState = !loadError && visibleRows.length === 0;
234+
235+
const listPageFilter = !hideNameLabelFilters && hasProjects && (
236+
<ListPageFilter
237+
data={data}
238+
loaded={loaded}
239+
rowFilters={filters}
240+
onFilterChange={onFilterChange}
241+
nameFilterPlaceholder={t('Search by name...')}
242+
/>
243+
);
208244

209245
return (
210246
<div>
@@ -224,25 +260,19 @@ const ProjectList: React.FC<ProjectListTabProps> = ({
224260
</ListPageHeader>
225261
)}
226262
<ListPageBody>
227-
{!hideNameLabelFilters && hasProjects && (
228-
<ListPageFilter
229-
data={data}
230-
loaded={loaded}
231-
rowFilters={filters}
232-
onFilterChange={onFilterChange}
233-
nameFilterPlaceholder={t('Search by name...')}
234-
/>
263+
{hasProjects && (
264+
<GitOpsListPageToolbar filters={listPageFilter} columnManagement={columnManagement} />
235265
)}
236266
<div
237267
className={
238-
showNamespaceColumn
268+
includeNamespaceColumn
239269
? 'gitops-project-list gitops-project-list--with-namespace'
240270
: 'gitops-project-list'
241271
}
242272
>
243273
<GitOpsDataViewTable
244-
columns={columnsDV}
245-
rows={rows}
274+
columns={visibleColumnsDV}
275+
rows={visibleRows}
246276
isEmpty={isEmptyState}
247277
emptyState={empty}
248278
isError={!!loadError}
@@ -390,82 +420,67 @@ export const sortData = (
390420
});
391421
};
392422

393-
const sortableHeaderProps = (
394-
ariaLabel: string,
395-
className: string,
396-
sort: ThProps['sort'],
397-
): ThProps => ({
398-
'aria-label': ariaLabel,
399-
className,
400-
sort,
401-
tooltip: '',
402-
});
403-
404423
export const useColumnsDV = (
405-
namespace: string | undefined,
424+
managedColumns: GitOpsManagedColumn[],
406425
getSortParams: (columnIndex: number) => ThProps['sort'],
426+
columnSortConfig: { key: string }[],
407427
): DataViewTh[] => {
408-
const showNamespace = !namespace || namespace === '';
409-
const i: number = showNamespace ? 1 : 0;
410428
const { t } = useTranslation('plugin__gitops-plugin');
411-
const columns: DataViewTh[] = [
412-
{
413-
cell: t('Name'),
414-
props: sortableHeaderProps('name', 'pf-m-width-20', getSortParams(0)),
415-
},
416-
...(showNamespace
417-
? [
418-
{
419-
cell: t('Namespace'),
420-
props: sortableHeaderProps('namespace', 'pf-m-width-15', getSortParams(1)),
421-
},
422-
]
423-
: []),
424-
{
425-
cell: t('Description'),
426-
props: sortableHeaderProps('description', 'pf-m-width-10', getSortParams(1 + i)),
427-
},
428-
{
429-
cell: t('Applications'),
430-
props: sortableHeaderProps('applications', 'pf-m-width-15', getSortParams(2 + i)),
431-
},
432-
{
433-
cell: t('Labels'),
434-
props: sortableHeaderProps('labels', 'pf-m-width-20', getSortParams(3 + i)),
435-
},
436-
{
437-
cell: t('Last Updated'),
438-
props: sortableHeaderProps(
439-
'last updated',
440-
'pf-m-width-15',
441-
getSortParams(showNamespace ? 5 : 4),
442-
),
443-
},
444-
{
445-
cell: '',
446-
props: { 'aria-label': 'actions' },
447-
},
448-
];
449429

450-
return columns;
430+
const titleById: Record<string, string> = {
431+
name: t('Name'),
432+
[NAMESPACE_COLUMN_ID]: t('Namespace'),
433+
description: t('Description'),
434+
applications: t('Applications'),
435+
labels: t('Labels'),
436+
'last-updated': t('Last Updated'),
437+
actions: '',
438+
};
439+
440+
const widthById: Record<string, string> = {
441+
name: 'pf-m-width-20',
442+
[NAMESPACE_COLUMN_ID]: 'pf-m-width-15',
443+
description: 'pf-m-width-10',
444+
applications: 'pf-m-width-15',
445+
labels: 'pf-m-width-20',
446+
'last-updated': 'pf-m-width-15',
447+
};
448+
449+
return managedColumns.map((column) => {
450+
if (column.id === 'actions') {
451+
return {
452+
cell: '',
453+
props: { 'aria-label': 'actions' },
454+
};
455+
}
456+
const sortIndex = columnSortConfig.findIndex((entry) => entry.key === column.id);
457+
return {
458+
cell: titleById[column.id] ?? column.title,
459+
props: {
460+
'aria-label': column.id,
461+
className: widthById[column.id],
462+
tooltip: '',
463+
...(sortIndex >= 0 ? { sort: getSortParams(sortIndex) } : {}),
464+
},
465+
};
466+
});
451467
};
452468

453469
export const useProjectsRowsDV = (
454470
projectsList: AppProjectKind[],
455-
namespace: string | undefined,
471+
managedColumns: GitOpsManagedColumn[],
456472
applications: ApplicationKind[],
457473
appsLoaded: boolean,
458474
): DataViewTr[] => {
459475
const rows: DataViewTr[] = [];
460476
if (projectsList == undefined || projectsList.length == 0) {
461477
return rows;
462478
}
463-
const showNamespace = !namespace || namespace === '';
464479
projectsList.forEach((obj, index) => {
465480
const appsCount = getApplicationsCount(obj, applications, appsLoaded);
466481

467-
rows.push([
468-
{
482+
const cellsById: Record<string, DataViewTd> = {
483+
name: {
469484
cell: (
470485
<ResourceLink
471486
groupVersionKind={modelToGroupVersionKind(AppProjectModel)}
@@ -477,23 +492,19 @@ export const useProjectsRowsDV = (
477492
id: 'name',
478493
dataLabel: 'Name',
479494
},
480-
...(showNamespace
481-
? [
482-
{
483-
cell: <ResourceLink kind="Namespace" name={obj.metadata.namespace} />,
484-
id: obj.metadata.namespace,
485-
dataLabel: 'Namespace',
486-
},
487-
]
488-
: []),
489-
{
495+
[NAMESPACE_COLUMN_ID]: {
496+
cell: <ResourceLink kind="Namespace" name={obj.metadata.namespace} />,
497+
id: obj.metadata.namespace,
498+
dataLabel: 'Namespace',
499+
},
500+
description: {
490501
id: 'description',
491502
dataLabel: 'Description',
492503
cell: (
493504
<span className="gitops-project-list__description">{obj.spec?.description || '-'}</span>
494505
),
495506
},
496-
{
507+
applications: {
497508
id: 'applications',
498509
dataLabel: 'Applications',
499510
cell: (
@@ -502,7 +513,7 @@ export const useProjectsRowsDV = (
502513
</span>
503514
),
504515
},
505-
{
516+
labels: {
506517
id: 'labels',
507518
dataLabel: 'Labels',
508519
cell: (
@@ -521,7 +532,7 @@ export const useProjectsRowsDV = (
521532
</div>
522533
),
523534
},
524-
{
535+
'last-updated': {
525536
id: 'last-updated',
526537
cell: (() => {
527538
const lastUpdate = getLastUpdateTimestamp(obj);
@@ -532,12 +543,14 @@ export const useProjectsRowsDV = (
532543
);
533544
})(),
534545
},
535-
{
546+
actions: {
536547
id: 'actions-' + index,
537548
cell: <ProjectActionsCell project={obj} index={index} />,
538549
props: { style: { paddingTop: 8, paddingRight: 0, paddingLeft: 0, width: 10 } },
539550
},
540-
]);
551+
};
552+
553+
rows.push(managedColumns.map((column) => cellsById[column.id]));
541554
});
542555
return rows;
543556
};

0 commit comments

Comments
 (0)