fix: address post-merge review findings for chat org scoping (#24297)

Addresses review findings from #23827 that were added post-merge:

- Persisted attachments now store `organizationId`; mismatched orgs
pruned on restore
- Workspace selection reconciliation: stale IDs from previous orgs
dropped via derived `effectiveWorkspaceId`
- Org picker uses `permittedOrganizations()` for RBAC-aware filtering
- Org picker hidden when user belongs to only one org
- Ref-sync `useEffect` replaced with `useEffectEvent`
- `CreateWorkspace()` and `ListTemplates()` take `organizationID` and
`db` as required function parameters instead of optional struct fields —
compiler enforces them, removes scattered nil guards
- Cross-org template check in `CreateWorkspace` is now unconditional
- `ListTemplates` org-scoping filter now has test coverage
- `setupChatInfra` comment fixed; test helpers use params structs
instead of positional UUIDs
- Enterprise test documents that org admin only sees own chats (handler
hardcodes `OwnerID` — future work needs sidebar UI before lifting that
restriction)

> 🤖
This commit is contained in:
Cian Johnston
2026-04-15 11:39:05 +01:00
committed by GitHub
parent 5812f84e1c
commit 6194bd6f57
15 changed files with 767 additions and 288 deletions
@@ -27,6 +27,7 @@ export const Navbar: FC = () => {
featureVisibility.connection_log && permissions.viewAnyConnectionLog;
const canViewAIBridge =
featureVisibility.aibridge && permissions.viewAnyAIBridgeInterception;
const canCreateChat = permissions.createChat;
const uniqueLinks = new Map<string, LinkConfig>();
for (const link of appearance.support_links ?? []) {
@@ -47,6 +48,7 @@ export const Navbar: FC = () => {
canViewAuditLog={canViewAuditLog}
canViewConnectionLog={canViewConnectionLog}
canViewAIBridge={canViewAIBridge}
canCreateChat={canCreateChat}
proxyContextValue={proxyContextValue}
/>
);
@@ -35,6 +35,7 @@ const meta: Meta<typeof NavbarView> = {
canViewDeployment: true,
canViewHealth: true,
canViewOrganizations: true,
canCreateChat: true,
supportLinks: [],
},
decorators: [withDashboardProvider],
@@ -91,6 +92,18 @@ export const ForMember: Story = {
canViewDeployment: false,
canViewHealth: false,
canViewOrganizations: false,
canCreateChat: false,
},
};
export const ForMemberWithAgentsAccess: Story = {
args: {
user: MockUserMember,
canViewAuditLog: false,
canViewDeployment: false,
canViewHealth: false,
canViewOrganizations: false,
canCreateChat: true,
},
};
@@ -36,6 +36,7 @@ interface NavbarViewProps {
canViewConnectionLog: boolean;
canViewHealth: boolean;
canViewAIBridge: boolean;
canCreateChat: boolean;
proxyContextValue?: ProxyContextValue;
}
@@ -57,6 +58,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
canViewAuditLog,
canViewConnectionLog,
canViewAIBridge,
canCreateChat,
proxyContextValue,
}) => {
const isDev = buildInfo ? isDevBuild(buildInfo) : false;
@@ -78,7 +80,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
)}
</NavLink>
<NavItems className="ml-4" user={user} />
<NavItems className="ml-4" user={user} canCreateChat={canCreateChat} />
{isPreRelease && buildInfo?.version && (
<a
@@ -165,9 +167,10 @@ export const NavbarView: FC<NavbarViewProps> = ({
interface NavItemsProps {
className?: string;
user: TypesGen.User;
canCreateChat: boolean;
}
const NavItems: FC<NavItemsProps> = ({ className, user }) => {
const NavItems: FC<NavItemsProps> = ({ className, user, canCreateChat }) => {
const location = useLocation();
return (
@@ -192,7 +195,7 @@ const NavItems: FC<NavItemsProps> = ({ className, user }) => {
Templates
</NavLink>
<TasksNavItem user={user} />
<AgentsNavItem />
<AgentsNavItem canCreateChat={canCreateChat} />
</nav>
);
};
@@ -257,11 +260,12 @@ function idleTasksLabel(count: number) {
return `You have ${count} ${count === 1 ? "task" : "tasks"} waiting for input`;
}
const AgentsNavItem: FC = () => {
const AgentsNavItem: FC<{ canCreateChat: boolean }> = ({ canCreateChat }) => {
const { experiments, buildInfo } = useDashboard();
const canSeeAgents = experiments.includes("agents") || isDevBuild(buildInfo);
const experimentEnabled =
experiments.includes("agents") || isDevBuild(buildInfo);
if (!canSeeAgents) {
if (!experimentEnabled || !canCreateChat) {
return null;
}