mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(settings): preserve tab when switching workspaces (#6704)
This commit is contained in:
+47
-1
@@ -20,6 +20,7 @@ const {
|
||||
}))
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
usePathname: () => '/workspace/workspace-denied',
|
||||
useRouter: () => ({ push: mockPush }),
|
||||
}))
|
||||
|
||||
@@ -49,7 +50,52 @@ vi.mock('@/stores/workflows/registry/store', () => ({
|
||||
) => selector({ switchToWorkspace: mockSwitchToWorkspace }),
|
||||
}))
|
||||
|
||||
import { useWorkspaceManagement } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-management'
|
||||
import {
|
||||
resolveWorkspaceSwitchHref,
|
||||
useWorkspaceManagement,
|
||||
} from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-management'
|
||||
|
||||
describe('resolveWorkspaceSwitchHref', () => {
|
||||
it('preserves the active settings section', () => {
|
||||
expect(
|
||||
resolveWorkspaceSwitchHref({
|
||||
pathname: '/workspace/workspace-a/settings/mcp',
|
||||
currentWorkspaceId: 'workspace-a',
|
||||
targetWorkspaceId: 'workspace-b',
|
||||
})
|
||||
).toBe('/workspace/workspace-b/settings/mcp')
|
||||
})
|
||||
|
||||
it('drops workspace-scoped settings detail segments', () => {
|
||||
expect(
|
||||
resolveWorkspaceSwitchHref({
|
||||
pathname: '/workspace/workspace-a/settings/secrets/credential-a',
|
||||
currentWorkspaceId: 'workspace-a',
|
||||
targetWorkspaceId: 'workspace-b',
|
||||
})
|
||||
).toBe('/workspace/workspace-b/settings/secrets')
|
||||
})
|
||||
|
||||
it('navigates to the workspace root outside settings', () => {
|
||||
expect(
|
||||
resolveWorkspaceSwitchHref({
|
||||
pathname: '/workspace/workspace-a/w/workflow-a',
|
||||
currentWorkspaceId: 'workspace-a',
|
||||
targetWorkspaceId: 'workspace-b',
|
||||
})
|
||||
).toBe('/workspace/workspace-b')
|
||||
})
|
||||
|
||||
it('fails fast when a settings pathname has no section', () => {
|
||||
expect(() =>
|
||||
resolveWorkspaceSwitchHref({
|
||||
pathname: '/workspace/workspace-a/settings/',
|
||||
currentWorkspaceId: 'workspace-a',
|
||||
targetWorkspaceId: 'workspace-b',
|
||||
})
|
||||
).toThrow('Settings pathname is missing a section')
|
||||
})
|
||||
})
|
||||
|
||||
function Harness() {
|
||||
useWorkspaceManagement({ workspaceId: 'workspace-denied', sessionUserId: 'user-1' })
|
||||
|
||||
+37
-3
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { updateUserSettingsContract } from '@/lib/api/contracts'
|
||||
import { WorkspaceRecencyStorage } from '@/lib/core/utils/browser-storage'
|
||||
@@ -25,6 +25,33 @@ interface UseWorkspaceManagementProps {
|
||||
sessionUserId?: string
|
||||
}
|
||||
|
||||
interface ResolveWorkspaceSwitchHrefParams {
|
||||
pathname: string
|
||||
currentWorkspaceId: string
|
||||
targetWorkspaceId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the active settings section across workspace switches without carrying
|
||||
* workspace-scoped detail IDs into the destination workspace.
|
||||
*/
|
||||
export function resolveWorkspaceSwitchHref({
|
||||
pathname,
|
||||
currentWorkspaceId,
|
||||
targetWorkspaceId,
|
||||
}: ResolveWorkspaceSwitchHrefParams): string {
|
||||
const targetWorkspaceHref = `/workspace/${targetWorkspaceId}`
|
||||
const settingsPrefix = `/workspace/${currentWorkspaceId}/settings/`
|
||||
if (!pathname.startsWith(settingsPrefix)) return targetWorkspaceHref
|
||||
|
||||
const [section] = pathname.slice(settingsPrefix.length).split('/')
|
||||
if (!section) {
|
||||
throw new Error(`Settings pathname is missing a section: ${pathname}`)
|
||||
}
|
||||
|
||||
return `${targetWorkspaceHref}/settings/${section}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages workspace operations including fetching, switching, creating, deleting, and leaving workspaces.
|
||||
* Handles URL synchronization and recency-based ordering. Route access is
|
||||
@@ -40,6 +67,7 @@ export function useWorkspaceManagement({
|
||||
sessionUserId,
|
||||
}: UseWorkspaceManagementProps) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const switchToWorkspace = useWorkflowRegistry((state) => state.switchToWorkspace)
|
||||
|
||||
const { data: workspaces = [], isLoading: isWorkspacesLoading } = useWorkspacesQuery(
|
||||
@@ -157,15 +185,21 @@ export function useWorkspaceManagement({
|
||||
return
|
||||
}
|
||||
|
||||
const href = resolveWorkspaceSwitchHref({
|
||||
pathname,
|
||||
currentWorkspaceId: workspaceIdRef.current,
|
||||
targetWorkspaceId: workspace.id,
|
||||
})
|
||||
|
||||
try {
|
||||
switchToWorkspace(workspace.id)
|
||||
routerRef.current?.push(`/workspace/${workspace.id}`)
|
||||
routerRef.current.push(href)
|
||||
logger.info(`Switched to workspace: ${workspace.name} (${workspace.id})`)
|
||||
} catch (error) {
|
||||
logger.error('Error switching workspace:', error)
|
||||
}
|
||||
},
|
||||
[switchToWorkspace]
|
||||
[pathname, switchToWorkspace]
|
||||
)
|
||||
|
||||
const handleCreateWorkspace = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user