Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/packages/console-app/locales/en/console-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"Node addresses": "Node addresses",
"Uptime": "Uptime",
"Edit": "Edit",
"You do not have permission to edit groups.": "You do not have permission to edit groups.",
"Inventory": "Inventory",
"Images": "Images",
"Image": "Image",
Expand Down Expand Up @@ -449,6 +450,7 @@
"worker": "worker",
"Filter by status": "Filter by status",
"Filter by roles": "Filter by roles",
"Filter by groups": "Filter by groups",
"Filter by architecture": "Filter by architecture",
"Filter by machine set": "Filter by machine set",
"Filter by MachineConfigPool": "Filter by MachineConfigPool",
Expand Down
64 changes: 54 additions & 10 deletions frontend/packages/console-app/src/components/nodes/NodesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FC } from 'react';
import { useMemo, useCallback, useEffect, Suspense } from 'react';
import { Button, ButtonVariant } from '@patternfly/react-core';
import { useRef, useMemo, useCallback, useEffect, Suspense } from 'react';
import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core';
import { DataViewCheckboxFilter } from '@patternfly/react-data-view';
import type { DataViewFilterOption } from '@patternfly/react-data-view/dist/esm/DataViewFilters';
import * as _ from 'lodash';
Expand Down Expand Up @@ -96,7 +96,7 @@ import { nodeStatus } from '../../status';
import { getNodeClientCSRs, isCSRResource } from './csr';
import GroupsEditorModal from './modals/GroupsEditorModal';
import NodeUptime from './node-dashboard/NodeUptime';
import { getNodeGroups } from './NodeGroupUtils';
import { getExistingGroups, getNodeGroups } from './NodeGroupUtils';
import NodeRoles from './NodeRoles';
import { NodeStatusWithExtensions } from './NodeStatus';
import {
Expand Down Expand Up @@ -702,6 +702,14 @@ const NodeList: FC<NodeListProps> = ({
[],
);

const nodeGroupFilterOptions = useMemo<DataViewFilterOption[]>(() => {
const groupNames = getExistingGroups(data as NodeKind[]);
return groupNames.map((groupName) => ({
value: groupName,
label: groupName,
}));
}, [data]);

const machineSetFilterOptions = useMemo<DataViewFilterOption[]>(
() =>
[
Expand Down Expand Up @@ -733,6 +741,7 @@ const NodeList: FC<NodeListProps> = ({
...initialFiltersDefault,
status: [],
roles: [],
groups: [],
architecture: [],
machineOwners: [],
machineConfigPools: [],
Expand All @@ -757,6 +766,17 @@ const NodeList: FC<NodeListProps> = ({
placeholder={t('console-app~Filter by roles')}
options={nodeRoleFilterOptions}
/>,
...(nodeMgmtV1Enabled
? [
<DataViewCheckboxFilter
key="groups"
filterId="groups"
title={t('console-app~Groups')}
placeholder={t('console-app~Filter by groups')}
options={nodeGroupFilterOptions}
/>,
]
: []),
<DataViewCheckboxFilter
key="architecture"
filterId="architecture"
Expand All @@ -783,9 +803,11 @@ const NodeList: FC<NodeListProps> = ({
t,
nodeStatusFilterOptions,
nodeRoleFilterOptions,
nodeGroupFilterOptions,
nodeArchitectureFilterOptions,
machineSetFilterOptions,
machineConfigPoolFilterOptions,
nodeMgmtV1Enabled,
],
);

Expand All @@ -811,6 +833,17 @@ const NodeList: FC<NodeListProps> = ({
}
}

// Groups filter
if (filters.groups.length > 0) {
if (isCSR) {
return false;
}
const nodeGroups = getNodeGroups(resource as NodeKind);
if (!filters.groups.some((r) => nodeGroups.includes(r))) {
return false;
}
}

// Architecture filter
if (filters.architecture.length > 0) {
if (isCSR) {
Expand Down Expand Up @@ -881,6 +914,7 @@ type NodeRowItem = (NodeKind | NodeCertificateSigningRequestKind) & {
type NodeFilters = ResourceFilters & {
status: string[];
roles: string[];
groups: string[];
architecture: string[];
machineOwners: string[];
machineConfigPools: string[];
Expand Down Expand Up @@ -911,6 +945,7 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
const { t } = useTranslation();
const launchOverlay = useOverlay();
const nodeMgmtV1Enabled = useFlag(FLAG_NODE_MGMT_V1);
const editGroupButtonRef = useRef<HTMLDivElement>(null);

const [selectedColumns, , columnPreferenceLoaded] = useUserPreference<TableColumnsType>(
COLUMN_MANAGEMENT_USER_PREFERENCE_KEY,
Expand Down Expand Up @@ -1047,13 +1082,22 @@ export const NodesPage: FC<NodesPageProps> = ({ selector }) => {
return (
<>
<ListPageHeader title={t('public~Nodes')}>
{nodeMgmtV1Enabled && !isEditLoading && canEdit ? (
<Button
variant={ButtonVariant.secondary}
onClick={() => launchOverlay(GroupsEditorModal, {})}
>
{t('console-app~Edit groups')}
</Button>
{nodeMgmtV1Enabled ? (
<>
<span ref={!isEditLoading && !canEdit ? editGroupButtonRef : undefined}>
<Button
variant={ButtonVariant.secondary}
onClick={() => launchOverlay(GroupsEditorModal, {})}
isDisabled={isEditLoading || !canEdit}
>
{t('console-app~Edit groups')}
</Button>
</span>
<Tooltip
triggerRef={editGroupButtonRef}
content={t('console-app~You do not have permission to edit groups.')}
/>
</>
) : null}
</ListPageHeader>
<ListPageBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FC } from 'react';
import { useContext } from 'react';
import { useRef, useContext } from 'react';
import {
Button,
ButtonVariant,
Expand All @@ -8,12 +8,11 @@ import {
CardHeader,
CardTitle,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
Divider,
DividerVariant,
Flex,
FlexItem,
Tooltip,
} from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router';
Expand Down Expand Up @@ -41,6 +40,7 @@ const DetailsCard: FC = () => {
const { t } = useTranslation();
const launchOverlay = useOverlay();
const nodeMgmtV1Enabled = useFlag(FLAG_NODE_MGMT_V1);
const editGroupButtonRef = useRef<HTMLDivElement>(null);

const [canEdit, isEditLoading] = useAccessReview({
group: NodeModel.apiGroup || '',
Expand Down Expand Up @@ -94,29 +94,29 @@ const DetailsCard: FC = () => {
{nodeMgmtV1Enabled ? (
<>
<Divider component={DividerVariant.div} className="pf-v6-u-w-75" />
<DescriptionListGroup>
<dt className="pf-v6-c-description-list__term" data-test="detail-item-title">
<span className="pf-v6-c-description-list__text pf-v6-u-w-100">
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<FlexItem>{t('console-app~Groups')}</FlexItem>
{!isEditLoading && canEdit ? (
<FlexItem>
<Button
variant={ButtonVariant.link}
isInline
onClick={() => launchOverlay(NodeGroupsEditorModal, { node: obj })}
>
{t('console-app~Edit')}
</Button>
</FlexItem>
) : null}
</Flex>
</span>
</dt>
<DescriptionListDescription data-test="detail-item-value">
{getNodeGroups(obj).sort().join(', ') || DASH}
</DescriptionListDescription>
</DescriptionListGroup>
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<FlexItem>
<OverviewDetailItem isLoading={!obj} title={t('console-app~Groups')}>
{getNodeGroups(obj).sort().join(', ') || DASH}
</OverviewDetailItem>
</FlexItem>
<FlexItem>
<div ref={!isEditLoading && !canEdit ? editGroupButtonRef : undefined}>
<Button
variant={ButtonVariant.link}
isInline
isDisabled={isEditLoading || !canEdit}
onClick={() => launchOverlay(NodeGroupsEditorModal, { node: obj })}
>
{t('console-app~Edit')}
</Button>
</div>
<Tooltip
triggerRef={editGroupButtonRef}
content={t('console-app~You do not have permission to edit groups.')}
/>
</FlexItem>
</Flex>
</>
) : null}
</DescriptionList>
Expand Down