11import 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' ;
44import { DataViewCheckboxFilter } from '@patternfly/react-data-view' ;
55import type { DataViewFilterOption } from '@patternfly/react-data-view/dist/esm/DataViewFilters' ;
66import * as _ from 'lodash' ;
@@ -96,7 +96,7 @@ import { nodeStatus } from '../../status';
9696import { getNodeClientCSRs , isCSRResource } from './csr' ;
9797import GroupsEditorModal from './modals/GroupsEditorModal' ;
9898import NodeUptime from './node-dashboard/NodeUptime' ;
99- import { getNodeGroups } from './NodeGroupUtils' ;
99+ import { getExistingGroups , getNodeGroups } from './NodeGroupUtils' ;
100100import NodeRoles from './NodeRoles' ;
101101import { NodeStatusWithExtensions } from './NodeStatus' ;
102102import {
@@ -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,13 @@ const NodeList: FC<NodeListProps> = ({
757766 placeholder = { t ( 'console-app~Filter by roles' ) }
758767 options = { nodeRoleFilterOptions }
759768 /> ,
769+ < DataViewCheckboxFilter
770+ key = "groups"
771+ filterId = "groups"
772+ title = { t ( 'console-app~Groups' ) }
773+ placeholder = { t ( 'console-app~Filter by groups' ) }
774+ options = { nodeGroupFilterOptions }
775+ /> ,
760776 < DataViewCheckboxFilter
761777 key = "architecture"
762778 filterId = "architecture"
@@ -783,6 +799,7 @@ const NodeList: FC<NodeListProps> = ({
783799 t ,
784800 nodeStatusFilterOptions ,
785801 nodeRoleFilterOptions ,
802+ nodeGroupFilterOptions ,
786803 nodeArchitectureFilterOptions ,
787804 machineSetFilterOptions ,
788805 machineConfigPoolFilterOptions ,
@@ -811,6 +828,17 @@ const NodeList: FC<NodeListProps> = ({
811828 }
812829 }
813830
831+ // Groups filter
832+ if ( filters . groups . length > 0 ) {
833+ if ( isCSR ) {
834+ return false ;
835+ }
836+ const nodeGroups = getNodeGroups ( resource as NodeKind ) ;
837+ if ( ! filters . groups . some ( ( r ) => nodeGroups . includes ( r ) ) ) {
838+ return false ;
839+ }
840+ }
841+
814842 // Architecture filter
815843 if ( filters . architecture . length > 0 ) {
816844 if ( isCSR ) {
@@ -881,6 +909,7 @@ type NodeRowItem = (NodeKind | NodeCertificateSigningRequestKind) & {
881909type NodeFilters = ResourceFilters & {
882910 status : string [ ] ;
883911 roles : string [ ] ;
912+ groups : string [ ] ;
884913 architecture : string [ ] ;
885914 machineOwners : string [ ] ;
886915 machineConfigPools : string [ ] ;
@@ -911,6 +940,7 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
911940 const { t } = useTranslation ( ) ;
912941 const launchOverlay = useOverlay ( ) ;
913942 const nodeMgmtV1Enabled = useFlag ( FLAG_NODE_MGMT_V1 ) ;
943+ const editGroupButtonRef = useRef < HTMLDivElement > ( null ) ;
914944
915945 const [ selectedColumns , , columnPreferenceLoaded ] = useUserPreference < TableColumnsType > (
916946 COLUMN_MANAGEMENT_USER_PREFERENCE_KEY ,
@@ -1047,13 +1077,22 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
10471077 return (
10481078 < >
10491079 < 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 >
1080+ { nodeMgmtV1Enabled ? (
1081+ < >
1082+ < span ref = { ! isEditLoading && ! canEdit ? editGroupButtonRef : undefined } >
1083+ < Button
1084+ variant = { ButtonVariant . secondary }
1085+ onClick = { ( ) => launchOverlay ( GroupsEditorModal , { } ) }
1086+ isDisabled = { isEditLoading || ! canEdit }
1087+ >
1088+ { t ( 'console-app~Edit groups' ) }
1089+ </ Button >
1090+ </ span >
1091+ < Tooltip
1092+ triggerRef = { editGroupButtonRef }
1093+ content = { t ( 'console-app~You do not have permission to edit groups.' ) }
1094+ />
1095+ </ >
10571096 ) : null }
10581097 </ ListPageHeader >
10591098 < ListPageBody >
0 commit comments