From 2ea6aa9e49919d7e2011b5dd6f5e253fa1a0fc67 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 5 Sep 2026 08:46:09 -0500 Subject: [PATCH] fix(connections): simplify GitHub access details --- ui/src/pages/apps/AppDetail.test.tsx | 51 +++++++++++++++++++ .../apps/app-detail/IdentitiesSection.tsx | 44 +++++++++------- 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/ui/src/pages/apps/AppDetail.test.tsx b/ui/src/pages/apps/AppDetail.test.tsx index 5c44bd4fdc..ff14888a4c 100644 --- a/ui/src/pages/apps/AppDetail.test.tsx +++ b/ui/src/pages/apps/AppDetail.test.tsx @@ -1425,6 +1425,57 @@ describe("AppDetail", () => { expect(findButton("Revoke")).toBeUndefined(); }); + it("shows dedicated GitHub access as compact action rows and links to the agent", async () => { + mockParams.tab = "permissions"; + getConnectionMock.mockResolvedValue(connection({ + credentialPolicy: "per_agent", + authKind: "oauth", + })); + listConnectionGrantsMock.mockResolvedValue({ + connection: { id: "conn-1", uid: "conn-1" }, + grants: [organizationGrant({ + id: "grant-agent", + kind: "agent", + subjectAgentId: "agent-1", + subjectUserId: null, + isDefault: false, + providerTenant: { + github: { + userId: "123", + login: "dottabot", + installationCount: 1, + repositoryCount: 1, + repositorySelection: "selected", + installationIds: ["456"], + installationOwnerLogins: ["paperclipai"], + managementUrl: "https://github.com/settings/installations/456", + webhookHealth: "pending", + lastWebhookAt: null, + lastAccessRefreshAt: "2026-09-05T12:00:00.000Z", + }, + }, + })], + capabilities: fullCapabilities(), + currentUserId: "user-1", + members: [], + }); + + await renderAppDetail(); + + expect(container.querySelector('a[href="/agents/agent-1"]')?.textContent).toContain("Used only by Coder"); + expect(container.textContent).toContain("Repositories"); + expect(container.textContent).toContain("1 selected repositories"); + expect(container.querySelector( + 'a[href="https://github.com/settings/installations/456"]', + )?.textContent).toBe("Manage repositories on GitHub"); + expect(findButton("Refresh access")).toBeTruthy(); + expect(container.textContent).not.toContain("Installation"); + expect(container.textContent).not.toContain("Token continuity"); + expect(container.textContent).not.toContain("Webhook health"); + expect(container.textContent).not.toContain("Last event"); + expect(container.textContent).not.toContain("Last access refresh"); + }); + it("persists an empty audience as all organization members", async () => { mockParams.tab = "permissions"; getConnectionMock.mockResolvedValue(connection({ createdByUserId: "user-1" })); diff --git a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx index 25d8cdd2c3..bc055ed74c 100644 --- a/ui/src/pages/apps/app-detail/IdentitiesSection.tsx +++ b/ui/src/pages/apps/app-detail/IdentitiesSection.tsx @@ -30,8 +30,9 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { cn } from "@/lib/utils"; +import { Link } from "@/lib/router"; import { brandChipBadge } from "@/lib/status-colors"; +import { cn } from "@/lib/utils"; import { audienceUserIds, grantAccountLabel, @@ -178,7 +179,14 @@ export function IdentitiesSection({ + Used only by {dedicatedAgent.name} + + ) : "Dedicated to one agent"} actions={!agentGrant && dedicatedAgent && capabilities?.canConfigure ? ( ) : null} + +
+
+
Refresh access
+
Sync repository access from GitHub.
+
{onRefreshAccess ? (