From a9f7edd271475892e80a08f697dafc914b314c9c Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Fri, 23 Feb 2024 15:49:28 -0500 Subject: [PATCH 1/8] refactor: [M3-7687] - Linodes Restricted User Experience 2/2 --- .../ResourcesSection.tsx | 2 + .../src/components/TagCell/TagCell.tsx | 6 +- .../features/Linodes/LinodeEntityDetail.tsx | 119 ++++++++++-------- .../Linodes/LinodeEntityDetailFooter.tsx | 5 +- .../Linodes/LinodeEntityDetailHeader.tsx | 12 +- .../LinodeBackup/BackupsPlaceholder.tsx | 2 - .../LinodeBackup/LinodeBackups.tsx | 2 - .../LinodeConfigs/LinodeConfigActionMenu.tsx | 2 + .../LinodeNetworking/LinodeIPAddresses.tsx | 28 +++-- .../NetworkingSummaryPanel.tsx | 85 ++++++------- .../LinodesDetail/LinodePermissionsError.tsx | 5 +- .../LinodeResize/LinodeResize.tsx | 18 +-- .../LinodeSettings/LinodeSettings.tsx | 2 - .../LinodeStorage/LinodeDiskActionMenu.tsx | 30 ++--- .../LinodeStorage/LinodeVolumes.tsx | 24 ++-- .../Linodes/LinodesLanding/LinodesLanding.tsx | 15 +++ .../LinodesLandingEmptyState.tsx | 12 ++ packages/manager/src/utilities/linodes.ts | 23 +--- 18 files changed, 214 insertions(+), 178 deletions(-) diff --git a/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx b/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx index 61ebc8a818f..33386afde6b 100644 --- a/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx +++ b/packages/manager/src/components/EmptyLandingPageResources/ResourcesSection.tsx @@ -24,7 +24,9 @@ import type { interface ButtonProps { children: string; + disabled?: boolean; onClick: () => void; + tooltipText?: string; } interface ResourcesSectionProps { diff --git a/packages/manager/src/components/TagCell/TagCell.tsx b/packages/manager/src/components/TagCell/TagCell.tsx index ced0fd1b51b..81e821c0008 100644 --- a/packages/manager/src/components/TagCell/TagCell.tsx +++ b/packages/manager/src/components/TagCell/TagCell.tsx @@ -1,6 +1,6 @@ import MoreHoriz from '@mui/icons-material/MoreHoriz'; -import Grid from '@mui/material/Unstable_Grid2'; import { styled } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; import { SxProps } from '@mui/system'; import * as React from 'react'; @@ -13,6 +13,7 @@ import { StyledPlusIcon, StyledTagButton } from '../Button/StyledTagButton'; import { AddTag } from './AddTag'; interface TagCellProps { + disabled?: boolean; listAllTags: (tags: string[]) => void; sx?: SxProps; tags: string[]; @@ -35,7 +36,7 @@ const checkOverflow = (el: any) => { }; const TagCell = (props: TagCellProps) => { - const { sx, tags, updateTags } = props; + const { disabled, sx, tags, updateTags } = props; const [hasOverflow, setOverflow] = React.useState(false); const [addingTag, setAddingTag] = React.useState(false); @@ -110,6 +111,7 @@ const TagCell = (props: TagCellProps) => { ) : null} } onClick={() => setAddingTag(true)} title="Add a tag" diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx index 816faf4c2bc..e395a2fccab 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx @@ -1,7 +1,10 @@ import * as React from 'react'; import { EntityDetail } from 'src/components/EntityDetail/EntityDetail'; +import { Notice } from 'src/components/Notice/Notice'; +import { getRestrictedResourceText } from 'src/features/Account/utils'; import { notificationContext as _notificationContext } from 'src/features/NotificationCenter/NotificationContext'; +import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; import { useVPCConfigInterface } from 'src/hooks/useVPCConfigInterface'; import { useInProgressEvents } from 'src/queries/events/events'; import { useAllImagesQuery } from 'src/queries/images'; @@ -64,6 +67,12 @@ export const LinodeEntityDetail = (props: Props) => { vpcLinodeIsAssignedTo, } = useVPCConfigInterface(linode.id); + const isLinodesReadOnly = useIsResourceRestricted({ + grantLevel: 'read_only', + grantType: 'linode', + id: linode.id, + }); + const imageVendor = images?.find((i) => i.id === linode.image)?.vendor ?? null; @@ -83,55 +92,67 @@ export const LinodeEntityDetail = (props: Props) => { const trimmedIPv6 = linode.ipv6?.replace('/128', '') || null; return ( - - } - footer={ - - } - header={ - + {isLinodesReadOnly && ( + - } - /> + )} + + } + footer={ + + } + header={ + + } + /> + ); }; diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetailFooter.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetailFooter.tsx index 203d50752ce..b373425e39b 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetailFooter.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetailFooter.tsx @@ -1,5 +1,5 @@ -import Grid from '@mui/material/Unstable_Grid2'; import { useTheme } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; import { useSnackbar } from 'notistack'; import * as React from 'react'; @@ -34,6 +34,7 @@ export type Props = LinodeEntityDetailProps & { }; interface FooterProps { + isLinodesReadOnly: boolean; linodeCreated: string; linodeId: number; linodeLabel: string; @@ -49,6 +50,7 @@ export const LinodeEntityDetailFooter = React.memo((props: FooterProps) => { const { data: profile } = useProfile(); const { + isLinodesReadOnly, linodeCreated, linodeId, linodePlan, @@ -160,6 +162,7 @@ export const LinodeEntityDetailFooter = React.memo((props: FooterProps) => { flexDirection: 'row-reverse', }, }} + disabled={isLinodesReadOnly} listAllTags={openTagDrawer} tags={linodeTags} updateTags={updateTags} diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx index f940664b6ef..1f5f0dff11c 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx @@ -13,6 +13,7 @@ import { TypographyProps } from 'src/components/Typography'; import { LinodeActionMenu } from 'src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu'; import { ProgressDisplay } from 'src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow'; import { lishLaunch } from 'src/features/Lish/lishUtils'; +import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; import { queryKey as linodesQueryKey } from 'src/queries/linodes/linodes'; import { sendLinodeActionMenuItemEvent } from 'src/utilities/analytics'; @@ -78,6 +79,12 @@ export const LinodeEntityDetailHeader = ( variant, } = props; + const isLinodesReadOnly = useIsResourceRestricted({ + grantLevel: 'read_only', + grantType: 'linode', + id: linodeId, + }); + const isRunning = linodeStatus === 'running'; const isOffline = linodeStatus === 'stopped' || linodeStatus === 'offline'; const isOther = !['offline', 'running', 'stopped'].includes(linodeStatus); @@ -186,14 +193,14 @@ export const LinodeEntityDetailHeader = ( handlers.onOpenPowerDialog(isRunning ? 'Power Off' : 'Power On') } buttonType="secondary" - disabled={!(isRunning || isOffline)} + disabled={!(isRunning || isOffline) || isLinodesReadOnly} sx={sxActionItem} > {isRunning ? 'Power Off' : 'Power On'} setIsAddDrawerOpen(true)} /> @@ -202,7 +202,7 @@ export const LinodeIPAddresses = (props: LinodeIPAddressesProps) => { } key={`${ipDisplay.address}-${ipDisplay.type}`} linodeId={linodeID} - readOnly={isLinodesReadOnly} + readOnly={isLinodesGrantReadOnly} /> ))} @@ -249,19 +249,19 @@ export const LinodeIPAddresses = (props: LinodeIPAddressesProps) => { linodeId={linodeID} onClose={() => setIsAddDrawerOpen(false)} open={isAddDrawerOpen} - readOnly={isLinodesReadOnly} + readOnly={isLinodesGrantReadOnly} /> setIsTransferDialogOpen(false)} open={isTransferDialogOpen} - readOnly={isLinodesReadOnly} + readOnly={isLinodesGrantReadOnly} /> setIsShareDialogOpen(false)} open={isShareDialogOpen} - readOnly={isLinodesReadOnly} + readOnly={isLinodesGrantReadOnly} /> {selectedIP && ( { const hostMaintenance = linode?.status === 'stopped'; const isLinodeOffline = linode?.status === 'offline'; - const isLinodesReadOnly = useIsResourceRestricted({ + const isLinodesGrantReadOnly = useIsResourceRestricted({ grantLevel: 'read_only', grantType: 'linode', id: linodeId, @@ -159,7 +159,7 @@ export const LinodeResize = (props: Props) => { } }, [resizeError]); - const tableDisabled = hostMaintenance || isLinodesReadOnly; + const tableDisabled = hostMaintenance || isLinodesGrantReadOnly; const submitButtonDisabled = preferences?.type_to_confirm !== false && @@ -193,7 +193,7 @@ export const LinodeResize = (props: Props) => { title={`Resize Linode ${linode?.label}`} > - {isLinodesReadOnly && } + {isLinodesGrantReadOnly && } {hostMaintenance && } {disksError && ( { const { data: linode } = useLinodeQuery(id); - const isLinodesReadOnly = useIsResourceRestricted({ + const isLinodesGrantReadOnly = useIsResourceRestricted({ grantLevel: 'read_only', grantType: 'linode', id, @@ -177,7 +177,7 @@ export const LinodeVolumes = () => {