From faabe37c994bdd610ce2ebc788b26cf0eed8a8fb Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 27 Jun 2023 17:21:49 -0400 Subject: [PATCH 1/5] disable delete button if there's only one public ip address --- .../LinodeNetworking/LinodeNetworking.tsx | 3 ++ .../LinodeNetworkingActionMenu.tsx | 31 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx index c11c9b86e1d..607bf882187 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx @@ -152,6 +152,7 @@ const LinodeNetworking = () => { const renderIPRow = (ipDisplay: IPDisplay) => { const { address, type, gateway, subnetMask, rdns, _ip, _range } = ipDisplay; + const isOnlyPublicIP = ips?.ipv4.public.length === 1; return ( { ipAddress={_ip} onRemove={openRemoveIPDialog} readOnly={readOnly} + isOnlyPublicIP={isOnlyPublicIP} /> ) : _range ? ( { onEdit={() => handleOpenEditRDNSForRange(_range)} onRemove={openRemoveIPRangeDialog} readOnly={readOnly} + isOnlyPublicIP={isOnlyPublicIP} /> ) : null} diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx index 84a44f884ff..6144312fb36 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx @@ -22,6 +22,7 @@ interface Props { ipType: IPTypes; ipAddress?: IPAddress | IPRange; readOnly: boolean; + isOnlyPublicIP: boolean; } type CombinedProps = Props & RouteComponentProps<{}>; @@ -32,7 +33,14 @@ export const LinodeNetworkingActionMenu: React.FC = (props) => { const theme = useTheme(); const matchesMdDown = useMediaQuery(theme.breakpoints.down('lg')); - const { onEdit, onRemove, ipType, ipAddress, readOnly } = props; + const { + onEdit, + onRemove, + ipType, + ipAddress, + readOnly, + isOnlyPublicIP, + } = props; const showEdit = ipType !== 'IPv4 – Private' && @@ -45,23 +53,37 @@ export const LinodeNetworkingActionMenu: React.FC = (props) => { // if we have a 116 we don't want to give the option to remove it const is116Range = ipAddress?.prefix === 116; + const readOnlyTooltip = readOnly + ? "You don't have permissions to perform this action" + : undefined; + + const isOnlyPublicIPTooltip = isOnlyPublicIP + ? 'Linodes must have at least one public IP' + : undefined; + const actions = [ onRemove && ipAddress && !is116Range && deletableIPTypes.includes(ipType) ? { title: 'Delete', - disabled: readOnly, onClick: () => { onRemove(ipAddress); }, + disabled: readOnly || isOnlyPublicIP, + tooltip: readOnly + ? readOnlyTooltip + : isOnlyPublicIP + ? isOnlyPublicIPTooltip + : undefined, } : null, onEdit && ipAddress && showEdit ? { title: 'Edit RDNS', - disabled: readOnly, onClick: () => { onEdit(ipAddress); }, + disabled: readOnly, + tooltip: readOnly ? readOnlyTooltip : undefined, } : null, ].filter(Boolean) as Action[]; @@ -74,8 +96,9 @@ export const LinodeNetworkingActionMenu: React.FC = (props) => { ); })} From f140c5f48c8cbb640bf7ae2032d54a0dd6297e84 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 27 Jun 2023 17:27:28 -0400 Subject: [PATCH 2/5] fix hardcoded readonly --- .../LinodeNetworking/LinodeNetworking.tsx | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx index 607bf882187..858399f4789 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx @@ -1,45 +1,46 @@ import { LinodeIPsResponse } from '@linode/api-v4/lib/linodes'; import { IPAddress, IPRange } from '@linode/api-v4/lib/networking'; +import Grid from '@mui/material/Unstable_Grid2'; +import { Theme, useTheme } from '@mui/material/styles'; import { IPv6, parse as parseIP } from 'ipaddr.js'; import * as React from 'react'; +import { useParams } from 'react-router-dom'; import AddNewLink from 'src/components/AddNewLink'; import Button from 'src/components/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; -import Hidden from 'src/components/core/Hidden'; -import Paper from 'src/components/core/Paper'; -import { makeStyles } from 'tss-react/mui'; -import { Theme, useTheme } from '@mui/material/styles'; -import { TableBody } from 'src/components/TableBody'; -import { TableHead } from 'src/components/TableHead'; -import Typography from 'src/components/core/Typography'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import OrderBy from 'src/components/OrderBy'; import { Table } from 'src/components/Table'; +import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; +import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; import { TableSortCell } from 'src/components/TableSortCell'; +import Hidden from 'src/components/core/Hidden'; +import Paper from 'src/components/core/Paper'; +import Typography from 'src/components/core/Typography'; +import { useLinodeQuery } from 'src/queries/linodes/linodes'; +import { + useAllIPsQuery, + useLinodeIPsQuery, +} from 'src/queries/linodes/networking'; +import { useGrants } from 'src/queries/profile'; +import { makeStyles } from 'tss-react/mui'; import LinodePermissionsError from '../LinodePermissionsError'; import AddIPDrawer from './AddIPDrawer'; +import { DeleteIPDialog } from './DeleteIPDialog'; +import { DeleteRangeDialog } from './DeleteRangeDialog'; import { EditIPRDNSDrawer } from './EditIPRDNSDrawer'; +import { EditRangeRDNSDrawer } from './EditRangeRDNSDrawer'; import IPSharing from './IPSharing'; import IPTransfer from './IPTransfer'; import LinodeNetworkingActionMenu from './LinodeNetworkingActionMenu'; import LinodeNetworkingSummaryPanel from './NetworkingSummaryPanel'; -import { IPTypes } from './types'; import { ViewIPDrawer } from './ViewIPDrawer'; -import { ViewRangeDrawer } from './ViewRangeDrawer'; import ViewRDNSDrawer from './ViewRDNSDrawer'; -import Grid from '@mui/material/Unstable_Grid2'; -import { - useAllIPsQuery, - useLinodeIPsQuery, -} from 'src/queries/linodes/networking'; -import { useParams } from 'react-router-dom'; -import { EditRangeRDNSDrawer } from './EditRangeRDNSDrawer'; -import { DeleteIPDialog } from './DeleteIPDialog'; -import { DeleteRangeDialog } from './DeleteRangeDialog'; -import { useLinodeQuery } from 'src/queries/linodes/linodes'; +import { ViewRangeDrawer } from './ViewRangeDrawer'; +import { IPTypes } from './types'; const useStyles = makeStyles()( (theme: Theme, _params, classes) => ({ @@ -100,12 +101,16 @@ const useStyles = makeStyles()( export const ipv4TableID = 'ips'; const LinodeNetworking = () => { - const readOnly = false; + const { data: grants } = useGrants(); const { classes } = useStyles(); const { linodeId } = useParams<{ linodeId: string }>(); const id = Number(linodeId); const { data: ips, isLoading, error } = useLinodeIPsQuery(id); + const readOnly = + grants !== undefined && + grants.linode.some((g) => g.id === id && g.permissions === 'read_only'); + const [selectedIP, setSelectedIP] = React.useState(); const [selectedRange, setSelectedRange] = React.useState(); From 04e212c7ebc74a01102ae894d01bdf2c557810a4 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 27 Jun 2023 17:35:32 -0400 Subject: [PATCH 3/5] clean up --- .../LinodeNetworkingActionMenu.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx index 6144312fb36..5061b0ab889 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx @@ -1,21 +1,15 @@ import { IPAddress, IPRange } from '@linode/api-v4/lib/networking'; +import { Theme } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import { useTheme } from '@mui/styles'; import { isEmpty } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import ActionMenu, { Action } from 'src/components/ActionMenu'; -import { makeStyles } from 'tss-react/mui'; -import { useTheme } from '@mui/styles'; -import { Theme } from '@mui/material/styles'; -import useMediaQuery from '@mui/material/useMediaQuery'; import InlineMenuAction from 'src/components/InlineMenuAction'; +import Box from 'src/components/core/Box'; import { IPTypes } from './types'; -const useStyles = makeStyles()(() => ({ - emptyCell: { - height: 40, - }, -})); - interface Props { onEdit?: (ip: IPAddress | IPRange) => void; onRemove?: (ip: IPAddress | IPRange) => void; @@ -27,9 +21,7 @@ interface Props { type CombinedProps = Props & RouteComponentProps<{}>; -export const LinodeNetworkingActionMenu: React.FC = (props) => { - const { classes } = useStyles(); - +export const LinodeNetworkingActionMenu = (props: CombinedProps) => { const theme = useTheme(); const matchesMdDown = useMediaQuery(theme.breakpoints.down('lg')); @@ -110,7 +102,7 @@ export const LinodeNetworkingActionMenu: React.FC = (props) => { )} ) : ( - + ); }; From 1df414ed582e4b719c066c4e99fa8c0a94abe669 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 27 Jun 2023 17:42:28 -0400 Subject: [PATCH 4/5] Added changeset: Disable delete button for public IP addresses if it's the only IP address --- packages/manager/.changeset/pr-9332-fixed-1687902148264.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-9332-fixed-1687902148264.md diff --git a/packages/manager/.changeset/pr-9332-fixed-1687902148264.md b/packages/manager/.changeset/pr-9332-fixed-1687902148264.md new file mode 100644 index 00000000000..1ba22de26c9 --- /dev/null +++ b/packages/manager/.changeset/pr-9332-fixed-1687902148264.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Disable delete button for public IP addresses if it's the only IP address ([#9332](https://github.com/linode/manager/pull/9332)) From 45b07e40430f4acc2b2684546441c373b3016485 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Wed, 28 Jun 2023 11:26:22 -0400 Subject: [PATCH 5/5] address feedback --- .../LinodesDetail/LinodeNetworking/LinodeNetworking.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx index 858399f4789..4b5571cda72 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworking.tsx @@ -157,7 +157,8 @@ const LinodeNetworking = () => { const renderIPRow = (ipDisplay: IPDisplay) => { const { address, type, gateway, subnetMask, rdns, _ip, _range } = ipDisplay; - const isOnlyPublicIP = ips?.ipv4.public.length === 1; + const isOnlyPublicIP = + ips?.ipv4.public.length === 1 && type === 'IPv4 – Public'; return (