From 5027b7c295bcfeb3bcd28ed8144cf943fa509e5e Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Tue, 15 Apr 2025 15:31:41 -0500 Subject: [PATCH 1/7] Add warning notice --- .../src/features/Volumes/VolumesLanding.tsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 40effdd6235..3ce07a83b3b 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -4,7 +4,9 @@ import { ErrorState, IconButton, InputAdornment, + Notice, TextField, + Typography, } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useNavigate, useParams, useSearch } from '@tanstack/react-router'; @@ -33,6 +35,7 @@ import { } from 'src/routes/volumes/constants'; import { VOLUME_TABLE_PREFERENCE_KEY } from 'src/routes/volumes/constants'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; +import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { DeleteVolumeDialog } from './Dialogs/DeleteVolumeDialog'; import { DetachVolumeDialog } from './Dialogs/DetachVolumeDialog'; @@ -71,6 +74,8 @@ export const VolumesLanding = () => { }); const { query } = search; + const { _isRestrictedUser } = useAccountManagement(); + const { handleOrderChange, order, orderBy } = useOrderV2({ initialRoute: { defaultOrder: { @@ -90,7 +95,12 @@ export const VolumesLanding = () => { }), }; - const { data: volumes, error, isFetching, isLoading } = useVolumesQuery( + const { + data: volumes, + error, + isFetching, + isLoading, + } = useVolumesQuery( { page: pagination.page, page_size: pagination.pageSize, @@ -98,9 +108,8 @@ export const VolumesLanding = () => { filter ); - const { - isBlockStorageEncryptionFeatureEnabled, - } = useIsBlockStorageEncryptionFeatureEnabled(); + const { isBlockStorageEncryptionFeatureEnabled } = + useIsBlockStorageEncryptionFeatureEnabled(); const { data: selectedVolume, isFetching: isFetchingVolume } = useDialogData({ enabled: !!params.volumeId, @@ -166,6 +175,14 @@ export const VolumesLanding = () => { return ( <> + {_isRestrictedUser && ( + + + Access restricted: You do not have permissions to + create or edit Volumes. + + + )} Date: Tue, 15 Apr 2025 15:48:17 -0500 Subject: [PATCH 2/7] Added changeset: Missing warning message for restricted users on Volumes page --- packages/manager/.changeset/pr-12037-fixed-1744750096750.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-12037-fixed-1744750096750.md diff --git a/packages/manager/.changeset/pr-12037-fixed-1744750096750.md b/packages/manager/.changeset/pr-12037-fixed-1744750096750.md new file mode 100644 index 00000000000..3b5398da8c4 --- /dev/null +++ b/packages/manager/.changeset/pr-12037-fixed-1744750096750.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Missing warning message for restricted users on Volumes page ([#12037](https://github.com/linode/manager/pull/12037)) From 94a5057021bad4a9f3a6934508e1a204f44e5740 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Tue, 15 Apr 2025 16:29:25 -0500 Subject: [PATCH 3/7] Reorder import --- packages/manager/src/features/Volumes/VolumesLanding.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 3ce07a83b3b..e3b71de2a5c 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -29,13 +29,13 @@ import { useDialogData } from 'src/hooks/useDialogData'; import { useOrderV2 } from 'src/hooks/useOrderV2'; import { usePaginationV2 } from 'src/hooks/usePaginationV2'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; +import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { VOLUME_TABLE_DEFAULT_ORDER, VOLUME_TABLE_DEFAULT_ORDER_BY, } from 'src/routes/volumes/constants'; import { VOLUME_TABLE_PREFERENCE_KEY } from 'src/routes/volumes/constants'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; -import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { DeleteVolumeDialog } from './Dialogs/DeleteVolumeDialog'; import { DetachVolumeDialog } from './Dialogs/DetachVolumeDialog'; From ea0fdc57a84e418f9c51a0f6857d37ed2b312571 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Tue, 15 Apr 2025 16:30:01 -0500 Subject: [PATCH 4/7] Reorder import --- packages/manager/src/features/Volumes/VolumesLanding.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index e3b71de2a5c..08170f4346d 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -25,11 +25,11 @@ import { TableRow } from 'src/components/TableRow'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; import { TableSortCell } from 'src/components/TableSortCell'; import { getRestrictedResourceText } from 'src/features/Account/utils'; +import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { useDialogData } from 'src/hooks/useDialogData'; import { useOrderV2 } from 'src/hooks/useOrderV2'; import { usePaginationV2 } from 'src/hooks/usePaginationV2'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; -import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { VOLUME_TABLE_DEFAULT_ORDER, VOLUME_TABLE_DEFAULT_ORDER_BY, From c4919ab08f22bdbda3c3d304ff28fe7cd65d593a Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Wed, 16 Apr 2025 16:13:32 -0500 Subject: [PATCH 5/7] Add warning icon to notice --- packages/manager/src/features/Volumes/VolumesLanding.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 08170f4346d..ef66b63e193 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -176,7 +176,7 @@ export const VolumesLanding = () => { <> {_isRestrictedUser && ( - + Access restricted: You do not have permissions to create or edit Volumes. From 18c5148e5a0edca81ed4a40bf5ab7af1d5d05256 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Thu, 17 Apr 2025 10:23:47 -0500 Subject: [PATCH 6/7] Use getRestrictedResourceText --- .../manager/src/features/Account/utils.ts | 24 ++++++++++++++++--- .../src/features/Volumes/VolumesLanding.tsx | 16 +++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/manager/src/features/Account/utils.ts b/packages/manager/src/features/Account/utils.ts index cd7aa09578c..ca56e516ffb 100644 --- a/packages/manager/src/features/Account/utils.ts +++ b/packages/manager/src/features/Account/utils.ts @@ -23,7 +23,7 @@ export type ActionType = | 'view'; interface GetRestrictedResourceText { - action?: ActionType; + action?: ActionType | ActionType[]; includeContactInfo?: boolean; isChildUser?: boolean; isSingular?: boolean; @@ -46,7 +46,7 @@ export type RestrictedGlobalGrantType = | NonAccountAccessGrant; /** - * Get a resource restricted message based on action and resource type. + * Get a resource restricted message based on action(s) and resource type. */ export const getRestrictedResourceText = ({ action = 'edit', @@ -61,7 +61,9 @@ export const getRestrictedResourceText = ({ const contactPerson = isChildUser ? PARENT_USER : ADMINISTRATOR; - let message = `You don't have permissions to ${action} ${resource}.`; + const actionText = formatAction(action); + + let message = `You don't have permissions to ${actionText} ${resource}.`; if (includeContactInfo) { message += ` Please contact your ${contactPerson} to request the necessary permissions.`; @@ -89,3 +91,19 @@ export const useIsTaxIdEnabled = (): { return { isTaxIdEnabled }; }; + +/** + * Formats one or more actions into a readable string + * @param action - A single action or array of actions + * + * @returns A formatted string representing the action(s) + */ +function formatAction(action: ActionType | ActionType[]): string { + if (!Array.isArray(action)) return action; + + const len = action.length; + if (len === 1) return action[0]; + if (len === 2) return `${action[0]} or ${action[1]}`; + + return `${action.slice(0, -1).join(', ')}, or ${action[action.length - 1]}`; +} diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index ef66b63e193..8fc12b1db3b 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -6,7 +6,6 @@ import { InputAdornment, Notice, TextField, - Typography, } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useNavigate, useParams, useSearch } from '@tanstack/react-router'; @@ -176,12 +175,15 @@ export const VolumesLanding = () => { <> {_isRestrictedUser && ( - - - Access restricted: You do not have permissions to - create or edit Volumes. - - + )} Date: Thu, 17 Apr 2025 10:37:32 -0500 Subject: [PATCH 7/7] Fix ESLint callouts --- packages/manager/src/features/Volumes/VolumesLanding.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 8fc12b1db3b..11f53ea9e4c 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -176,14 +176,14 @@ export const VolumesLanding = () => { {_isRestrictedUser && ( + variant="warning" + /> )}