Skip to content

Commit 5f69ee0

Browse files
committed
fix(org): clarify membership navigation and role-aware settings
1 parent 72d067c commit 5f69ee0

31 files changed

Lines changed: 713 additions & 275 deletions

File tree

‎apps/sim/app/o/[organizationId]/components/organization-page/organization-page.tsx‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ function OrganizationPageView({
119119

120120
<div className={cn(COLUMN_CLASS, SIDEBAR_DIVIDER_PAD_ABOVE_CLASS, 'flex shrink-0 flex-col')}>
121121
<div className='flex flex-col gap-1 pt-8'>
122-
<h1 className='text-[18px] text-[var(--text-primary)]'>{title}</h1>
122+
<h1 className='text-[var(--text-primary)] text-lg'>{title}</h1>
123123
{description && <p className='text-[var(--text-muted)] text-small'>{description}</p>}
124124
</div>
125125
<div className='mt-4 flex items-center justify-between gap-2'>
@@ -159,7 +159,6 @@ function OrganizationPageView({
159159
aria-label='Search'
160160
spellCheck={false}
161161
autoComplete='off'
162-
/* Grows leftward from the chip's spot, to this and no further. */
163162
className='w-[240px]'
164163
onChange={(event) => setSearch(event.target.value)}
165164
onKeyDown={(event) => {
@@ -174,7 +173,7 @@ function OrganizationPageView({
174173
aria-label='Close search'
175174
onClick={closeSearch}
176175
>
177-
<X className='size-[13px]' />
176+
<X className='size-[14px]' />
178177
</Button>
179178
}
180179
/>

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/chats-section/chats-section.tsx‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client'
22

3-
import { memo } from 'react'
43
import { chipVariants, cn, DropdownMenuItem, Loader, OverflowText, Skeleton } from '@sim/emcn'
54
import { MoreHorizontal, Pin, Task } from '@sim/emcn/icons'
65
import Link from 'next/link'
@@ -30,13 +29,7 @@ interface ChatRowProps {
3029
onMoreClick: (e: React.MouseEvent<HTMLButtonElement>, href: string) => void
3130
}
3231

33-
const ChatRow = memo(function ChatRow({
34-
chat,
35-
isCurrentRoute,
36-
isMenuOpen,
37-
onContextMenu,
38-
onMoreClick,
39-
}: ChatRowProps) {
32+
function ChatRow({ chat, isCurrentRoute, isMenuOpen, onContextMenu, onMoreClick }: ChatRowProps) {
4033
/**
4134
* The trailing slot fits one glyph, and the dot wins over the pin: it reports
4235
* transient state (a run in progress, or an unread reply elsewhere), while pinning
@@ -89,7 +82,7 @@ const ChatRow = memo(function ChatRow({
8982
</div>
9083
</Link>
9184
)
92-
})
85+
}
9386

9487
interface ChatsSectionProps {
9588
chats: OrganizationChat[]

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/organization-header/organization-header.tsx‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import {
88
DropdownMenuItem,
99
DropdownMenuTrigger,
1010
} from '@sim/emcn'
11-
import { PanelLeft, Settings } from '@sim/emcn/icons'
12-
import Link from 'next/link'
11+
import { Building, PanelLeft, Settings } from '@sim/emcn/icons'
1312
import { IdentityTile } from '@/components/identity-tile/identity-tile'
13+
import { OrganizationMenuItems } from '@/components/organization-menu-items/organization-menu-items'
1414
import { getOrganizationSettingsHref } from '@/components/settings/navigation'
15+
import { SettingsGuardedLink } from '@/components/settings/settings-guarded-link'
16+
import { WORKSPACES_PATH } from '@/lib/navigation/paths'
1517
import type { OrganizationSurfaceOrganization } from '@/lib/organizations/surface'
1618
import { SIDEBAR_RAIL_CHIP_CLASS } from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
1719

@@ -87,11 +89,18 @@ export function OrganizationHeader({
8789
sideOffset={8}
8890
className='w-64 max-w-[calc(100vw-24px)]'
8991
>
92+
<OrganizationMenuItems currentOrganizationId={organization.id} />
9093
<DropdownMenuItem asChild>
91-
<Link href={getOrganizationSettingsHref(organization.id, 'members')}>
94+
<SettingsGuardedLink href={getOrganizationSettingsHref(organization.id, 'members')}>
9295
<Settings className='size-[14px]' />
9396
Organization settings
94-
</Link>
97+
</SettingsGuardedLink>
98+
</DropdownMenuItem>
99+
<DropdownMenuItem asChild>
100+
<SettingsGuardedLink href={WORKSPACES_PATH}>
101+
<Building className='size-[14px]' />
102+
Switch workspace
103+
</SettingsGuardedLink>
95104
</DropdownMenuItem>
96105
</DropdownMenuContent>
97106
</DropdownMenu>

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/workspaces-rail-flyout/workspaces-rail-flyout.tsx‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client'
22

3-
import { useMemo } from 'react'
43
import { useOrganizationWorkspaces } from '@/app/o/[organizationId]/components/organization-sidebar/hooks'
54
import type { FlyoutEntry } from '@/app/workspace/[workspaceId]/components/folders'
65
import { CollapsedResourceFlyout } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu'
@@ -18,17 +17,13 @@ interface WorkspacesRailFlyoutProps {
1817
export function WorkspacesRailFlyout({ organizationId }: WorkspacesRailFlyoutProps) {
1918
const { workspaces, isLoading } = useOrganizationWorkspaces(organizationId)
2019

21-
const entries = useMemo(
22-
(): FlyoutEntry[] =>
23-
workspaces.map((workspace) => ({
24-
kind: 'item',
25-
id: workspace.id,
26-
name: workspace.name,
27-
pinned: false,
28-
href: `/workspace/${workspace.id}`,
29-
})),
30-
[workspaces]
31-
)
20+
const entries: FlyoutEntry[] = workspaces.map((workspace) => ({
21+
kind: 'item',
22+
id: workspace.id,
23+
name: workspace.name,
24+
pinned: false,
25+
href: `/workspace/${workspace.id}`,
26+
}))
3227

3328
return (
3429
<CollapsedResourceFlyout

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/hooks/use-collapsed-tooltips.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ export function useCollapsedTooltips(isCollapsed: boolean): boolean {
1919
setShowCollapsedTooltips(false)
2020
}, [isCollapsed])
2121

22-
return showCollapsedTooltips
22+
return isCollapsed && showCollapsedTooltips
2323
}
Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
import { useMemo } from 'react'
2-
import { useWorkspacesQuery, type Workspace } from '@/hooks/queries/workspace'
3-
4-
/** Stable identity while the list loads, so the section's memos don't churn. */
5-
const EMPTY_WORKSPACES: Workspace[] = []
1+
import { useWorkspacesQuery } from '@/hooks/queries/workspace'
62

73
/**
84
* The organization's workspaces the viewer belongs to, for the sidebar's
95
* Workspaces section. Read from the viewer's workspace list — the same query the
106
* workspace switcher uses — narrowed to those the organization owns.
117
*/
128
export function useOrganizationWorkspaces(organizationId: string) {
13-
const { data = EMPTY_WORKSPACES, isLoading } = useWorkspacesQuery()
9+
const { data = [], isLoading } = useWorkspacesQuery()
1410

15-
const workspaces = useMemo(
16-
() => data.filter((workspace) => workspace.organizationId === organizationId),
17-
[data, organizationId]
18-
)
11+
const workspaces = data.filter((workspace) => workspace.organizationId === organizationId)
1912

2013
return { workspaces, isLoading }
2114
}

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/navigation.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ interface OrganizationNavEntry {
1111
route: OrganizationNavRoute
1212
}
1313

14+
/** The nav item whose collapsed rail chip also opens a flyout of the organization's workspaces. */
15+
export const WORKSPACES_NAV_ID = 'workspaces'
16+
1417
/**
1518
* The pinned block at the top of the organization sidebar, in display order.
1619
* Hrefs are resolved per organization by {@link buildOrganizationNavItems}.
1720
*/
18-
19-
/** The nav item whose collapsed rail chip also opens a flyout of the organization's workspaces. */
20-
export const WORKSPACES_NAV_ID = 'workspaces'
21-
2221
const ORGANIZATION_NAV_ENTRIES: readonly OrganizationNavEntry[] = [
2322
{ id: 'home', label: 'Home', icon: Home, route: 'home' },
2423
{ id: 'integrations', label: 'Integrations', icon: Integration, route: 'integrations' },

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/organization-sidebar.tsx‎

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { memo, useCallback, useEffect, useRef, useState } from 'react'
3+
import { memo, useCallback, useRef, useState } from 'react'
44
import { Chip, cn, scrollFadeAttributes, scrollFadeClass, useScrollEdges } from '@sim/emcn'
55
import { PanelLeft } from '@sim/emcn/icons'
66
import { createLogger } from '@sim/logger'
@@ -131,27 +131,23 @@ export const OrganizationSidebar = memo(function OrganizationSidebar() {
131131
[isHrefMenuOpen, closeHrefMenu, openHrefMenu]
132132
)
133133

134-
const handleHrefMenuClose = useCallback(() => {
134+
const handleHrefMenuClose = () => {
135135
closeHrefMenu()
136136
setMenuHref(null)
137-
}, [closeHrefMenu])
137+
}
138138

139-
const handleOpenInNewTab = useCallback(() => {
139+
const handleOpenInNewTab = () => {
140140
if (menuHref) window.open(menuHref, '_blank', 'noopener,noreferrer')
141-
}, [menuHref])
141+
}
142142

143-
const handleCopyLink = useCallback(async () => {
143+
const handleCopyLink = async () => {
144144
if (!menuHref) return
145145
try {
146146
await navigator.clipboard.writeText(`${window.location.origin}${menuHref}`)
147147
} catch (error) {
148148
logger.error('Failed to copy link to clipboard', { error })
149149
}
150-
}, [menuHref])
151-
152-
useEffect(() => {
153-
if (!isHrefMenuOpen) setMenuHref(null)
154-
}, [isHrefMenuOpen])
150+
}
155151

156152
const handleOpenDocs = () => {
157153
window.open(DOCS_URL, '_blank', 'noopener,noreferrer')
@@ -163,15 +159,12 @@ export const OrganizationSidebar = memo(function OrganizationSidebar() {
163159
captureEvent(posthog, 'slack_community_opened', { source: 'help_menu' })
164160
}
165161

166-
const handleEdgeKeyDown = useCallback(
167-
(e: React.KeyboardEvent) => {
168-
if (isCollapsed && (e.key === 'Enter' || e.key === ' ')) {
169-
e.preventDefault()
170-
toggleCollapsed()
171-
}
172-
},
173-
[isCollapsed, toggleCollapsed]
174-
)
162+
const handleEdgeKeyDown = (e: React.KeyboardEvent) => {
163+
if (isCollapsed && (e.key === 'Enter' || e.key === ' ')) {
164+
e.preventDefault()
165+
toggleCollapsed()
166+
}
167+
}
175168

176169
useRegisterGlobalCommands(() =>
177170
createCommands([
@@ -305,7 +298,7 @@ export const OrganizationSidebar = memo(function OrganizationSidebar() {
305298
isLoading={chatsLoading}
306299
isCollapsed={isCollapsed}
307300
pathname={pathname}
308-
menuOpenHref={menuHref}
301+
menuOpenHref={isHrefMenuOpen ? menuHref : null}
309302
onContextMenu={handleHrefContextMenu}
310303
onMoreClick={handleChatMoreClick}
311304
/>
Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client'
22

3-
import { Button, buttonVariants } from '@sim/emcn'
3+
import { Chip, ChipLink } from '@sim/emcn'
44
import { ArrowLeft, Compass, Home } from '@sim/emcn/icons'
5-
import Link from 'next/link'
65
import { useParams, useRouter } from 'next/navigation'
76
import { organizationRoutes } from '@/lib/navigation/paths'
87
import { ErrorShell } from '@/app/workspace/[workspaceId]/components/error/error'
@@ -18,14 +17,12 @@ export default function OrganizationNotFound() {
1817
description="The page you're looking for doesn't exist or has been moved."
1918
icon={<Compass className='size-[22px]' />}
2019
>
21-
<Button variant='default' size='md' onClick={() => router.back()}>
22-
<ArrowLeft className='mr-1.5 size-[14px]' />
20+
<Chip leftIcon={ArrowLeft} onClick={() => router.back()}>
2321
Go back
24-
</Button>
25-
<Link href={homeHref} className={buttonVariants({ variant: 'primary', size: 'md' })}>
26-
<Home className='mr-1.5 size-[14px]' />
22+
</Chip>
23+
<ChipLink href={homeHref} variant='primary' leftIcon={Home}>
2724
Return home
28-
</Link>
25+
</ChipLink>
2926
</ErrorShell>
3027
)
3128
}

‎apps/sim/app/o/[organizationId]/settings/[section]/settings.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export function OrganizationSettings({ section }: OrganizationSettingsProps) {
6262
{section === 'members' && (
6363
<TeamManagement
6464
organizationId={organizationId}
65+
canInviteMembers={viewer.canInviteMembers}
6566
billingHref={getOrganizationSettingsHref(organizationId, 'billing')}
6667
/>
6768
)}

0 commit comments

Comments
 (0)