diff --git a/packages/manager/.changeset/pr-10320-fixed-1711553963315.md b/packages/manager/.changeset/pr-10320-fixed-1711553963315.md new file mode 100644 index 00000000000..857db6d8b5d --- /dev/null +++ b/packages/manager/.changeset/pr-10320-fixed-1711553963315.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Wrong status indicator when provisioning a LKE ([#10320](https://github.com/linode/manager/pull/10320)) diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx index 8db3aad953e..ed4ff6d7878 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx @@ -2,16 +2,16 @@ import { AutoscaleSettings, PoolNodeResponse, } from '@linode/api-v4/lib/kubernetes'; -import Grid from '@mui/material/Unstable_Grid2'; import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; +import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import { Button } from 'src/components/Button/Button'; import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; -import NodeTable from './NodeTable'; +import { NodeTable } from './NodeTable'; interface Props { autoscaler: AutoscaleSettings; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx new file mode 100644 index 00000000000..a41e1178f6e --- /dev/null +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx @@ -0,0 +1,128 @@ +import { APIError } from '@linode/api-v4/lib/types'; +import Grid from '@mui/material/Unstable_Grid2'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; + +import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; +import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; +import { TableCell } from 'src/components/TableCell'; +import { Typography } from 'src/components/Typography'; +import { transitionText } from 'src/features/Linodes/transitions'; +import { useInProgressEvents } from 'src/queries/events/events'; + +import NodeActionMenu from './NodeActionMenu'; +import { StyledCopyTooltip, StyledTableRow } from './NodeTable.styles'; + +export interface NodeRow { + instanceId?: number; + instanceStatus?: string; + ip?: string; + label?: string; + nodeId: string; + nodeStatus: string; +} + +interface NodeRowProps extends NodeRow { + linodeError?: APIError[]; + openRecycleNodeDialog: (nodeID: string, linodeLabel: string) => void; + typeLabel: string; +} + +export const NodeRow = React.memo((props: NodeRowProps) => { + const { + instanceId, + instanceStatus, + ip, + label, + linodeError, + nodeId, + nodeStatus, + openRecycleNodeDialog, + typeLabel, + } = props; + + const { data: events } = useInProgressEvents(); + + const recentEvent = events?.find( + (event) => + event.entity?.id === instanceId && event.entity?.type === 'linode' + ); + + const linodeLink = instanceId ? `/linodes/${instanceId}` : undefined; + + const nodeReadyAndInstanceRunning = + nodeStatus === 'ready' && instanceStatus === 'running'; + + const iconStatus = + nodeStatus === 'not_ready' + ? 'other' + : nodeReadyAndInstanceRunning + ? 'active' + : 'inactive'; + + const displayLabel = label ?? typeLabel; + + const displayStatus = + nodeStatus === 'not_ready' + ? 'Provisioning' + : transitionText(instanceStatus ?? '', instanceId ?? -1, recentEvent); + + const displayIP = ip ?? ''; + + return ( + + + + + + {linodeLink ? ( + {displayLabel} + ) : ( + displayLabel + )} + + + + + + {linodeError ? ( + ({ + color: theme.color.red, + })} + > + Error retrieving status + + ) : ( + <> + + {displayStatus} + + )} + + + {linodeError ? ( + ({ + color: theme.color.red, + })} + > + Error retrieving IP + + ) : displayIP.length > 0 ? ( + <> + + + + ) : null} + + + + + + ); +}); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts new file mode 100644 index 00000000000..482ab5b66fc --- /dev/null +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts @@ -0,0 +1,42 @@ +import { styled } from '@mui/material/styles'; + +import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; +import { Table } from 'src/components/Table'; +import { TableRow } from 'src/components/TableRow'; + +export const StyledTableRow = styled(TableRow, { + label: 'TableRow', +})(({ theme }) => ({ + '& svg': { + height: `12px`, + opacity: 0, + width: `12px`, + }, + '&:hover': { + backgroundColor: theme.bg.lightBlue1, + }, + [`&:hover .copy-tooltip > svg, & .copy-tooltip:focus > svg`]: { + opacity: 1, + }, + marginLeft: 4, + top: 1, +})); + +export const StyledTable = styled(Table, { + label: 'Table', +})(({ theme }) => ({ + borderLeft: `1px solid ${theme.borderColors.borderTable}`, + borderRight: `1px solid ${theme.borderColors.borderTable}`, +})); + +export const StyledCopyTooltip = styled(CopyTooltip, { + label: 'CopyTooltip', +})(() => ({ + '& svg': { + height: `12px`, + opacity: 0, + width: `12px`, + }, + marginLeft: 4, + top: 1, +})); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx index 6b5ee353879..abb227b83c6 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx @@ -1,17 +1,9 @@ import { PoolNodeResponse } from '@linode/api-v4/lib/kubernetes'; -import { APIError } from '@linode/api-v4/lib/types'; -import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; import * as React from 'react'; -import { Link } from 'react-router-dom'; -import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import OrderBy from 'src/components/OrderBy'; import Paginate from 'src/components/Paginate'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; -import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; import { TableContentWrapper } from 'src/components/TableContentWrapper/TableContentWrapper'; @@ -20,57 +12,14 @@ import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; import { TableSortCell } from 'src/components/TableSortCell'; import { Typography } from 'src/components/Typography'; -import { transitionText } from 'src/features/Linodes/transitions'; -import { useInProgressEvents } from 'src/queries/events/events'; import { useAllLinodesQuery } from 'src/queries/linodes/linodes'; import { LinodeWithMaintenance } from 'src/utilities/linodes'; -import NodeActionMenu from './NodeActionMenu'; +import { NodeRow as _NodeRow } from './NodeRow'; +import { StyledTable } from './NodeTable.styles'; -const useStyles = makeStyles()( - (theme: Theme, _params, classes) => ({ - copy: { - '& svg': { - height: `12px`, - opacity: 0, - width: `12px`, - }, - marginLeft: 4, - top: 1, - }, - error: { - color: theme.color.red, - }, - ipCell: { - ...theme.applyTableHeaderStyles, - width: '35%', - }, - labelCell: { - ...theme.applyTableHeaderStyles, - width: '35%', - }, - row: { - '&:hover': { - backgroundColor: theme.bg.lightBlue1, - }, - [`&:hover .${classes.copy} > svg, & .${classes.copy}:focus > svg`]: { - opacity: 1, - }, - }, - statusCell: { - ...theme.applyTableHeaderStyles, - width: '15%', - }, - table: { - borderLeft: `1px solid ${theme.borderColors.borderTable}`, - borderRight: `1px solid ${theme.borderColors.borderTable}`, - }, - }) -); +import type { NodeRow } from './NodeRow'; -// ============================================================================= -// NodeTable -// ============================================================================= export interface Props { nodes: PoolNodeResponse[]; openRecycleNodeDialog: (nodeID: string, linodeLabel: string) => void; @@ -78,11 +27,9 @@ export interface Props { typeLabel: string; } -export const NodeTable: React.FC = (props) => { +export const NodeTable = React.memo((props: Props) => { const { nodes, openRecycleNodeDialog, poolId, typeLabel } = props; - const { classes } = useStyles(); - const { data: linodes, error, isLoading } = useAllLinodesQuery(); const rowData = nodes.map((thisNode) => nodeToRow(thisNode, linodes ?? [])); @@ -100,15 +47,15 @@ export const NodeTable: React.FC = (props) => { pageSize, }) => ( <> - + ({ + ...theme.applyTableHeaderStyles, + width: '35%', + })} active={orderBy === 'label'} - className={classes.labelCell} direction={order} handleClick={handleOrderChange} label={'label'} @@ -116,8 +63,11 @@ export const NodeTable: React.FC = (props) => { Linode ({ + ...theme.applyTableHeaderStyles, + width: '35%', + })} active={orderBy === 'instanceStatus'} - className={classes.statusCell} direction={order} handleClick={handleOrderChange} label={'instanceStatus'} @@ -125,8 +75,11 @@ export const NodeTable: React.FC = (props) => { Status ({ + ...theme.applyTableHeaderStyles, + width: '15%', + })} active={orderBy === 'ip'} - className={classes.ipCell} direction={order} handleClick={handleOrderChange} label={'ip'} @@ -144,7 +97,7 @@ export const NodeTable: React.FC = (props) => { > {paginatedAndOrderedData.map((eachRow) => { return ( - = (props) => { -
+ = (props) => { )} ); -}; - -export default React.memo(NodeTable); - -// ============================================================================= -// NodeRow -// ============================================================================= -interface NodeRow { - instanceId?: number; - instanceStatus?: string; - ip?: string; - label?: string; - nodeId: string; - nodeStatus: string; -} - -interface NodeRowProps extends NodeRow { - linodeError?: APIError[]; - openRecycleNodeDialog: (nodeID: string, linodeLabel: string) => void; - typeLabel: string; -} - -export const NodeRow: React.FC = React.memo((props) => { - const { - instanceId, - instanceStatus, - ip, - label, - linodeError, - nodeId, - nodeStatus, - openRecycleNodeDialog, - typeLabel, - } = props; - - const { classes } = useStyles(); - - const { data: events } = useInProgressEvents(); - - const recentEvent = events?.find( - (event) => - event.entity?.id === instanceId && event.entity?.type === 'linode' - ); - - const linodeLink = instanceId ? `/linodes/${instanceId}` : undefined; - - const nodeReadyAndInstanceRunning = - nodeStatus === 'ready' && instanceStatus === 'running'; - const iconStatus = nodeReadyAndInstanceRunning ? 'active' : 'inactive'; - - const displayLabel = label ?? typeLabel; - const displayStatus = - nodeStatus === 'not_ready' - ? 'Provisioning' - : transitionText(instanceStatus ?? '', instanceId ?? -1, recentEvent); - - const displayIP = ip ?? ''; - - return ( - - - - - - {linodeLink ? ( - {displayLabel} - ) : ( - displayLabel - )} - - - - - - {linodeError ? ( - - Error retrieving status - - ) : ( - <> - - {displayStatus} - - )} - - - {linodeError ? ( - Error retrieving IP - ) : displayIP.length > 0 ? ( - <> - - - - ) : null} - - - - - - ); }); -// ============================================================================= -// Utilities -// ============================================================================= - /** * Transforms an LKE Pool Node to a NodeRow. */