From c1798a4642852db0c02f91a8ff7a42eb9c949434 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 31 Dec 2025 10:50:42 -0500 Subject: [PATCH] Improve agent access page UI - Remove disabled 'Public (Coming Soon)' visibility option - Move permissions reference table into a modal dialog - Display all org members with inherited access when visibility is 'organization' - Show access source (Team Owner/Admin, Team Member, Direct) in members table - Fix UserSelector dropdown focus bug by auto-focusing search input on open --- .../[agent]/access/agent-access-client.tsx | 28 ++- .../[agent]/access/members-table.tsx | 77 ++++++- .../[agent]/access/permissions-reference.tsx | 199 ++++++++++-------- .../[agent]/access/visibility-section.tsx | 20 -- internal/site/components/user-selector.tsx | 14 +- 5 files changed, 212 insertions(+), 126 deletions(-) diff --git a/internal/site/app/(app)/[organization]/[agent]/access/agent-access-client.tsx b/internal/site/app/(app)/[organization]/[agent]/access/agent-access-client.tsx index 253b4f3e..55ee1b97 100644 --- a/internal/site/app/(app)/[organization]/[agent]/access/agent-access-client.tsx +++ b/internal/site/app/(app)/[organization]/[agent]/access/agent-access-client.tsx @@ -8,7 +8,7 @@ import { useMemo, useState } from "react"; import useSWR from "swr"; import { AddMemberModal } from "./add-member-modal"; import { MembersTable } from "./members-table"; -import { PermissionsReference } from "./permissions-reference"; +import { PermissionsReferenceModal } from "./permissions-reference"; import { VisibilitySection } from "./visibility-section"; interface AgentAccessClientProps { @@ -77,13 +77,20 @@ export function AgentAccessClient({ orgAdminsAndOwners.map((m) => m.user.id) ); + // Get regular org members (not admins/owners) - they get read access when visibility is organization + const regularOrgMembers = + orgMembers?.filter((m) => m.role === "member") || []; + // Filter out org admins and owners from the member list since they always have access const explicitMembers = (members || []).filter( (member) => !member.user_id || !orgAdminsAndOwnersIds.has(member.user_id) ); - // Total count includes both explicit and implicit members - const totalMembersCount = explicitMembers.length + orgAdminsAndOwners.length; + // Total count depends on visibility + const totalMembersCount = + agentVisibility === "organization" + ? (orgMembers?.length || 0) // When team visible, all org members have access + : explicitMembers.length + orgAdminsAndOwners.length; return ( @@ -99,8 +106,6 @@ export function AgentAccessClient({ organizationName={organizationName} /> - -
@@ -111,15 +116,20 @@ export function AgentAccessClient({ to this agent

- +
+ + +
void; onUpdatePermission: ( @@ -19,11 +21,25 @@ interface MembersTableProps { export function MembersTable({ explicitMembers, implicitMembers, + regularOrgMembers, + agentVisibility, currentUserId, onDelete, onUpdatePermission, }: MembersTableProps) { - const totalCount = explicitMembers.length + implicitMembers.length; + // Build a set of user IDs that have explicit grants (to exclude from inherited members) + const explicitUserIds = new Set( + explicitMembers.map((m) => m.user_id).filter(Boolean) + ); + + // Filter regular org members to exclude those with explicit grants + const inheritedTeamMembers = + agentVisibility === "organization" + ? regularOrgMembers.filter((m) => !explicitUserIds.has(m.user.id)) + : []; + + const totalCount = + explicitMembers.length + implicitMembers.length + inheritedTeamMembers.length; return (
@@ -69,6 +85,13 @@ export function MembersTable({ orgMember={orgMember} /> ))} + {/* Inherited team members (when visibility is organization) */} + {inheritedTeamMembers.map((orgMember) => ( + + ))} {/* Explicit members */} {explicitMembers.map((member) => (
- {orgMember.role} + Team {orgMember.role} +
+ + + + — + + + + ); +} + +// Inherited team member row for regular org members when visibility is "organization" +function InheritedTeamMemberRow({ + orgMember, +}: { + orgMember: OrganizationMember; +}) { + const displayName = + orgMember.user.display_name || orgMember.user.username || "Unknown"; + + return ( + + +
+ +
+
+ {displayName} +
+
+ @{orgMember.user.username} +
+
+
+ + + + Read + + + +
+ + Team member
diff --git a/internal/site/app/(app)/[organization]/[agent]/access/permissions-reference.tsx b/internal/site/app/(app)/[organization]/[agent]/access/permissions-reference.tsx index 0a8d28f5..7974d74f 100644 --- a/internal/site/app/(app)/[organization]/[agent]/access/permissions-reference.tsx +++ b/internal/site/app/(app)/[organization]/[agent]/access/permissions-reference.tsx @@ -1,7 +1,14 @@ "use client"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Check, X } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Check, HelpCircle, X } from "lucide-react"; const PERMISSION_FEATURES = [ { @@ -72,107 +79,115 @@ function PermissionIcon({ allowed }: { allowed: boolean }) { ); } -export function PermissionsReference() { +export function PermissionsReferenceModal() { return ( - - - Permission levels -

- Understand what each permission level allows -

-
- -
-

- Note: Organization admins and owners automatically - have admin permission on all agents in this organization. -

-
-
- {/* Permission headers */} -
-
- Feature -
-
Read
-
Write
-
Admin
+ + + + + + + Permission levels + + Understand what each permission level allows + + +
+
+

+ Note: Organization admins and owners automatically + have admin permission on all agents in this organization. +

- - {/* Permission categories */} - {PERMISSION_FEATURES.map((category, categoryIndex) => ( -
-

- {category.category} -

-
- {category.features.map((feature, featureIndex) => ( -
-
- {feature.name} -
-
- -
-
- -
-
- -
-
- ))} +
+ {/* Permission headers */} +
+
+ Feature
+
Read
+
Write
+
Admin
- ))} - {/* Summary section */} -
-
-
- - R - -
-
-

Read

-

- Perfect for team members who need to use the agent -

-
-
-
-
- - W - + {/* Permission categories */} + {PERMISSION_FEATURES.map((category, categoryIndex) => ( +
+

+ {category.category} +

+
+ {category.features.map((feature, featureIndex) => ( +
+
+ {feature.name} +
+
+ +
+
+ +
+
+ +
+
+ ))} +
-
-

Write

-

- For developers who build and debug agents -

+ ))} + + {/* Summary section */} +
+
+
+ + R + +
+
+

Read

+

+ Perfect for team members who need to use the agent +

+
-
-
-
- - A - +
+
+ + W + +
+
+

Write

+

+ For developers who build and debug agents +

+
-
-

Admin

-

- Full control for managing the agent and its access -

+
+
+ + A + +
+
+

Admin

+

+ Full control for managing the agent and its access +

+
- - + +
); } diff --git a/internal/site/app/(app)/[organization]/[agent]/access/visibility-section.tsx b/internal/site/app/(app)/[organization]/[agent]/access/visibility-section.tsx index 70b1cec7..2c04faf3 100644 --- a/internal/site/app/(app)/[organization]/[agent]/access/visibility-section.tsx +++ b/internal/site/app/(app)/[organization]/[agent]/access/visibility-section.tsx @@ -151,26 +151,6 @@ export function VisibilitySection({

-