Skip to content

Commit b9b9349

Browse files
fix(navigation): restore organization return from shared workspaces
1 parent 293e883 commit b9b9349

6 files changed

Lines changed: 36 additions & 53 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.test.tsx‎

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@ import { createRoot, type Root } from 'react-dom/client'
66
import { renderToString } from 'react-dom/server'
77
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
88

9-
const { mockNavigateToSettings, mockWorkspacePermissions, hostContext } = vi.hoisted(() => ({
9+
const { mockNavigateToSettings, mockWorkspacePermissions, organizationList } = vi.hoisted(() => ({
1010
mockNavigateToSettings: vi.fn(),
11-
hostContext: {
12-
hostOrganizationId: null as string | null,
13-
viewer: { isHostOrganizationMember: false },
14-
features: { organizationSearch: false as boolean | undefined },
15-
},
11+
organizationList: { data: [] as { id: string }[] | undefined },
1612
mockWorkspacePermissions: { canAdmin: true, canEdit: true, canRead: true },
1713
}))
1814

19-
vi.mock('@/app/workspace/[workspaceId]/providers/workspace-host-provider', () => ({
20-
useWorkspaceHostContext: () => hostContext,
15+
vi.mock('@/hooks/queries/organization', () => ({
16+
useOrganizationList: () => organizationList,
2117
}))
2218

2319
const onWorkspaceSwitch = vi.fn()
@@ -29,7 +25,6 @@ vi.mock('next/navigation', () => ({
2925
useRouter: () => ({ push: vi.fn() }),
3026
usePathname: () => '/workspace/ws-emir/home',
3127
}))
32-
vi.mock('@/lib/auth/auth-client', () => ({ useActiveOrganization: () => ({ data: null }) }))
3328
vi.mock('@/hooks/use-settings-navigation', () => ({
3429
useSettingsNavigation: () => ({
3530
navigateToSettings: mockNavigateToSettings,
@@ -170,9 +165,7 @@ function typeInto(input: HTMLInputElement, value: string) {
170165

171166
beforeEach(() => {
172167
vi.clearAllMocks()
173-
hostContext.hostOrganizationId = null
174-
hostContext.viewer.isHostOrganizationMember = false
175-
hostContext.features.organizationSearch = false
168+
organizationList.data = []
176169
Object.assign(mockWorkspacePermissions, { canAdmin: true, canEdit: true, canRead: true })
177170
// jsdom implements neither; the component scrolls the active row into view.
178171
Element.prototype.scrollIntoView = vi.fn()
@@ -187,8 +180,7 @@ describe('WorkspaceHeader workspace switcher highlight', () => {
187180
it.each([null, 'organization'])(
188181
'keeps access requests out of the workspace switcher (%s)',
189182
(organizationId) => {
190-
hostContext.hostOrganizationId = organizationId
191-
render()
183+
render({ workspaces: WORKSPACES.map((workspace) => ({ ...workspace, organizationId })) })
192184
expect(document.body).not.toHaveTextContent('My access requests')
193185
expect(document.body).not.toHaveTextContent('Review access requests')
194186
}
@@ -414,28 +406,24 @@ describe('WorkspaceHeader workspace switcher highlight', () => {
414406
})
415407

416408
describe('WorkspaceHeader context navigation', () => {
417-
it('links to the current host organization for enrolled members', () => {
418-
hostContext.hostOrganizationId = 'host-org'
419-
hostContext.viewer.isHostOrganizationMember = true
420-
hostContext.features.organizationSearch = true
421-
render()
422-
expect(document.querySelector('a[href="/o/host-org"]')).toHaveTextContent(
423-
'Back to organization'
424-
)
425-
})
409+
it.each([null, 'another-organization', 'viewer-organization'])(
410+
'links to the viewer organization landing independently of workspace host %s',
411+
(organizationId) => {
412+
organizationList.data = [{ id: 'viewer-organization' }]
413+
render({ workspaces: WORKSPACES.map((workspace) => ({ ...workspace, organizationId })) })
414+
expect(document.querySelector('a[href="/o"]')).toHaveTextContent('Back to organization')
415+
expect(document.querySelector('a[href^="/o/"]')).toBeNull()
416+
}
417+
)
426418

427-
it.each([
428-
{ org: null, member: true, enabled: true },
429-
{ org: 'host-org', member: false, enabled: true },
430-
{ org: 'host-org', member: true, enabled: false },
431-
{ org: 'host-org', member: true, enabled: undefined },
432-
])('hides inaccessible organization navigation: %j', ({ org, member, enabled }) => {
433-
hostContext.hostOrganizationId = org
434-
hostContext.viewer.isHostOrganizationMember = member
435-
hostContext.features.organizationSearch = enabled
436-
render()
437-
expect(document.querySelector('a[href^="/o/"]')).toBeNull()
438-
})
419+
it.each([[], undefined])(
420+
'hides organization navigation without loaded memberships: %j',
421+
(data) => {
422+
organizationList.data = data
423+
render()
424+
expect(document.body).not.toHaveTextContent('Back to organization')
425+
}
426+
)
439427

440428
it('keeps settings in the profile menu instead of duplicating it in the switcher', () => {
441429
render()

‎apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ import { WorkspaceContextMenu } from '@/components/workspaces/workspace-context-
3434
import { useDeploymentShape } from '@/lib/core/config/deployment-shape'
3535
import { WORKSPACE_SEARCH_THRESHOLD } from '@/lib/workspaces/constants'
3636
import { getWorkspaceInitial } from '@/lib/workspaces/initials'
37-
import { getWorkspaceOrganizationHref } from '@/lib/workspaces/organization-navigation'
3837
import { InviteModal } from '@/app/workspace/[workspaceId]/components/invite-modal'
39-
import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider'
4038
import { useWorkspacePermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
4139
import { SidebarRenameRow } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-rename-row'
4240
import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/delete-modal/delete-modal'
@@ -50,6 +48,7 @@ import {
5048
type WorkspaceCreationPolicy,
5149
workspaceKeys,
5250
} from '@/hooks/queries/workspace'
51+
import { useOrganizationNavigationHref } from '@/hooks/use-organization-navigation'
5352
import { usePermissionConfig } from '@/hooks/use-permission-config'
5453
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
5554

@@ -273,8 +272,7 @@ function WorkspaceHeaderImpl({
273272

274273
const { navigateToSettings } = useSettingsNavigation()
275274
const queryClient = useQueryClient()
276-
const hostContext = useWorkspaceHostContext()
277-
const organizationHref = getWorkspaceOrganizationHref(hostContext)
275+
const organizationHref = useOrganizationNavigationHref()
278276

279277
const activeWorkspaceFull = workspaces.find((w) => w.id === workspaceId) || null
280278
const isWorkspaceReady = !isWorkspacesLoading && activeWorkspaceFull !== null

‎apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ import { DOCS_URL, SLACK_COMMUNITY_URL } from '@/lib/help-links'
4949
import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/mothership/resource-types'
5050
import { captureEvent } from '@/lib/posthog/client'
5151
import { LOGO_ACCEPT_ATTRIBUTE } from '@/lib/uploads/client/logo-file'
52-
import { getWorkspaceOrganizationHref } from '@/lib/workspaces/organization-navigation'
5352
import { useSidebarChrome } from '@/app/workspace/[workspaceId]/components/workspace-chrome'
5453
import { CONNECT_MODE } from '@/app/workspace/[workspaceId]/integrations/connect-route'
5554
import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
@@ -137,6 +136,7 @@ import { useUpdateWorkflow } from '@/hooks/queries/workflows'
137136
import type { Workspace } from '@/hooks/queries/workspace'
138137
import { useContextMenu } from '@/hooks/use-context-menu'
139138
import { useMothershipChatEvents } from '@/hooks/use-mothership-chat-events'
139+
import { useOrganizationNavigationHref } from '@/hooks/use-organization-navigation'
140140
import { usePermissionConfig } from '@/hooks/use-permission-config'
141141
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
142142
import { useFolderStore } from '@/stores/folders/store'
@@ -820,7 +820,7 @@ export const Sidebar = memo(function Sidebar() {
820820
onNavigate: () => handleOpenSettings(id),
821821
}))
822822

823-
const organizationHref = getWorkspaceOrganizationHref(hostContext)
823+
const organizationHref = useOrganizationNavigationHref()
824824
if (organizationHref) {
825825
profileNavigationLinks.push({
826826
label: 'Organization',
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ORGANIZATIONS_PATH } from '@/lib/navigation/paths'
2+
import { useOrganizationList } from '@/hooks/queries/organization'
3+
4+
/** Account navigation follows the viewer's memberships, independently of the workspace host. */
5+
export function useOrganizationNavigationHref(): string | null {
6+
const { data: organizations } = useOrganizationList()
7+
return organizations?.length ? ORGANIZATIONS_PATH : null
8+
}

‎apps/sim/lib/navigation/paths.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const WORKSPACES_PATH = '/workspace'
2323
export const WORKSPACE_SETTINGS_PATH = `${WORKSPACES_PATH}?redirect=settings`
2424

2525
/** Root of the organization surface; `/o` alone resolves like {@link APP_ENTRY_PATH}. */
26-
const ORGANIZATIONS_PATH = '/o'
26+
export const ORGANIZATIONS_PATH = '/o'
2727

2828
/**
2929
* Every destination under one organization's surface, built from one place so the

‎apps/sim/lib/workspaces/organization-navigation.ts‎

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)