11import { RichEmptyState , Skeleton } from "@corbits/react-ui" ;
22import { useQuery } from "@tanstack/react-query" ;
33
4- import { IdentityAvatar , WorkbenchAvatar } from "@/chat/avatar" ;
5- import type { WorkbenchParticipant } from "@/chat/threads-api" ;
4+ import { WorkbenchAvatar } from "@/chat/avatar" ;
5+ import { listChatAgents , type WorkbenchParticipant } from "@/chat/threads-api" ;
6+ import { CaretRight } from "@/lib/icons" ;
67import { Link } from "../navigation" ;
78import { tenantKeys } from "../query-client" ;
89import { principalLabel } from "../settings/identity" ;
910import { listPrincipals } from "../settings/tenancy-api" ;
11+ import { useWorkerRole } from "../worker-role-query" ;
12+ import { useWorkerStatus } from "../worker-status" ;
13+ import "./drawer.css" ;
1014
11- /** Humans are the bench tenant's user principals; agents are the bench's
12- * workers, already listed as participants. */
15+ function capitalize ( text : string ) : string {
16+ return text . charAt ( 0 ) . toUpperCase ( ) + text . slice ( 1 ) ;
17+ }
18+
19+ function WorkerRow ( {
20+ workbenchTenantId,
21+ participant,
22+ } : {
23+ readonly workbenchTenantId : string ;
24+ readonly participant : WorkbenchParticipant ;
25+ } ) {
26+ const agents = useQuery ( {
27+ queryKey : tenantKeys . agents ( workbenchTenantId ) ,
28+ queryFn : ( ) => listChatAgents ( workbenchTenantId ) ,
29+ } ) ;
30+ const agent = agents . data ?. find ( ( a ) => a . id === participant . id ) ;
31+ const status = useWorkerStatus ( workbenchTenantId , participant . id ) ;
32+ const body = (
33+ < >
34+ < WorkbenchAvatar kind = "worker" name = { participant . name } size = "md" status = { status . tone } />
35+ < span className = "drawer-list-text" >
36+ < b > { participant . name } </ b >
37+ { agent === undefined ? null : (
38+ < WorkerRoleLine workbenchTenantId = { workbenchTenantId } agent = { agent } />
39+ ) }
40+ </ span >
41+ < CaretRight size = { 14 } aria-hidden = "true" />
42+ </ >
43+ ) ;
44+ return (
45+ < li >
46+ < Link className = "drawer-list-link" to = { `/workers/${ encodeURIComponent ( participant . id ) } ` } >
47+ { body }
48+ </ Link >
49+ </ li >
50+ ) ;
51+ }
52+
53+ function WorkerRoleLine ( {
54+ workbenchTenantId,
55+ agent,
56+ } : {
57+ readonly workbenchTenantId : string ;
58+ readonly agent : Parameters < typeof useWorkerRole > [ 1 ] ;
59+ } ) {
60+ return < span > { useWorkerRole ( workbenchTenantId , agent ) } </ span > ;
61+ }
62+
63+ /** Humans are the bench tenant's user principals; workers are the bench's
64+ * agents, already listed as participants. */
1365export function MembersTab ( {
1466 workbenchTenantId,
1567 participants,
1668 loading,
1769} : {
1870 readonly workbenchTenantId : string ;
19- /** True until the roster resolves; an empty list then isn't "no agents ". */
71+ /** True until the roster resolves; an empty list then isn't "no workers ". */
2072 readonly loading : boolean ;
2173 readonly participants : readonly WorkbenchParticipant [ ] ;
2274} ) {
@@ -26,13 +78,14 @@ export function MembersTab({
2678 queryKey : [ ...tenantKeys . principals ( workbenchTenantId ) , "members" ] ,
2779 queryFn : async ( ) => ( await listPrincipals ( workbenchTenantId ) ) . filter ( ( p ) => p . kind === "user" ) ,
2880 } ) ;
29- const agents = participants . filter ( ( p ) => p . kind === "agent" ) ;
81+ const workers = participants . filter ( ( p ) => p . kind === "agent" ) ;
3082
3183 return (
3284 < div >
3385 < section className = "drawer-sec" >
3486 < div className = "drawer-sec-head" >
3587 < h3 > People</ h3 >
88+ < Link to = "/settings/people" > Invite</ Link >
3689 </ div >
3790 { people . isPending ? < Skeleton className = "h-12 w-full" /> : null }
3891 { people . isError ? (
@@ -47,16 +100,14 @@ export function MembersTab({
47100 ) : null }
48101 < ul className = "drawer-list" >
49102 { ( people . data ?? [ ] ) . map ( ( person ) => {
50- const { label } = principalLabel ( person . displayName ) ;
51- const detail = [ person . email , person . roles . map ( ( r ) => r . name ) . join ( ", " ) ]
52- . filter ( ( s ) : s is string => s !== undefined && s !== "" )
53- . join ( " · " ) ;
103+ const name = capitalize ( principalLabel ( person . displayName ) . label ) ;
104+ const role = person . roles . map ( ( r ) => capitalize ( r . name ) ) . join ( ", " ) ;
54105 return (
55106 < li key = { person . id } >
56- < IdentityAvatar kind = "person" name = { label } principalId = { person . id } />
107+ < WorkbenchAvatar kind = "person" name = { name } size = "md" />
57108 < span className = "drawer-list-text" >
58- < b > { label } </ b >
59- < span > { detail } </ span >
109+ < b > { name } </ b >
110+ { role === "" ? null : < span > { role } </ span > }
60111 </ span >
61112 </ li >
62113 ) ;
@@ -65,39 +116,17 @@ export function MembersTab({
65116 </ section >
66117 < section className = "drawer-sec" >
67118 < div className = "drawer-sec-head" >
68- < h3 > Agents</ h3 >
119+ < h3 > Workers</ h3 >
120+ < Link to = "/workers" > Add</ Link >
69121 </ div >
70122 { loading ? < Skeleton className = "h-12 w-full" /> : null }
71- { ! loading && agents . length === 0 ? (
72- < p className = "workbench-info-empty-note" > No agents yet.</ p >
123+ { ! loading && workers . length === 0 ? (
124+ < p className = "workbench-info-empty-note" > No workers yet.</ p >
73125 ) : null }
74126 < ul className = "drawer-list" >
75- { agents . map ( ( agent ) => {
76- const workerId = agent . assetName === undefined ? undefined : agent . id ;
77- const body = (
78- < >
79- < WorkbenchAvatar kind = "worker" name = { agent . name } size = "md" />
80- < span className = "drawer-list-text" >
81- < b > { agent . name } </ b >
82- < span > { agent . address === "" ? "Starting" : "Running" } </ span >
83- </ span >
84- </ >
85- ) ;
86- return (
87- < li key = { agent . id } >
88- { workerId === undefined ? (
89- body
90- ) : (
91- < Link
92- className = "drawer-list-link"
93- to = { `/workers/${ encodeURIComponent ( workerId ) } ` }
94- >
95- { body }
96- </ Link >
97- ) }
98- </ li >
99- ) ;
100- } ) }
127+ { workers . map ( ( worker ) => (
128+ < WorkerRow key = { worker . id } workbenchTenantId = { workbenchTenantId } participant = { worker } />
129+ ) ) }
101130 </ ul >
102131 </ section >
103132 </ div >
0 commit comments