1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-22 06:49:40 +02:00

refactor(cluster): migrate nodes datatable to react [EE-4962] (#10459)

Co-authored-by: testa113 <testa113>
This commit is contained in:
Ali 2023-10-16 21:19:08 +01:00 committed by GitHub
parent b346fd7f39
commit 0e47f22c0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 448 additions and 219 deletions

View file

@ -0,0 +1,47 @@
import { CellContext } from '@tanstack/react-table';
import { BarChart } from 'lucide-react';
import { Link } from '@@/Link';
import { Icon } from '@@/Icon';
import { NodeRowData } from '../types';
import { columnHelper } from './helper';
export function getActions(metricsEnabled: boolean) {
return columnHelper.accessor(() => '', {
header: 'Actions',
enableSorting: false,
cell: (props) => (
<ActionsCell
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
metricsEnabled={metricsEnabled}
/>
),
});
}
function ActionsCell({
row: { original: node },
metricsEnabled,
}: CellContext<NodeRowData, string> & {
metricsEnabled: boolean;
}) {
const nodeName = node.metadata?.name;
return (
<div className="flex gap-1.5">
{metricsEnabled && (
<Link
title="Stats"
to="kubernetes.cluster.node.stats"
params={{ nodeName }}
className="flex items-center p-1"
>
<Icon icon={BarChart} />
</Link>
)}
</div>
);
}

View file

@ -0,0 +1,14 @@
import { parseCpu } from '@/react/kubernetes/utils';
import { NodeRowData } from '../types';
import { columnHelper } from './helper';
export const cpu = columnHelper.accessor((row) => getCPU(row), {
header: 'CPU',
cell: ({ row: { original: node } }) => getCPU(node),
});
function getCPU(node: NodeRowData) {
return parseCpu(node.status?.allocatable?.cpu ?? '');
}

View file

@ -0,0 +1,5 @@
import { createColumnHelper } from '@tanstack/react-table';
import { NodeRowData } from '../types';
export const columnHelper = createColumnHelper<NodeRowData>();

View file

@ -0,0 +1,25 @@
import { name } from './name';
import { role } from './role';
import { status } from './status';
import { cpu } from './cpu';
import { memory } from './memory';
import { version } from './version';
import { ip } from './ip';
import { getActions } from './actions';
export function getColumns(isServerMetricsEnabled: boolean) {
if (!isServerMetricsEnabled) {
return [name, role, status, cpu, memory, version, ip];
}
return [
name,
role,
status,
cpu,
memory,
version,
ip,
getActions(isServerMetricsEnabled),
];
}

View file

@ -0,0 +1,11 @@
import { getInternalNodeIpAddress } from '../utils';
import { columnHelper } from './helper';
export const ip = columnHelper.accessor(
(row) => getInternalNodeIpAddress(row) ?? '-',
{
header: 'IP Address',
cell: ({ row }) => getInternalNodeIpAddress(row.original) ?? '-',
}
);

View file

@ -0,0 +1,16 @@
import filesizeParser from 'filesize-parser';
import { humanize } from '@/portainer/filters/filters';
import { NodeRowData } from '../types';
import { columnHelper } from './helper';
export const memory = columnHelper.accessor((row) => getMemory(row), {
header: 'Memory',
cell: ({ row: { original: node } }) => getMemory(node),
});
function getMemory(node: NodeRowData) {
return humanize(filesizeParser(node.status?.allocatable?.memory ?? ''));
}

View file

@ -0,0 +1,35 @@
import { CellContext } from '@tanstack/react-table';
import { Authorized } from '@/react/hooks/useUser';
import { Link } from '@@/Link';
import { Badge } from '@@/Badge';
import { NodeRowData } from '../types';
import { columnHelper } from './helper';
export const name = columnHelper.accessor('Name', {
header: 'Name',
cell: NameCell,
});
function NameCell({
row: { original: node },
}: CellContext<NodeRowData, string>) {
const nodeName = node.metadata?.name;
return (
<div className="flex gap-2 whitespace-nowrap">
<Authorized
authorizations="K8sClusterNodeR"
childrenUnauthorized={nodeName}
>
<Link to="kubernetes.cluster.node" params={{ nodeName }}>
{nodeName}
</Link>
</Authorized>
{node.isApi && <Badge type="info">api</Badge>}
{node.isPublishedNode && <Badge type="success">environment IP</Badge>}
</div>
);
}

View file

@ -0,0 +1,8 @@
import { getRole } from '../utils';
import { columnHelper } from './helper';
export const role = columnHelper.accessor((row) => getRole(row), {
header: 'Role',
cell: ({ row: { original: node } }) => getRole(node),
});

View file

@ -0,0 +1,44 @@
import { CellContext } from '@tanstack/react-table';
import { StatusBadge } from '@@/StatusBadge';
import { NodeRowData } from '../types';
import { columnHelper } from './helper';
export const status = columnHelper.accessor((row) => getStatus(row), {
header: 'Status',
cell: StatusCell,
});
function StatusCell({
row: { original: node },
}: CellContext<NodeRowData, string>) {
const status = getStatus(node);
const isDeleting =
node.metadata?.annotations?.['portainer.io/removing-node'] === 'true';
if (isDeleting) {
return <StatusBadge color="warning">Removing</StatusBadge>;
}
return (
<div className="whitespace-nowrap">
<StatusBadge color={status === 'Ready' ? 'success' : 'warning'}>
{status}
</StatusBadge>
{node.spec?.unschedulable && (
<StatusBadge color="warning" className="mt-2">
SchedulingDisabled
</StatusBadge>
)}
</div>
);
}
function getStatus(node: NodeRowData) {
return (
node.status?.conditions?.find((condition) => condition.status === 'True')
?.type ?? 'Not ready'
);
}

View file

@ -0,0 +1,10 @@
import { columnHelper } from './helper';
export const version = columnHelper.accessor(
(row) => row.status?.nodeInfo?.kubeletVersion ?? '',
{
header: 'Version',
cell: ({ row: { original: node } }) =>
node.status?.nodeInfo?.kubeletVersion ?? '',
}
);