Skip to content

Commit f34d07d

Browse files
CONSOLE-5158: Add filter by group for nodes
1 parent 89b5685 commit f34d07d

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-15
lines changed

frontend/packages/console-app/locales/en/console-app.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@
389389
"Node addresses": "Node addresses",
390390
"Uptime": "Uptime",
391391
"Edit": "Edit",
392+
"You do not have permission to edit groups.": "You do not have permission to edit groups.",
392393
"Inventory": "Inventory",
393394
"Images": "Images",
394395
"Image": "Image",
@@ -449,6 +450,7 @@
449450
"worker": "worker",
450451
"Filter by status": "Filter by status",
451452
"Filter by roles": "Filter by roles",
453+
"Filter by groups": "Filter by groups",
452454
"Filter by architecture": "Filter by architecture",
453455
"Filter by machine set": "Filter by machine set",
454456
"Filter by MachineConfigPool": "Filter by MachineConfigPool",

frontend/packages/console-app/src/components/nodes/NodesPage.tsx

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { FC } from 'react';
2-
import { useMemo, useCallback, useEffect, Suspense } from 'react';
3-
import { Button, ButtonVariant } from '@patternfly/react-core';
2+
import { useRef, useMemo, useCallback, useEffect, Suspense } from 'react';
3+
import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core';
44
import { DataViewCheckboxFilter } from '@patternfly/react-data-view';
55
import type { DataViewFilterOption } from '@patternfly/react-data-view/dist/esm/DataViewFilters';
66
import * as _ from 'lodash';
@@ -96,7 +96,7 @@ import { nodeStatus } from '../../status';
9696
import { getNodeClientCSRs, isCSRResource } from './csr';
9797
import GroupsEditorModal from './modals/GroupsEditorModal';
9898
import NodeUptime from './node-dashboard/NodeUptime';
99-
import { getNodeGroups } from './NodeGroupUtils';
99+
import { getExistingGroups, getNodeGroups } from './NodeGroupUtils';
100100
import NodeRoles from './NodeRoles';
101101
import { NodeStatusWithExtensions } from './NodeStatus';
102102
import {
@@ -702,6 +702,14 @@ const NodeList: FC<NodeListProps> = ({
702702
[],
703703
);
704704

705+
const nodeGroupFilterOptions = useMemo<DataViewFilterOption[]>(() => {
706+
const groupNames = getExistingGroups(data as NodeKind[]);
707+
return groupNames.map((groupName) => ({
708+
value: groupName,
709+
label: groupName,
710+
}));
711+
}, [data]);
712+
705713
const machineSetFilterOptions = useMemo<DataViewFilterOption[]>(
706714
() =>
707715
[
@@ -733,6 +741,7 @@ const NodeList: FC<NodeListProps> = ({
733741
...initialFiltersDefault,
734742
status: [],
735743
roles: [],
744+
groups: [],
736745
architecture: [],
737746
machineOwners: [],
738747
machineConfigPools: [],
@@ -757,6 +766,17 @@ const NodeList: FC<NodeListProps> = ({
757766
placeholder={t('console-app~Filter by roles')}
758767
options={nodeRoleFilterOptions}
759768
/>,
769+
...(nodeMgmtV1Enabled
770+
? [
771+
<DataViewCheckboxFilter
772+
key="groups"
773+
filterId="groups"
774+
title={t('console-app~Groups')}
775+
placeholder={t('console-app~Filter by groups')}
776+
options={nodeGroupFilterOptions}
777+
/>,
778+
]
779+
: []),
760780
<DataViewCheckboxFilter
761781
key="architecture"
762782
filterId="architecture"
@@ -783,9 +803,11 @@ const NodeList: FC<NodeListProps> = ({
783803
t,
784804
nodeStatusFilterOptions,
785805
nodeRoleFilterOptions,
806+
nodeGroupFilterOptions,
786807
nodeArchitectureFilterOptions,
787808
machineSetFilterOptions,
788809
machineConfigPoolFilterOptions,
810+
nodeMgmtV1Enabled,
789811
],
790812
);
791813

@@ -811,6 +833,17 @@ const NodeList: FC<NodeListProps> = ({
811833
}
812834
}
813835

836+
// Groups filter
837+
if (filters.groups.length > 0) {
838+
if (isCSR) {
839+
return false;
840+
}
841+
const nodeGroups = getNodeGroups(resource as NodeKind);
842+
if (!filters.groups.some((r) => nodeGroups.includes(r))) {
843+
return false;
844+
}
845+
}
846+
814847
// Architecture filter
815848
if (filters.architecture.length > 0) {
816849
if (isCSR) {
@@ -881,6 +914,7 @@ type NodeRowItem = (NodeKind | NodeCertificateSigningRequestKind) & {
881914
type NodeFilters = ResourceFilters & {
882915
status: string[];
883916
roles: string[];
917+
groups: string[];
884918
architecture: string[];
885919
machineOwners: string[];
886920
machineConfigPools: string[];
@@ -911,6 +945,7 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
911945
const { t } = useTranslation();
912946
const launchOverlay = useOverlay();
913947
const nodeMgmtV1Enabled = useFlag(FLAG_NODE_MGMT_V1);
948+
const editGroupButtonRef = useRef<HTMLDivElement>(null);
914949

915950
const [selectedColumns, , columnPreferenceLoaded] = useUserPreference<TableColumnsType>(
916951
COLUMN_MANAGEMENT_USER_PREFERENCE_KEY,
@@ -1047,13 +1082,22 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
10471082
return (
10481083
<>
10491084
<ListPageHeader title={t('public~Nodes')}>
1050-
{nodeMgmtV1Enabled && !isEditLoading && canEdit ? (
1051-
<Button
1052-
variant={ButtonVariant.secondary}
1053-
onClick={() => launchOverlay(GroupsEditorModal, {})}
1054-
>
1055-
{t('console-app~Edit groups')}
1056-
</Button>
1085+
{nodeMgmtV1Enabled ? (
1086+
<>
1087+
<span ref={!isEditLoading && !canEdit ? editGroupButtonRef : undefined}>
1088+
<Button
1089+
variant={ButtonVariant.secondary}
1090+
onClick={() => launchOverlay(GroupsEditorModal, {})}
1091+
isDisabled={isEditLoading || !canEdit}
1092+
>
1093+
{t('console-app~Edit groups')}
1094+
</Button>
1095+
</span>
1096+
<Tooltip
1097+
triggerRef={editGroupButtonRef}
1098+
content={t('console-app~You do not have permission to edit groups.')}
1099+
/>
1100+
</>
10571101
) : null}
10581102
</ListPageHeader>
10591103
<ListPageBody>

frontend/packages/console-app/src/components/nodes/node-dashboard/DetailsCard.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { FC } from 'react';
2-
import { useContext } from 'react';
2+
import { useRef, useContext } from 'react';
33
import {
44
Button,
55
ButtonVariant,
@@ -14,6 +14,7 @@ import {
1414
DividerVariant,
1515
Flex,
1616
FlexItem,
17+
Tooltip,
1718
} from '@patternfly/react-core';
1819
import { useTranslation } from 'react-i18next';
1920
import { Link } from 'react-router';
@@ -41,6 +42,7 @@ const DetailsCard: FC = () => {
4142
const { t } = useTranslation();
4243
const launchOverlay = useOverlay();
4344
const nodeMgmtV1Enabled = useFlag(FLAG_NODE_MGMT_V1);
45+
const editGroupButtonRef = useRef<HTMLDivElement>(null);
4446

4547
const [canEdit, isEditLoading] = useAccessReview({
4648
group: NodeModel.apiGroup || '',
@@ -99,17 +101,22 @@ const DetailsCard: FC = () => {
99101
<span className="pf-v6-c-description-list__text pf-v6-u-w-100">
100102
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
101103
<FlexItem>{t('console-app~Groups')}</FlexItem>
102-
{!isEditLoading && canEdit ? (
103-
<FlexItem>
104+
<FlexItem>
105+
<div ref={!isEditLoading && !canEdit ? editGroupButtonRef : undefined}>
104106
<Button
105107
variant={ButtonVariant.link}
106108
isInline
109+
isDisabled={isEditLoading || !canEdit}
107110
onClick={() => launchOverlay(NodeGroupsEditorModal, { node: obj })}
108111
>
109112
{t('console-app~Edit')}
110113
</Button>
111-
</FlexItem>
112-
) : null}
114+
</div>
115+
<Tooltip
116+
triggerRef={editGroupButtonRef}
117+
content={t('console-app~You do not have permission to edit groups.')}
118+
/>
119+
</FlexItem>
113120
</Flex>
114121
</span>
115122
</dt>

0 commit comments

Comments
 (0)