From 635b88c3a17f9fb969e7e4b87d133915bdd557e0 Mon Sep 17 00:00:00 2001 From: Dawson Toth Date: Thu, 14 May 2026 15:38:37 -0400 Subject: [PATCH] fix: Handle read-only roles better https://harperdb.atlassian.net/browse/STUDIO-596 --- src/features/auth/store/authStore.ts | 2 +- src/features/instance/InstanceNavBar.tsx | 4 ++-- .../applications/context/EditorViewProvider.tsx | 17 +++++++++++++---- src/features/instance/applications/index.tsx | 9 +++++++++ src/features/instance/config/overview/index.tsx | 4 +++- .../config/users/modals/EditUserModal.tsx | 4 +++- .../databases/components/DatabasesSidebar.tsx | 17 +++++++++++------ .../api/instance/applications/getComponents.ts | 3 ++- 8 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/features/auth/store/authStore.ts b/src/features/auth/store/authStore.ts index 7f69e0cfd..f8631e44c 100644 --- a/src/features/auth/store/authStore.ts +++ b/src/features/auth/store/authStore.ts @@ -190,7 +190,7 @@ class AuthStore { return value ? JSON.parse(atob(value)) : undefined; } - public checkForFabricConnect(id: EntityIds): boolean { + public checkForFabricConnect(id: EntityIds | undefined): boolean { return localStorage.getItem(this.fabricConnectKeyPrefix + id) === 'true'; } diff --git a/src/features/instance/InstanceNavBar.tsx b/src/features/instance/InstanceNavBar.tsx index 2822f1067..8dd95bde9 100644 --- a/src/features/instance/InstanceNavBar.tsx +++ b/src/features/instance/InstanceNavBar.tsx @@ -34,7 +34,7 @@ export function InstanceNavBar() { const params = useParams({ strict: false }); const { version }: RegistrationInfoResponse = useLoaderData({ strict: false }); - const statusAvailable = wasAReleasedBeforeB('4.6.0', version); + const statusAvailable = wasAReleasedBeforeB('4.6.0', version) && canManage; const apisAvailable = wasAReleasedBeforeB('4.7.0-beta.7', version); const instanceParams = useInstanceClientIdParams(); @@ -43,7 +43,7 @@ export function InstanceNavBar() { const links = useMemo(() => [ - { + canManage && { to: buildAbsoluteLinkToPage(params), activeOptions: { exact: true }, name: 'Applications', diff --git a/src/features/instance/applications/context/EditorViewProvider.tsx b/src/features/instance/applications/context/EditorViewProvider.tsx index 7db39d649..0e9718be8 100644 --- a/src/features/instance/applications/context/EditorViewProvider.tsx +++ b/src/features/instance/applications/context/EditorViewProvider.tsx @@ -21,7 +21,7 @@ import { } from '@/integrations/api/instance/applications/setComponentFile'; import { useListener } from '@/lib/events/listener'; import { setWatchedValue } from '@/lib/events/watcher'; -import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useNavigate, useSearch } from '@tanstack/react-router'; import { PropsWithChildren, useCallback, useEffect, useMemo, useState } from 'react'; import { TreeItemIndex } from 'react-complex-tree/src/types'; @@ -43,8 +43,17 @@ export function EditorViewProvider({ children }: PropsWithChildren) { /* Create our structured view from the relational API data. */ - const { data: apiComponents } = useSuspenseQuery(getComponentsQueryOptions(instanceParams)); - const mappedData = useMemo(() => calculateRootEntries(apiComponents.entries), [apiComponents]); + const { data: apiComponents } = useQuery(getComponentsQueryOptions(instanceParams)); + const mappedData = useMemo(() => { + if (!apiComponents) { + return { + rootEntries: [], + pathsRegistry: new Map(), + allEntries: new Map(), + }; + } + return calculateRootEntries(apiComponents.entries); + }, [apiComponents]); const reloadRootEntries = useCallback(async () => queryClient.fetchQuery({ @@ -63,7 +72,7 @@ export function EditorViewProvider({ children }: PropsWithChildren) { ).map(rootEntry => rootEntry.name); let defaultFocusedItem = defaultFolderExpansions[0]; let defaultSelectedItem = defaultFolderExpansions.slice(0, 1); - if (!defaultFocusedItem) { + if (!defaultFocusedItem && apiComponents) { defaultFocusedItem = newApplication; defaultSelectedItem = [newApplication]; } diff --git a/src/features/instance/applications/index.tsx b/src/features/instance/applications/index.tsx index 1df4de53f..a4721e06a 100644 --- a/src/features/instance/applications/index.tsx +++ b/src/features/instance/applications/index.tsx @@ -1,4 +1,7 @@ +import { useInstanceManagePermission } from '@/hooks/usePermissions'; import { useSessionToggler } from '@/hooks/useSessionToggler'; +import { buildAbsoluteLinkToPage } from '@/lib/urls/buildAbsoluteLinkToPage'; +import { Navigate, useParams } from '@tanstack/react-router'; import { cx } from 'class-variance-authority'; import { ApplicationsSidebar } from './components/ApplicationsSidebar'; import { ContentActions } from './components/ContentActions'; @@ -12,8 +15,14 @@ import { RedeployApplicationModal } from './modals/RedeployApplicationModal'; import { RenameFileModal } from './modals/RenameFileModal'; export function ApplicationsEditor() { + const canManage = useInstanceManagePermission(); + const params = useParams({ strict: false }); const { toggle, toggled } = useSessionToggler('ApplicationsSidebarOpened', true); + if (!canManage) { + return ; + } + return (