diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/__tests__/sessions-sidebar.test.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/__tests__/sessions-sidebar.test.tsx index 1528564ec..09097c5a6 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/__tests__/sessions-sidebar.test.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/__tests__/sessions-sidebar.test.tsx @@ -103,7 +103,7 @@ describe('SessionsSidebar', () => { render(); expect(screen.getByText('Sessions')).toBeDefined(); expect(screen.getByText('Schedules')).toBeDefined(); - expect(screen.getByText('Sharing')).toBeDefined(); + expect(screen.getByText('Pair Prompting')).toBeDefined(); expect(screen.getByText('Access Keys')).toBeDefined(); expect(screen.getByText('Workspace Settings')).toBeDefined(); }); diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/sessions-sidebar.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/sessions-sidebar.tsx index ba40b4020..3d2dc2615 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/sessions-sidebar.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/components/sessions-sidebar.tsx @@ -130,7 +130,7 @@ export function SessionsSidebar({ href: `/projects/${projectName}/scheduled-sessions`, }, { - label: "Sharing", + label: "Pair Prompting", icon: Share2, href: `/projects/${projectName}/permissions`, }, diff --git a/components/frontend/src/components/workspace-sections/__tests__/sharing-section.test.tsx b/components/frontend/src/components/workspace-sections/__tests__/sharing-section.test.tsx index 439bb52ec..71258d106 100644 --- a/components/frontend/src/components/workspace-sections/__tests__/sharing-section.test.tsx +++ b/components/frontend/src/components/workspace-sections/__tests__/sharing-section.test.tsx @@ -60,7 +60,7 @@ describe('SharingSection', () => { it('renders permissions table with data', () => { render(); - expect(screen.getByText('Sharing')).toBeDefined(); + expect(screen.getByText('Pair Prompting')).toBeDefined(); expect(screen.getByText('developers')).toBeDefined(); expect(screen.getByText('alice')).toBeDefined(); }); @@ -87,7 +87,7 @@ describe('SharingSection', () => { render(); fireEvent.click(screen.getByText('Grant Permission')); - const nameInput = screen.getByPlaceholderText('Enter group name'); + const nameInput = screen.getByPlaceholderText('e.g., platform-team'); fireEvent.change(nameInput, { target: { value: 'new-team' } }); // Click the Grant Permission button in the dialog footer @@ -150,5 +150,6 @@ describe('SharingSection', () => { render(); expect(screen.getByText('No users or groups have access yet')).toBeDefined(); + expect(screen.queryByText('Grant First Permission')).toBeNull(); }); }); diff --git a/components/frontend/src/components/workspace-sections/sharing-section.tsx b/components/frontend/src/components/workspace-sections/sharing-section.tsx index 642e73d61..4f7e4a997 100644 --- a/components/frontend/src/components/workspace-sections/sharing-section.tsx +++ b/components/frontend/src/components/workspace-sections/sharing-section.tsx @@ -1,7 +1,7 @@ 'use client'; import { useCallback, useMemo, useState } from 'react'; -import { Users, User as UserIcon, Plus, Loader2, Trash2, Info } from 'lucide-react'; +import { Users, User as UserIcon, Plus, Loader2, Trash2, Info, HelpCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; @@ -12,6 +12,7 @@ import { Label } from '@/components/ui/label'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { DestructiveConfirmationDialog } from '@/components/confirmation-dialog'; import { useProjectPermissions, useAddProjectPermission, useRemoveProjectPermission } from '@/services/queries'; @@ -50,6 +51,8 @@ export function SharingSection({ projectName }: SharingSectionProps) { const isAdmin = userRole === 'admin' || userRole === undefined; + const subjectPlaceholder = grantForm.subjectType === 'group' ? 'e.g., platform-team' : 'e.g., jdoe@example.com'; + const handleGrant = useCallback(() => { if (!grantForm.subjectName.trim()) { setGrantError(`${grantForm.subjectType === 'group' ? 'Group' : 'User'} name is required`); @@ -113,16 +116,10 @@ export function SharingSection({ projectName }: SharingSectionProps) { () => (
-

No users or groups have access yet

- {isAdmin && ( - - )} +

No users or groups have access yet

), - [isAdmin] + [] ); return ( @@ -143,7 +140,7 @@ export function SharingSection({ projectName }: SharingSectionProps) {
- Sharing + Pair Prompting Users and groups with access to this workspace and their roles
@@ -227,9 +224,9 @@ export function SharingSection({ projectName }: SharingSectionProps) { - {/* Grant Permission Dialog */} + Grant Permission Add a user or group to this workspace with a role @@ -251,14 +248,28 @@ export function SharingSection({ projectName }: SharingSectionProps) {
- +
+ + + + + + + {grantForm.subjectType === 'group' ? ( +

Enter the LDAP or identity provider group name, e.g. platform-team, ai-engineering

+ ) : ( +

Enter the username or email, e.g. jdoe, jdoe@example.com

+ )} +
+
+
{ldapEnabled ? ( setGrantForm((prev) => ({ ...prev, subjectName: val }))} disabled={addPermissionMutation.isPending} @@ -266,7 +277,7 @@ export function SharingSection({ projectName }: SharingSectionProps) { ) : ( setGrantForm((prev) => ({ ...prev, subjectName: e.target.value }))} disabled={addPermissionMutation.isPending} @@ -327,10 +338,10 @@ export function SharingSection({ projectName }: SharingSectionProps) { )} + - {/* Revoke Permission Dialog */} { it('should visit each workspace page via direct routes', () => { // Each workspace section now has its own route (no more ?section= params) - // Sharing page — covers sharing-section.tsx + // Pair Prompting page — covers sharing-section.tsx cy.visit(`/projects/${workspaceSlug}/permissions`) - cy.get('body', { timeout: 10000 }).should('contain.text', 'Sharing') + cy.get('body', { timeout: 10000 }).should('contain.text', 'Pair Prompting') cy.wait(500) // Access Keys page — covers keys page @@ -484,7 +484,7 @@ describe('Ambient Session Management Tests', () => { it('should interact with sharing tab', () => { cy.visit(`/projects/${workspaceSlug}/permissions`) - cy.get('body', { timeout: 15000 }).should('contain.text', 'Sharing') + cy.get('body', { timeout: 15000 }).should('contain.text', 'Pair Prompting') // Look for "Grant Permission" button cy.get('body').then(($body) => { @@ -1339,11 +1339,11 @@ describe('Ambient Session Management Tests', () => { it('should interact with sharing tab Grant Permission dialog', () => { cy.visit(`/projects/${workspaceSlug}/permissions`) - cy.get('body', { timeout: 15000 }).should('contain.text', 'Sharing') + cy.get('body', { timeout: 15000 }).should('contain.text', 'Pair Prompting') - // Look for "Grant Permission" or "Grant First Permission" button + // Look for "Grant Permission" button cy.get('body').then(($body) => { - const grantBtn = $body.find('button:contains("Grant Permission"), button:contains("Grant First Permission")') + const grantBtn = $body.find('button:contains("Grant Permission")') if (grantBtn.length) { cy.wrap(grantBtn.first()).click({ force: true }) cy.wait(500) @@ -1654,7 +1654,7 @@ describe('Ambient Session Management Tests', () => { it('should click Refresh button on sharing tab', () => { cy.visit(`/projects/${workspaceSlug}/permissions`) - cy.get('body', { timeout: 15000 }).should('contain.text', 'Sharing') + cy.get('body', { timeout: 15000 }).should('contain.text', 'Pair Prompting') cy.get('body').then(($body) => { if ($body.find('button:contains("Refresh")').length) {