agent-relay-server 0.123.0 → 0.123.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/docs/openapi.json +1 -1
- package/package.json +3 -2
- package/public/assets/MessageBubble-NCggGKTA.js +2 -0
- package/public/assets/{MessageBubble-BTeFExjj.js.map → MessageBubble-NCggGKTA.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-jU1DXg7w.js → PlanGraphPanel-CSdszmGk.js} +2 -2
- package/public/assets/{PlanGraphPanel-jU1DXg7w.js.map → PlanGraphPanel-CSdszmGk.js.map} +1 -1
- package/public/assets/{activity-DUig2iYW.js → activity-Cyp2nolX.js} +2 -2
- package/public/assets/{activity-DUig2iYW.js.map → activity-Cyp2nolX.js.map} +1 -1
- package/public/assets/{agent-profiles-D-oFmb6S.js → agent-profiles-D5Izg0Vn.js} +2 -2
- package/public/assets/{agent-profiles-D-oFmb6S.js.map → agent-profiles-D5Izg0Vn.js.map} +1 -1
- package/public/assets/{agent-type-icon-Dan2l3Tm.js → agent-type-icon-BxUopALO.js} +2 -2
- package/public/assets/{agent-type-icon-Dan2l3Tm.js.map → agent-type-icon-BxUopALO.js.map} +1 -1
- package/public/assets/agents-DIOxuJ6M.js +2 -0
- package/public/assets/{agents-BkC6mcTh.js.map → agents-DIOxuJ6M.js.map} +1 -1
- package/public/assets/{analytics-C3Dlmqe9.js → analytics-DqAuUAwX.js} +3 -3
- package/public/assets/{analytics-C3Dlmqe9.js.map → analytics-DqAuUAwX.js.map} +1 -1
- package/public/assets/{automation-AeMZ70WQ.js → automation-Bn6mOLAR.js} +2 -2
- package/public/assets/{automation-AeMZ70WQ.js.map → automation-Bn6mOLAR.js.map} +1 -1
- package/public/assets/{badge-DCIT3Irj.js → badge-XEru6wto.js} +2 -2
- package/public/assets/{badge-DCIT3Irj.js.map → badge-XEru6wto.js.map} +1 -1
- package/public/assets/branch-state-badge-Mf0Ow77K.js +2 -0
- package/public/assets/{branch-state-badge-CNW10MTC.js.map → branch-state-badge-Mf0Ow77K.js.map} +1 -1
- package/public/assets/{button-CzdTkRIf.js → button-C3CQSr4p.js} +2 -2
- package/public/assets/{button-CzdTkRIf.js.map → button-C3CQSr4p.js.map} +1 -1
- package/public/assets/card-C5XTIPvq.js +2 -0
- package/public/assets/{card-C3BGuVC6.js.map → card-C5XTIPvq.js.map} +1 -1
- package/public/assets/{channels-Bc_mEd56.js → channels-DVRkVeky.js} +2 -2
- package/public/assets/{channels-Bc_mEd56.js.map → channels-DVRkVeky.js.map} +1 -1
- package/public/assets/chat-eUaPz399.js +2 -0
- package/public/assets/{chat-BCNCuTgw.js.map → chat-eUaPz399.js.map} +1 -1
- package/public/assets/{connectors-h0d5Mc6e.js → connectors-Cqubkw9C.js} +2 -2
- package/public/assets/{connectors-h0d5Mc6e.js.map → connectors-Cqubkw9C.js.map} +1 -1
- package/public/assets/copy-button-Bil2CjzT.js +2 -0
- package/public/assets/{copy-button-B3o1jpLu.js.map → copy-button-Bil2CjzT.js.map} +1 -1
- package/public/assets/{dist-OUPTEP6q.js → dist-4VwhnLtj.js} +2 -2
- package/public/assets/{dist-OUPTEP6q.js.map → dist-4VwhnLtj.js.map} +1 -1
- package/public/assets/{dist-BIjlN-xB.js → dist-B8tD9ZcR.js} +2 -2
- package/public/assets/{dist-BIjlN-xB.js.map → dist-B8tD9ZcR.js.map} +1 -1
- package/public/assets/{dist-BInckSJC.js → dist-BJaV2BD4.js} +2 -2
- package/public/assets/{dist-BInckSJC.js.map → dist-BJaV2BD4.js.map} +1 -1
- package/public/assets/{dist-B-T7lg0a.js → dist-C5z05Vkz.js} +2 -2
- package/public/assets/{dist-B-T7lg0a.js.map → dist-C5z05Vkz.js.map} +1 -1
- package/public/assets/dist-CFtFIhu3.js +2 -0
- package/public/assets/{dist-DT-OV6sQ.js.map → dist-CFtFIhu3.js.map} +1 -1
- package/public/assets/{dist-C7aTlosb.js → dist-CGaHpxnN.js} +2 -2
- package/public/assets/{dist-C7aTlosb.js.map → dist-CGaHpxnN.js.map} +1 -1
- package/public/assets/dist-CjyfQThq.js +2 -0
- package/public/assets/{dist-DGfp5dLz.js.map → dist-CjyfQThq.js.map} +1 -1
- package/public/assets/dist-CrUl2aQp.js +2 -0
- package/public/assets/{dist-B6woQ9Tb.js.map → dist-CrUl2aQp.js.map} +1 -1
- package/public/assets/{dist-B3e4B9pg.js → dist-DKmTXfpP.js} +2 -2
- package/public/assets/{dist-B3e4B9pg.js.map → dist-DKmTXfpP.js.map} +1 -1
- package/public/assets/{dist-B9G83DXv.js → dist-V3wu3BHw.js} +2 -2
- package/public/assets/{dist-B9G83DXv.js.map → dist-V3wu3BHw.js.map} +1 -1
- package/public/assets/{dist-BlzVGRhh.js → dist-r8wyUTkK.js} +2 -2
- package/public/assets/{dist-BlzVGRhh.js.map → dist-r8wyUTkK.js.map} +1 -1
- package/public/assets/{es2015-CnKXYKpf.js → es2015-5DTJe35n.js} +2 -2
- package/public/assets/{es2015-CnKXYKpf.js.map → es2015-5DTJe35n.js.map} +1 -1
- package/public/assets/formatted-body-CvrQ2D1J.js +3 -0
- package/public/assets/{formatted-body-CVr3Iev9.js.map → formatted-body-CvrQ2D1J.js.map} +1 -1
- package/public/assets/{formatted-body-impl-Cq5SRGpm.js → formatted-body-impl-BJ7jdbpR.js} +2 -2
- package/public/assets/{formatted-body-impl-Cq5SRGpm.js.map → formatted-body-impl-BJ7jdbpR.js.map} +1 -1
- package/public/assets/index-DnUo0Zqg.js +25 -0
- package/public/assets/index-DnUo0Zqg.js.map +1 -0
- package/public/assets/{input-B6Q0NWCS.js → input-CXMbJ848.js} +2 -2
- package/public/assets/{input-B6Q0NWCS.js.map → input-CXMbJ848.js.map} +1 -1
- package/public/assets/{insights-C8lIBXg3.js → insights-CQGH5LhD.js} +2 -2
- package/public/assets/{insights-C8lIBXg3.js.map → insights-CQGH5LhD.js.map} +1 -1
- package/public/assets/{integrations-7OFsJtY7.js → integrations-9G5TJnnU.js} +2 -2
- package/public/assets/{integrations-7OFsJtY7.js.map → integrations-9G5TJnnU.js.map} +1 -1
- package/public/assets/{lib-BHsWPMKB.js → lib-BcUW1l-W.js} +2 -2
- package/public/assets/{lib-BHsWPMKB.js.map → lib-BcUW1l-W.js.map} +1 -1
- package/public/assets/lucide-react-CoU_Xts2.js +10 -0
- package/public/assets/{lucide-react-DuhlF2Sj.js.map → lucide-react-CoU_Xts2.js.map} +1 -1
- package/public/assets/maintenance-7c7UvpAt.js +2 -0
- package/public/assets/{maintenance-HDpg20OY.js.map → maintenance-7c7UvpAt.js.map} +1 -1
- package/public/assets/{managed-agents-muCIMxng.js → managed-agents-yh4HPO5a.js} +4 -4
- package/public/assets/{managed-agents-muCIMxng.js.map → managed-agents-yh4HPO5a.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-Cx7pCfA6.js → markdown-preview-impl-BuOn5-Uc.js} +2 -2
- package/public/assets/{markdown-preview-impl-Cx7pCfA6.js.map → markdown-preview-impl-BuOn5-Uc.js.map} +1 -1
- package/public/assets/memory-Qv0JN8CE.js +2 -0
- package/public/assets/{memory-CTO1FfJ-.js.map → memory-Qv0JN8CE.js.map} +1 -1
- package/public/assets/messages-CgjHJmc2.js +2 -0
- package/public/assets/{messages-RWSXG2UM.js.map → messages-CgjHJmc2.js.map} +1 -1
- package/public/assets/{orchestrators-5aiYw3yx.js → orchestrators-CgL4CFzb.js} +2 -2
- package/public/assets/{orchestrators-5aiYw3yx.js.map → orchestrators-CgL4CFzb.js.map} +1 -1
- package/public/assets/{overview-Du6XLt5l.js → overview-Bri04eYE.js} +2 -2
- package/public/assets/{overview-Du6XLt5l.js.map → overview-Bri04eYE.js.map} +1 -1
- package/public/assets/{pairs-BDRnlEta.js → pairs-C78LwUH2.js} +2 -2
- package/public/assets/{pairs-BDRnlEta.js.map → pairs-C78LwUH2.js.map} +1 -1
- package/public/assets/{projects-DwsWE9es.js → projects-CquHTMo1.js} +2 -2
- package/public/assets/{projects-DwsWE9es.js.map → projects-CquHTMo1.js.map} +1 -1
- package/public/assets/prompt-settings-DDEkMqPZ.js +2 -0
- package/public/assets/{prompt-settings-CaJX_LbA.js.map → prompt-settings-DDEkMqPZ.js.map} +1 -1
- package/public/assets/{react-dom-DWBmcawP.js → react-dom-BQICbUuF.js} +2 -2
- package/public/assets/{react-dom-DWBmcawP.js.map → react-dom-BQICbUuF.js.map} +1 -1
- package/public/assets/registry-5KhP5FE5.js +2 -0
- package/public/assets/registry-5KhP5FE5.js.map +1 -0
- package/public/assets/{security-BpVrdYhJ.js → security-wvnRKFvm.js} +2 -2
- package/public/assets/{security-BpVrdYhJ.js.map → security-wvnRKFvm.js.map} +1 -1
- package/public/assets/{select-DallbU93.js → select-BIfbVmLf.js} +2 -2
- package/public/assets/{select-DallbU93.js.map → select-BIfbVmLf.js.map} +1 -1
- package/public/assets/settings-B6Ntahx7.js +6 -0
- package/public/assets/{settings-BUQirdyI.js.map → settings-B6Ntahx7.js.map} +1 -1
- package/public/assets/{store-DMZSTozC.js → store-zFLzd0Jd.js} +5 -5
- package/public/assets/{store-DMZSTozC.js.map → store-zFLzd0Jd.js.map} +1 -1
- package/public/assets/{task-chip-DXfvXBDx.js → task-chip-BHM-e3I8.js} +2 -2
- package/public/assets/{task-chip-DXfvXBDx.js.map → task-chip-BHM-e3I8.js.map} +1 -1
- package/public/assets/{tasks-k8mQDfKy.js → tasks-CUglafo5.js} +2 -2
- package/public/assets/{tasks-k8mQDfKy.js.map → tasks-CUglafo5.js.map} +1 -1
- package/public/assets/{teams-DI-Lhhbl.js → teams-EE8RHhHM.js} +2 -2
- package/public/assets/{teams-DI-Lhhbl.js.map → teams-EE8RHhHM.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BL853Fpi.js → terminal-viewer-impl-DBRkdyag.js} +3 -3
- package/public/assets/{terminal-viewer-impl-BL853Fpi.js.map → terminal-viewer-impl-DBRkdyag.js.map} +1 -1
- package/public/assets/{use-keyboard-viewport-CP109w9y.js → use-keyboard-viewport-CAa7IQTm.js} +2 -2
- package/public/assets/{use-keyboard-viewport-CP109w9y.js.map → use-keyboard-viewport-CAa7IQTm.js.map} +1 -1
- package/public/assets/user-avatar-kYPfLgXp.js +2 -0
- package/public/assets/{user-avatar-D9xyGi3u.js.map → user-avatar-kYPfLgXp.js.map} +1 -1
- package/public/assets/{work-queue-DrLIdvxB.js → work-queue-BFQVVL4T.js} +2 -2
- package/public/assets/{work-queue-DrLIdvxB.js.map → work-queue-BFQVVL4T.js.map} +1 -1
- package/public/assets/workspaces-CR6f7G0R.js +3 -0
- package/public/assets/{workspaces-CnNL6T8Q.js.map → workspaces-CR6f7G0R.js.map} +1 -1
- package/public/index.html +23 -23
- package/runner/hooks/rg-r-guard/hooks.json +18 -0
- package/runner/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/launch-assembly.ts +34 -1
- package/runner/src/provisioning.ts +15 -0
- package/src/services/spawn-chain-inspector.ts +134 -0
- package/public/assets/MessageBubble-BTeFExjj.js +0 -2
- package/public/assets/agents-BkC6mcTh.js +0 -2
- package/public/assets/branch-state-badge-CNW10MTC.js +0 -2
- package/public/assets/card-C3BGuVC6.js +0 -2
- package/public/assets/chat-BCNCuTgw.js +0 -2
- package/public/assets/copy-button-B3o1jpLu.js +0 -2
- package/public/assets/dist-B6woQ9Tb.js +0 -2
- package/public/assets/dist-DGfp5dLz.js +0 -2
- package/public/assets/dist-DT-OV6sQ.js +0 -2
- package/public/assets/formatted-body-CVr3Iev9.js +0 -3
- package/public/assets/index--9UMzCFf.js +0 -25
- package/public/assets/index--9UMzCFf.js.map +0 -1
- package/public/assets/lucide-react-DuhlF2Sj.js +0 -10
- package/public/assets/maintenance-HDpg20OY.js +0 -2
- package/public/assets/memory-CTO1FfJ-.js +0 -2
- package/public/assets/messages-RWSXG2UM.js +0 -2
- package/public/assets/prompt-settings-CaJX_LbA.js +0 -2
- package/public/assets/registry-C-uIL0Nl.js +0 -2
- package/public/assets/registry-C-uIL0Nl.js.map +0 -1
- package/public/assets/settings-BUQirdyI.js +0 -6
- package/public/assets/user-avatar-D9xyGi3u.js +0 -2
- package/public/assets/workspaces-CnNL6T8Q.js +0 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=task-chip-
|
|
1
|
+
import{rr as e,tr as t}from"./lucide-react-CoU_Xts2.js";var n=e(),r={open:`bg-blue-400`,claimed:`bg-purple-300`,in_progress:`bg-purple-400`,blocked:`bg-yellow-400`,orphaned:`bg-zinc-400`,done:`bg-emerald-400`,failed:`bg-red-400`,canceled:`bg-zinc-400`};function i({id:e,title:i,status:a,onClick:o,className:s,compact:c}){let l=a?r[a]:void 0;return(0,n.jsxs)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),o(e)},title:i?`Task T${e}: ${i}`:`Task T${e}`,"aria-label":`Open task T${e}`,className:t(`inline-flex max-w-full items-center rounded-full border font-medium leading-none transition-colors hover:brightness-125 focus:outline-none focus-visible:ring-1 focus-visible:ring-current`,c?`h-4 gap-1 pl-1 pr-1.5 text-[9px]`:`h-5 gap-1.5 pl-1.5 pr-2 text-[10px]`,`border-orange-500/25 bg-orange-500/10 text-orange-300`,s),children:[l&&(0,n.jsx)(`span`,{className:t(`h-1.5 w-1.5 shrink-0 rounded-full`,l)}),(0,n.jsxs)(`span`,{className:`truncate font-mono tracking-tight`,children:[`T`,e]}),!c&&i&&(0,n.jsx)(`span`,{className:`max-w-[8rem] truncate opacity-70`,children:i})]})}export{i as t};
|
|
2
|
+
//# sourceMappingURL=task-chip-BHM-e3I8.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-chip-
|
|
1
|
+
{"version":3,"file":"task-chip-BHM-e3I8.js","names":[],"sources":["../../dashboard/src/components/shared/task-chip.tsx"],"sourcesContent":["import { cn } from '@/lib/utils'\n\nconst STATUS_DOT: Record<string, string> = {\n open: 'bg-blue-400',\n claimed: 'bg-purple-300',\n in_progress: 'bg-purple-400',\n blocked: 'bg-yellow-400',\n orphaned: 'bg-zinc-400',\n done: 'bg-emerald-400',\n failed: 'bg-red-400',\n canceled: 'bg-zinc-400',\n}\n\nexport function TaskChip({ id, title, status, onClick, className, compact }: {\n id: number\n title?: string\n status?: string\n onClick: (e: React.MouseEvent) => void\n className?: string\n /** Compact/inline variant: smaller pill, no title text, flows inside prose. */\n compact?: boolean\n}) {\n const dot = status ? STATUS_DOT[status] : undefined\n return (\n <button\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); onClick(e) }}\n title={title ? `Task T${id}: ${title}` : `Task T${id}`}\n aria-label={`Open task T${id}`}\n className={cn(\n 'inline-flex max-w-full items-center rounded-full border font-medium leading-none transition-colors hover:brightness-125 focus:outline-none focus-visible:ring-1 focus-visible:ring-current',\n compact\n ? 'h-4 gap-1 pl-1 pr-1.5 text-[9px]'\n : 'h-5 gap-1.5 pl-1.5 pr-2 text-[10px]',\n 'border-orange-500/25 bg-orange-500/10 text-orange-300',\n className,\n )}\n >\n {dot && <span className={cn('h-1.5 w-1.5 shrink-0 rounded-full', dot)} />}\n <span className=\"truncate font-mono tracking-tight\">T{id}</span>\n {!compact && title && <span className=\"max-w-[8rem] truncate opacity-70\">{title}</span>}\n </button>\n )\n}\n"],"mappings":"kEAEM,EAAqC,CACzC,KAAM,cACN,QAAS,gBACT,YAAa,gBACb,QAAS,gBACT,SAAU,cACV,KAAM,iBACN,OAAQ,aACR,SAAU,cACX,CAED,SAAgB,EAAS,CAAE,KAAI,QAAO,SAAQ,UAAS,YAAW,WAQ/D,CACD,IAAM,EAAM,EAAS,EAAW,GAAU,IAAA,GAC1C,OACE,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,QAAU,GAAM,CAAE,EAAE,iBAAiB,CAAE,EAAQ,EAAE,EACjD,MAAO,EAAQ,SAAS,EAAG,IAAI,IAAU,SAAS,IAClD,aAAY,cAAc,IAC1B,UAAW,EACT,6LACA,EACI,mCACA,sCACJ,wDACA,EACD,UAZH,CAcG,IAAO,EAAA,EAAA,KAAC,OAAD,CAAM,UAAW,EAAG,oCAAqC,EAAI,CAAI,CAAA,EACzE,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,6CAAhB,CAAoD,IAAE,EAAU,GAC/D,CAAC,GAAW,IAAS,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,4CAAoC,EAAa,CAAA,CAChF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{
|
|
2
|
-
//# sourceMappingURL=tasks-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{Bt as t,H as n,Nt as r,Zt as i,dn as a,gr as o,ir as s,rr as c}from"./lucide-react-CoU_Xts2.js";import{p as l,t as u}from"./store-zFLzd0Jd.js";import{t as d}from"./types-CRptbSYh.js";import{K as f,h as p}from"./display-GFbGUG0O.js";import{t as m}from"./badge-XEru6wto.js";import{t as h}from"./button-C3CQSr4p.js";import{G as g,l as _}from"./index-DnUo0Zqg.js";import{o as v,r as y,t as b}from"./card-C5XTIPvq.js";var x=e(o(),1),S=c(),C={open:`bg-blue-500/10 text-blue-400 border-blue-500/20`,in_progress:`bg-purple-500/10 text-purple-400 border-purple-500/20`,blocked:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,done:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,failed:`bg-red-500/10 text-red-400 border-red-500/20`,canceled:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,claimed:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`},w={none:`bg-zinc-500/10 text-zinc-400`,uncommitted:`bg-yellow-500/10 text-yellow-400`,"committed-on-branch":`bg-blue-500/10 text-blue-400`,"landed-on-main":`bg-emerald-500/10 text-emerald-400`,deploying:`bg-purple-500/10 text-purple-400`,deployed:`bg-teal-500/10 text-teal-400`},T={none:`None`,uncommitted:`Uncommitted`,"committed-on-branch":`On Branch`,"landed-on-main":`Landed`,deploying:`Deploying`,deployed:`Deployed`},E=[`open`,`in_progress`,`blocked`,`done`,`failed`,`canceled`];function D(){let e=l(),t=u(e=>e.deliverableTasks),r=u(e=>e.deliverableStatusFilter),i=u(e=>e.deliverableStageFilter),o=u(e=>e.deliverableProjectFilter),c=u(e=>e.set),f=u(e=>e.fetchDeliverableTasks),p=u(e=>e.openTaskDetail),_=u(e=>e.openTaskCreate),[v,y]=(0,x.useState)([]);(0,x.useEffect)(()=>{let e=!1;return s(`GET`,`/projects`).then(t=>{e||y(t.projects??[])}).catch(()=>{e||y([])}),()=>{e=!0}},[]);let b=(0,x.useMemo)(()=>new Map(v.map(e=>[e.id,e])),[v]),C=(0,x.useMemo)(()=>{let e=v.map(e=>({value:e.id,label:e.title||e.slug||e.issueRepo||e.id}));for(let n of t)n.projectId&&!e.some(e=>e.value===n.projectId)&&e.push({value:n.projectId,label:n.repo||n.projectId});return e.sort((e,t)=>e.label.localeCompare(t.label))},[t,v]);function w(e){c({deliverableStatusFilter:e}),f()}function D(e){c({deliverableStageFilter:e}),f()}function k(e){c({deliverableProjectFilter:e}),f()}return(0,S.jsxs)(`div`,{className:`space-y-4`,children:[(0,S.jsxs)(`div`,{className:`flex items-center justify-between flex-wrap gap-2`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(a,{className:`w-5 h-5 text-muted-foreground`}),(0,S.jsx)(`h2`,{className:`text-lg font-semibold`,children:`Tasks`}),(0,S.jsx)(m,{variant:`secondary`,children:t.length})]}),(0,S.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,S.jsxs)(h,{size:`sm`,className:`h-8 gap-1.5`,onClick:_,children:[(0,S.jsx)(n,{className:`w-3.5 h-3.5`}),`New Task`]}),(0,S.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:r,onChange:e=>w(e.target.value),children:[(0,S.jsx)(`option`,{value:``,children:`All statuses`}),E.map(e=>(0,S.jsx)(`option`,{value:e,children:e},e))]}),(0,S.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:i,onChange:e=>D(e.target.value),children:[(0,S.jsx)(`option`,{value:``,children:`All stages`}),d.map(e=>(0,S.jsx)(`option`,{value:e,children:T[e]??e},e))]}),(0,S.jsxs)(`select`,{className:`rounded-md border border-input bg-background px-3 py-1.5 text-sm`,value:o,onChange:e=>k(e.target.value),children:[(0,S.jsx)(`option`,{value:``,children:`All projects`}),C.map(e=>(0,S.jsx)(`option`,{value:e.value,children:e.label},e.value))]})]})]}),(0,S.jsx)(g,{className:`h-[calc(100dvh-11rem)]`,children:(0,S.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2 lg:grid-cols-3 pr-2`,children:[t.length===0&&(0,S.jsx)(`div`,{className:`col-span-full text-center text-muted-foreground py-16 text-sm`,children:`No deliverable tasks`}),t.map(t=>(0,S.jsx)(O,{task:t,now:e,project:t.projectId?b.get(t.projectId):void 0,onOpen:()=>p(t)},t.id))]})})]})}function O({task:e,now:n,project:a,onOpen:o}){let s=C[e.status]??`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`,c=e.deliverableStage?w[e.deliverableStage]??`bg-zinc-500/10 text-zinc-400`:``,l=e.label||e.title,u=a?.title||a?.slug||e.repo||e.projectId;return(0,S.jsxs)(b,{className:`flex flex-col cursor-pointer hover:border-zinc-600 transition-colors min-w-0`,onClick:o,children:[(0,S.jsxs)(v,{className:`py-3 px-4 pb-0 space-y-0`,children:[(0,S.jsxs)(`div`,{className:`flex items-start gap-2 justify-between`,children:[(0,S.jsxs)(`div`,{className:`flex gap-1.5 flex-wrap`,children:[(0,S.jsx)(m,{variant:`outline`,className:`text-xs px-1.5 py-0 border ${s}`,children:e.status}),e.deliverableStage&&e.deliverableStage!==`none`&&(0,S.jsx)(m,{variant:`secondary`,className:`text-xs px-1.5 py-0 ${c}`,children:T[e.deliverableStage]??e.deliverableStage})]}),(0,S.jsxs)(`span`,{className:`text-xs text-muted-foreground shrink-0`,children:[`T`,e.id]})]}),(0,S.jsx)(`p`,{className:`font-medium text-sm mt-2 line-clamp-2`,children:l})]}),(0,S.jsxs)(y,{className:`px-4 py-3 flex-1 flex flex-col justify-between gap-2`,children:[(0,S.jsxs)(`div`,{className:`space-y-0.5 text-xs text-muted-foreground`,children:[u&&(0,S.jsxs)(`div`,{className:`flex items-center gap-1 min-w-0`,children:[(0,S.jsx)(t,{className:`w-3 h-3 shrink-0`}),(0,S.jsx)(`span`,{className:`truncate min-w-0`,children:u})]}),e.ownerAgentId&&(0,S.jsxs)(`div`,{className:`flex items-center gap-1 min-w-0`,children:[(0,S.jsx)(`span`,{className:`text-muted-foreground/60 shrink-0`,children:`owner`}),(0,S.jsx)(_,{agentId:e.ownerAgentId,fallbackLabel:e.ownerAgentLabel,noIcon:!0,className:`text-xs min-w-0`})]}),e.branch&&(0,S.jsxs)(`div`,{className:`flex items-center gap-1 min-w-0`,children:[(0,S.jsx)(r,{className:`w-3 h-3 shrink-0`}),(0,S.jsx)(`span`,{className:`font-mono truncate min-w-0`,children:e.branch})]}),(0,S.jsxs)(`div`,{className:`flex items-center gap-1`,title:p(e.updatedAt),children:[(0,S.jsx)(`span`,{className:`text-muted-foreground/60`,children:`updated`}),(0,S.jsx)(`span`,{children:f(n,e.updatedAt)})]})]}),(0,S.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,S.jsx)(h,{size:`sm`,variant:`outline`,className:`h-7 text-xs`,onClick:e=>{e.stopPropagation(),o()},children:`Details`}),e.externalUrl&&(0,S.jsx)(`a`,{href:e.externalUrl,target:`_blank`,rel:`noopener noreferrer`,className:`ml-auto`,onClick:e=>e.stopPropagation(),children:(0,S.jsx)(h,{size:`sm`,variant:`ghost`,className:`h-7 px-2`,children:(0,S.jsx)(i,{className:`w-3.5 h-3.5`})})})]})]})]})}export{D as TasksView};
|
|
2
|
+
//# sourceMappingURL=tasks-CUglafo5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-k8mQDfKy.js","names":[],"sources":["../../dashboard/src/components/views/tasks.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { useRelayStore, useNow } from '@/store'\nimport { api } from '@/lib/api'\nimport { Card, CardContent, CardHeader } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ClipboardList, ExternalLink, FolderGit2, GitBranch, Plus } from 'lucide-react'\nimport { timeAgo, fmtTime } from '@/lib/display'\nimport { AgentLink } from '@/components/chat/TraceView'\nimport { TASK_DELIVERABLE_STAGES } from 'agent-relay-sdk/types'\nimport type { Project, Task } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n done: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n failed: 'bg-red-500/10 text-red-400 border-red-500/20',\n canceled: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n}\n\nconst STAGE_COLORS: Record<string, string> = {\n none: 'bg-zinc-500/10 text-zinc-400',\n uncommitted: 'bg-yellow-500/10 text-yellow-400',\n 'committed-on-branch': 'bg-blue-500/10 text-blue-400',\n 'landed-on-main': 'bg-emerald-500/10 text-emerald-400',\n deploying: 'bg-purple-500/10 text-purple-400',\n deployed: 'bg-teal-500/10 text-teal-400',\n}\n\nconst STAGE_LABELS: Record<string, string> = {\n none: 'None',\n uncommitted: 'Uncommitted',\n 'committed-on-branch': 'On Branch',\n 'landed-on-main': 'Landed',\n deploying: 'Deploying',\n deployed: 'Deployed',\n}\n\nconst DELIVERABLE_STATUS_OPTIONS = ['open', 'in_progress', 'blocked', 'done', 'failed', 'canceled']\n\nexport function TasksView() {\n const now = useNow()\n const deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const deliverableStatusFilter = useRelayStore((s) => s.deliverableStatusFilter)\n const deliverableStageFilter = useRelayStore((s) => s.deliverableStageFilter)\n const deliverableProjectFilter = useRelayStore((s) => s.deliverableProjectFilter)\n const set = useRelayStore((s) => s.set)\n const fetchDeliverableTasks = useRelayStore((s) => s.fetchDeliverableTasks)\n const openTaskDetail = useRelayStore((s) => s.openTaskDetail)\n const openTaskCreate = useRelayStore((s) => s.openTaskCreate)\n const [projects, setProjects] = useState<Project[]>([])\n\n useEffect(() => {\n let cancelled = false\n api<{ projects: Project[] }>('GET', '/projects')\n .then((res) => { if (!cancelled) setProjects(res.projects ?? []) })\n .catch(() => { if (!cancelled) setProjects([]) })\n return () => { cancelled = true }\n }, [])\n\n const projectById = useMemo(() => new Map(projects.map((project) => [project.id, project])), [projects])\n const projectOptions = useMemo(() => {\n const options = projects.map((project) => ({\n value: project.id,\n label: project.title || project.slug || project.issueRepo || project.id,\n }))\n for (const task of deliverableTasks) {\n if (task.projectId && !options.some((option) => option.value === task.projectId)) {\n options.push({ value: task.projectId, label: task.repo || task.projectId })\n }\n }\n return options.sort((a, b) => a.label.localeCompare(b.label))\n }, [deliverableTasks, projects])\n\n function handleStatusFilter(value: string) {\n set({ deliverableStatusFilter: value })\n void fetchDeliverableTasks()\n }\n\n function handleStageFilter(value: string) {\n set({ deliverableStageFilter: value })\n void fetchDeliverableTasks()\n }\n\n function handleProjectFilter(value: string) {\n set({ deliverableProjectFilter: value })\n void fetchDeliverableTasks()\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ClipboardList className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Tasks</h2>\n <Badge variant=\"secondary\">{deliverableTasks.length}</Badge>\n </div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Button size=\"sm\" className=\"h-8 gap-1.5\" onClick={openTaskCreate}>\n <Plus className=\"w-3.5 h-3.5\" />\n New Task\n </Button>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableStatusFilter}\n onChange={(e) => handleStatusFilter(e.target.value)}\n >\n <option value=\"\">All statuses</option>\n {DELIVERABLE_STATUS_OPTIONS.map((s) => <option key={s} value={s}>{s}</option>)}\n </select>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableStageFilter}\n onChange={(e) => handleStageFilter(e.target.value)}\n >\n <option value=\"\">All stages</option>\n {TASK_DELIVERABLE_STAGES.map((s) => <option key={s} value={s}>{STAGE_LABELS[s] ?? s}</option>)}\n </select>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableProjectFilter}\n onChange={(e) => handleProjectFilter(e.target.value)}\n >\n <option value=\"\">All projects</option>\n {projectOptions.map((project) => <option key={project.value} value={project.value}>{project.label}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3 pr-2\">\n {deliverableTasks.length === 0 && (\n <div className=\"col-span-full text-center text-muted-foreground py-16 text-sm\">No deliverable tasks</div>\n )}\n {deliverableTasks.map((task) => (\n <DeliverableTaskCard\n key={task.id}\n task={task}\n now={now}\n project={task.projectId ? projectById.get(task.projectId) : undefined}\n onOpen={() => openTaskDetail(task)}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction DeliverableTaskCard({\n task, now, project, onOpen,\n}: {\n task: Task\n now: number\n project?: Project\n onOpen: () => void\n}) {\n const statusClass = STATUS_COLORS[task.status] ?? 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n const stageClass = task.deliverableStage ? (STAGE_COLORS[task.deliverableStage] ?? 'bg-zinc-500/10 text-zinc-400') : ''\n const label = task.label || task.title\n const projectLabel = project?.title || project?.slug || task.repo || task.projectId\n\n return (\n <Card\n className=\"flex flex-col cursor-pointer hover:border-zinc-600 transition-colors min-w-0\"\n onClick={onOpen}\n >\n <CardHeader className=\"py-3 px-4 pb-0 space-y-0\">\n <div className=\"flex items-start gap-2 justify-between\">\n <div className=\"flex gap-1.5 flex-wrap\">\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{task.status}</Badge>\n {task.deliverableStage && task.deliverableStage !== 'none' && (\n <Badge variant=\"secondary\" className={`text-xs px-1.5 py-0 ${stageClass}`}>\n {STAGE_LABELS[task.deliverableStage] ?? task.deliverableStage}\n </Badge>\n )}\n </div>\n <span className=\"text-xs text-muted-foreground shrink-0\">T{task.id}</span>\n </div>\n <p className=\"font-medium text-sm mt-2 line-clamp-2\">{label}</p>\n </CardHeader>\n <CardContent className=\"px-4 py-3 flex-1 flex flex-col justify-between gap-2\">\n <div className=\"space-y-0.5 text-xs text-muted-foreground\">\n {projectLabel && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <FolderGit2 className=\"w-3 h-3 shrink-0\" />\n <span className=\"truncate min-w-0\">{projectLabel}</span>\n </div>\n )}\n {task.ownerAgentId && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <span className=\"text-muted-foreground/60 shrink-0\">owner</span>\n <AgentLink agentId={task.ownerAgentId} fallbackLabel={task.ownerAgentLabel} noIcon className=\"text-xs min-w-0\" />\n </div>\n )}\n {task.branch && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <GitBranch className=\"w-3 h-3 shrink-0\" />\n <span className=\"font-mono truncate min-w-0\">{task.branch}</span>\n </div>\n )}\n <div className=\"flex items-center gap-1\" title={fmtTime(task.updatedAt)}>\n <span className=\"text-muted-foreground/60\">updated</span>\n <span>{timeAgo(now, task.updatedAt)}</span>\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={(e) => { e.stopPropagation(); onOpen() }}>\n Details\n </Button>\n {task.externalUrl && (\n <a href={task.externalUrl} target=\"_blank\" rel=\"noopener noreferrer\" className=\"ml-auto\" onClick={(e) => e.stopPropagation()}>\n <Button size=\"sm\" variant=\"ghost\" className=\"h-7 px-2\">\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </Button>\n </a>\n )}\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tasks-CUglafo5.js","names":[],"sources":["../../dashboard/src/components/views/tasks.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { useRelayStore, useNow } from '@/store'\nimport { api } from '@/lib/api'\nimport { Card, CardContent, CardHeader } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { ClipboardList, ExternalLink, FolderGit2, GitBranch, Plus } from 'lucide-react'\nimport { timeAgo, fmtTime } from '@/lib/display'\nimport { AgentLink } from '@/components/chat/TraceView'\nimport { TASK_DELIVERABLE_STAGES } from 'agent-relay-sdk/types'\nimport type { Project, Task } from '@/types'\n\nconst STATUS_COLORS: Record<string, string> = {\n open: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n in_progress: 'bg-purple-500/10 text-purple-400 border-purple-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n done: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n failed: 'bg-red-500/10 text-red-400 border-red-500/20',\n canceled: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n claimed: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n}\n\nconst STAGE_COLORS: Record<string, string> = {\n none: 'bg-zinc-500/10 text-zinc-400',\n uncommitted: 'bg-yellow-500/10 text-yellow-400',\n 'committed-on-branch': 'bg-blue-500/10 text-blue-400',\n 'landed-on-main': 'bg-emerald-500/10 text-emerald-400',\n deploying: 'bg-purple-500/10 text-purple-400',\n deployed: 'bg-teal-500/10 text-teal-400',\n}\n\nconst STAGE_LABELS: Record<string, string> = {\n none: 'None',\n uncommitted: 'Uncommitted',\n 'committed-on-branch': 'On Branch',\n 'landed-on-main': 'Landed',\n deploying: 'Deploying',\n deployed: 'Deployed',\n}\n\nconst DELIVERABLE_STATUS_OPTIONS = ['open', 'in_progress', 'blocked', 'done', 'failed', 'canceled']\n\nexport function TasksView() {\n const now = useNow()\n const deliverableTasks = useRelayStore((s) => s.deliverableTasks)\n const deliverableStatusFilter = useRelayStore((s) => s.deliverableStatusFilter)\n const deliverableStageFilter = useRelayStore((s) => s.deliverableStageFilter)\n const deliverableProjectFilter = useRelayStore((s) => s.deliverableProjectFilter)\n const set = useRelayStore((s) => s.set)\n const fetchDeliverableTasks = useRelayStore((s) => s.fetchDeliverableTasks)\n const openTaskDetail = useRelayStore((s) => s.openTaskDetail)\n const openTaskCreate = useRelayStore((s) => s.openTaskCreate)\n const [projects, setProjects] = useState<Project[]>([])\n\n useEffect(() => {\n let cancelled = false\n api<{ projects: Project[] }>('GET', '/projects')\n .then((res) => { if (!cancelled) setProjects(res.projects ?? []) })\n .catch(() => { if (!cancelled) setProjects([]) })\n return () => { cancelled = true }\n }, [])\n\n const projectById = useMemo(() => new Map(projects.map((project) => [project.id, project])), [projects])\n const projectOptions = useMemo(() => {\n const options = projects.map((project) => ({\n value: project.id,\n label: project.title || project.slug || project.issueRepo || project.id,\n }))\n for (const task of deliverableTasks) {\n if (task.projectId && !options.some((option) => option.value === task.projectId)) {\n options.push({ value: task.projectId, label: task.repo || task.projectId })\n }\n }\n return options.sort((a, b) => a.label.localeCompare(b.label))\n }, [deliverableTasks, projects])\n\n function handleStatusFilter(value: string) {\n set({ deliverableStatusFilter: value })\n void fetchDeliverableTasks()\n }\n\n function handleStageFilter(value: string) {\n set({ deliverableStageFilter: value })\n void fetchDeliverableTasks()\n }\n\n function handleProjectFilter(value: string) {\n set({ deliverableProjectFilter: value })\n void fetchDeliverableTasks()\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <ClipboardList className=\"w-5 h-5 text-muted-foreground\" />\n <h2 className=\"text-lg font-semibold\">Tasks</h2>\n <Badge variant=\"secondary\">{deliverableTasks.length}</Badge>\n </div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Button size=\"sm\" className=\"h-8 gap-1.5\" onClick={openTaskCreate}>\n <Plus className=\"w-3.5 h-3.5\" />\n New Task\n </Button>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableStatusFilter}\n onChange={(e) => handleStatusFilter(e.target.value)}\n >\n <option value=\"\">All statuses</option>\n {DELIVERABLE_STATUS_OPTIONS.map((s) => <option key={s} value={s}>{s}</option>)}\n </select>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableStageFilter}\n onChange={(e) => handleStageFilter(e.target.value)}\n >\n <option value=\"\">All stages</option>\n {TASK_DELIVERABLE_STAGES.map((s) => <option key={s} value={s}>{STAGE_LABELS[s] ?? s}</option>)}\n </select>\n <select\n className=\"rounded-md border border-input bg-background px-3 py-1.5 text-sm\"\n value={deliverableProjectFilter}\n onChange={(e) => handleProjectFilter(e.target.value)}\n >\n <option value=\"\">All projects</option>\n {projectOptions.map((project) => <option key={project.value} value={project.value}>{project.label}</option>)}\n </select>\n </div>\n </div>\n\n <ScrollArea className=\"h-[calc(100dvh-11rem)]\">\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3 pr-2\">\n {deliverableTasks.length === 0 && (\n <div className=\"col-span-full text-center text-muted-foreground py-16 text-sm\">No deliverable tasks</div>\n )}\n {deliverableTasks.map((task) => (\n <DeliverableTaskCard\n key={task.id}\n task={task}\n now={now}\n project={task.projectId ? projectById.get(task.projectId) : undefined}\n onOpen={() => openTaskDetail(task)}\n />\n ))}\n </div>\n </ScrollArea>\n </div>\n )\n}\n\nfunction DeliverableTaskCard({\n task, now, project, onOpen,\n}: {\n task: Task\n now: number\n project?: Project\n onOpen: () => void\n}) {\n const statusClass = STATUS_COLORS[task.status] ?? 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n const stageClass = task.deliverableStage ? (STAGE_COLORS[task.deliverableStage] ?? 'bg-zinc-500/10 text-zinc-400') : ''\n const label = task.label || task.title\n const projectLabel = project?.title || project?.slug || task.repo || task.projectId\n\n return (\n <Card\n className=\"flex flex-col cursor-pointer hover:border-zinc-600 transition-colors min-w-0\"\n onClick={onOpen}\n >\n <CardHeader className=\"py-3 px-4 pb-0 space-y-0\">\n <div className=\"flex items-start gap-2 justify-between\">\n <div className=\"flex gap-1.5 flex-wrap\">\n <Badge variant=\"outline\" className={`text-xs px-1.5 py-0 border ${statusClass}`}>{task.status}</Badge>\n {task.deliverableStage && task.deliverableStage !== 'none' && (\n <Badge variant=\"secondary\" className={`text-xs px-1.5 py-0 ${stageClass}`}>\n {STAGE_LABELS[task.deliverableStage] ?? task.deliverableStage}\n </Badge>\n )}\n </div>\n <span className=\"text-xs text-muted-foreground shrink-0\">T{task.id}</span>\n </div>\n <p className=\"font-medium text-sm mt-2 line-clamp-2\">{label}</p>\n </CardHeader>\n <CardContent className=\"px-4 py-3 flex-1 flex flex-col justify-between gap-2\">\n <div className=\"space-y-0.5 text-xs text-muted-foreground\">\n {projectLabel && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <FolderGit2 className=\"w-3 h-3 shrink-0\" />\n <span className=\"truncate min-w-0\">{projectLabel}</span>\n </div>\n )}\n {task.ownerAgentId && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <span className=\"text-muted-foreground/60 shrink-0\">owner</span>\n <AgentLink agentId={task.ownerAgentId} fallbackLabel={task.ownerAgentLabel} noIcon className=\"text-xs min-w-0\" />\n </div>\n )}\n {task.branch && (\n <div className=\"flex items-center gap-1 min-w-0\">\n <GitBranch className=\"w-3 h-3 shrink-0\" />\n <span className=\"font-mono truncate min-w-0\">{task.branch}</span>\n </div>\n )}\n <div className=\"flex items-center gap-1\" title={fmtTime(task.updatedAt)}>\n <span className=\"text-muted-foreground/60\">updated</span>\n <span>{timeAgo(now, task.updatedAt)}</span>\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Button size=\"sm\" variant=\"outline\" className=\"h-7 text-xs\" onClick={(e) => { e.stopPropagation(); onOpen() }}>\n Details\n </Button>\n {task.externalUrl && (\n <a href={task.externalUrl} target=\"_blank\" rel=\"noopener noreferrer\" className=\"ml-auto\" onClick={(e) => e.stopPropagation()}>\n <Button size=\"sm\" variant=\"ghost\" className=\"h-7 px-2\">\n <ExternalLink className=\"w-3.5 h-3.5\" />\n </Button>\n </a>\n )}\n </div>\n </CardContent>\n </Card>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{Dn as t,L as n,er as r,g as i,hr as a,nr as o,nt as ee,rr as s,u as te}from"./lucide-react-DuhlF2Sj.js";import{i as ne,n as c,t as l}from"./store-DMZSTozC.js";import{d as u}from"./types-CRptbSYh.js";import{G as re,K as d,d as f,h as p}from"./display-GFbGUG0O.js";import{a as ie,i as ae,n as oe,r as se,t as ce}from"./select-DallbU93.js";import{t as m}from"./badge-DCIT3Irj.js";import{t as h}from"./button-CzdTkRIf.js";import{t as le}from"./input-B6Q0NWCS.js";import{G as g,X as ue}from"./index--9UMzCFf.js";import{o as _,r as v,s as y,t as b}from"./card-C3BGuVC6.js";import{t as de}from"./PlanGraphPanel-jU1DXg7w.js";var x=e(a(),1),S=o();function C(e){if(e==null)return`-`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.round(t/60);if(n<60)return`${n}m`;let r=Math.round(n/60);return r<48?`${r}h`:`${Math.round(r/24)}d`}function w(e){return`${Math.round(e*100)}%`}function T(e){return e===`success`?`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`:e===`partial`?`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`:e===`failed`?`bg-red-500/10 text-red-400 border-red-500/20`:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`}function fe({data:e,loading:n,error:r}){let i=e?.totals.byOutcomeStatus,a=e?.compositionShapes.slice(0,6)??[],o=e?.recent.slice(0,8)??[];return(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(t,{className:`size-4 text-primary`}),` Outcomes`]}),(0,S.jsxs)(m,{variant:`secondary`,children:[e?.totals.total??0,` dissolved`]})]})}),(0,S.jsxs)(v,{className:`space-y-5`,children:[r?(0,S.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:r}):null,n&&!e?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`Loading outcomes…`}):!e||e.totals.total===0?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No dissolved teams yet`}):(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`div`,{className:`flex flex-wrap gap-2 text-xs`,children:[`success`,`partial`,`failed`,`unknown`].map(e=>{let t=i?.[e]??0;return(0,S.jsxs)(m,{variant:`outline`,className:t===0?`text-muted-foreground`:T(e===`unknown`?null:e),children:[t,` `,e]},e)})}),(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:`Composition win-rate`}),(0,S.jsx)(`div`,{className:`divide-y divide-border rounded-md border border-border`,children:a.map(e=>(0,S.jsxs)(`div`,{className:`grid gap-2 px-3 py-2 text-sm md:grid-cols-[1fr_auto_auto] md:items-center`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`break-words font-medium`,title:e.shape,children:e.shape}),(0,S.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e.count,` team`,e.count===1?``:`s`,` · avg `,C(e.avgWallClockMs)]})]}),(0,S.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.successCount,`/`,e.count]}),(0,S.jsx)(m,{variant:`outline`,className:T(e.successRate>=.5?`success`:e.successRate>0?`partial`:`failed`),children:w(e.successRate)})]},e.shape))})]}),(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:`Recent outcomes`}),(0,S.jsx)(`div`,{className:`overflow-hidden rounded-md border border-border`,children:(0,S.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,S.jsx)(`thead`,{className:`bg-muted/40 text-xs text-muted-foreground`,children:(0,S.jsxs)(`tr`,{children:[(0,S.jsx)(`th`,{className:`px-3 py-2 text-left font-medium`,children:`Team`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-left font-medium`,children:`Outcome`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-right font-medium`,children:`Members`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-right font-medium`,children:`Duration`})]})}),(0,S.jsx)(`tbody`,{className:`divide-y divide-border`,children:o.map(e=>(0,S.jsxs)(`tr`,{children:[(0,S.jsxs)(`td`,{className:`max-w-[220px] px-3 py-2`,children:[(0,S.jsx)(`div`,{className:`truncate font-medium`,children:e.title||e.id.slice(0,12)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]}),(0,S.jsx)(`td`,{className:`px-3 py-2`,children:(0,S.jsx)(m,{variant:`outline`,className:T(e.outcome),children:e.outcome??`unknown`})}),(0,S.jsx)(`td`,{className:`px-3 py-2 text-right`,children:e.memberCount}),(0,S.jsx)(`td`,{className:`px-3 py-2 text-right text-muted-foreground`,children:C(e.durationMs)})]},e.id))})]})})]})]})]})]})}var pe=[],me=[{value:`all`,label:`All kinds`},{value:`decision`,label:`Decisions`},{value:`fact`,label:`Facts`},{value:`ruled_out`,label:`Ruled out`}],he={decision:`decision`,fact:`fact`,ruled_out:`ruled out`};function ge(e){return e.runtimeState?e.runtimeState:e.status===`dissolved`?`dissolved`:e.zeroMembersSince==null?`active`:`orphaned`}var _e={busy:`bg-blue-500/10 text-blue-400 border-blue-500/20`,idle:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,degraded:`bg-orange-500/10 text-orange-400 border-orange-500/20`,stalled:`bg-amber-500/10 text-amber-400 border-amber-500/20`,active:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,orphaned:`bg-amber-500/10 text-amber-400 border-amber-500/20`,dissolved:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`},ve={success:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,partial:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,failed:`bg-red-500/10 text-red-400 border-red-500/20`},ye={active:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,superseded:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,retracted:`bg-red-500/10 text-red-400 border-red-500/20`};function E(e){return e?e.slice(0,12):`-`}function be(e){if(e===void 0)return`-`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.round(t/60);if(n<60)return`${n}m`;let r=Math.round(n/60);return r<48?`${r}h`:`${Math.round(r/24)}d`}function xe(e){if(!e.composition)return null;try{let t=JSON.parse(e.composition);return Array.isArray(t)?t.length:null}catch{return null}}function Se(e,t){let n=t.trim().toLowerCase();return n?[e.title,e.id,e.coordinatorId,e.rootCoordinatorId,e.outcomeStatus,e.outcomeSummary].some(e=>String(e??``).toLowerCase().includes(n)):!0}function Ce(e){return e.status===`offline`?`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`:e.status===`busy`?`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`:e.status===`stale`?`bg-red-500/10 text-red-400 border-red-500/20`:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`}function D(){let[e,t]=(0,x.useState)(null),[a,o]=(0,x.useState)([]),[C,w]=(0,x.useState)(null),[T,D]=(0,x.useState)(`all`),[O,we]=(0,x.useState)(``),[k,A]=(0,x.useState)(!1),[Te,j]=(0,x.useState)(!1),[M,N]=(0,x.useState)(null),[P,Ee]=(0,x.useState)(()=>Date.now()),F=l(e=>e.teams),I=e??F[0]?.id??null,L=I?c(`team`,I):``,R=l(e=>e.teamsLoading),z=l(e=>e.teamsError),De=l(e=>e.fetchTeams),B=l(e=>(I?e.issuesByEntity[L]:void 0)??pe),V=l(e=>e.issueRefsById),Oe=l(e=>L?!!e.issueLoading[L]:!1),H=l(e=>L?e.issueErrors[L]??null:null),U=l(e=>e.fetchIssuesForEntity),ke=l(e=>e.teamOutcomeAggregates),Ae=l(e=>e.teamOutcomeAggregatesLoading),je=l(e=>e.teamOutcomeAggregatesError),Me=l(e=>e.fetchTeamOutcomeAggregates),W=l(e=>e.fetchPlan),Ne=l(e=>e.dissolveTeam),Pe=l(e=>e.openConfirm),G=l(e=>e.agentsById),Fe=l(e=>e.switchView),Ie=l(e=>e.openInboxThread),Le=l(e=>I?e.plansByTeam[ne(I)]??null:null),K=e=>{let t=G[e];return t?f(t):e.slice(-8)};async function q(e,t=T){j(!0),N(null);try{let n=new URLSearchParams({team:e});t!==`all`&&n.set(`kind`,t),o((await s(`GET`,`/blackboard?${n.toString()}`)).entries),w(e)}catch(t){N(u(t)),o([]),w(e)}finally{j(!1)}}async function J(){A(!0),N(null);try{let[t]=await Promise.all([De(),Me()]);Ee(Date.now());let n=e??t[0]?.id;n?(W({teamId:n}),await Promise.all([q(n,T),U(`team`,n)])):(o([]),w(null))}catch(e){N(u(e))}finally{A(!1)}}(0,x.useEffect)(()=>{J()},[]),(0,x.useEffect)(()=>{C&&q(C,T)},[T]);let Y=(0,x.useMemo)(()=>F.filter(e=>Se(e,O)),[F,O]),X=F.find(e=>e.id===I)??null,Z=C===I?a:[],Q=(0,x.useMemo)(()=>B.map(e=>({...e,issueRef:V[e.issueRef.id]??e.issueRef})),[V,B]),$=(0,x.useMemo)(()=>I?Object.values(G).filter(e=>e.teamId===I).sort((e,t)=>f(e).localeCompare(f(t))||e.id.localeCompare(t.id)):[],[G,I]),Re=(0,x.useMemo)(()=>{let e={decision:[],fact:[],ruled_out:[]};for(let t of Z)e[t.kind].push(t);return e},[Z]),ze=T===`all`?[`decision`,`fact`,`ruled_out`]:[T];return(0,S.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(te,{className:`size-5 text-primary`}),(0,S.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Teams`}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`active and dissolved team working sets`})]}),(0,S.jsxs)(h,{variant:`outline`,size:`sm`,onClick:()=>void J(),disabled:k||R,children:[(0,S.jsx)(n,{className:r(`size-4`,(k||R)&&`animate-spin`)}),` Refresh`]})]}),(M||z)&&(0,S.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:M||z}),(0,S.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[minmax(320px,420px)_1fr]`,children:[(0,S.jsxs)(`div`,{className:`min-h-[520px] min-w-0 space-y-3`,children:[(0,S.jsxs)(`div`,{className:`space-y-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-center justify-between gap-3`,children:[(0,S.jsx)(`span`,{children:`Team list`}),(0,S.jsx)(m,{variant:`secondary`,children:F.length})]}),(0,S.jsx)(le,{value:O,onChange:e=>we(e.target.value),placeholder:`Filter teams`,className:`h-8`})]}),(0,S.jsx)(`div`,{children:(k||R)&&F.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Loading teams…`}):Y.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:F.length===0?`No teams`:`No teams match`}):(0,S.jsx)(g,{className:`h-[610px] pr-3`,children:(0,S.jsx)(`div`,{className:`space-y-2`,children:Y.map(e=>{let n=e.id===I,i=xe(e),a=ge(e),o=e.coordinatorId,s=()=>{t(e.id),W({teamId:e.id}),U(`team`,e.id),q(e.id,T)};return(0,S.jsx)(`div`,{role:`button`,tabIndex:0,onClick:s,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),s())},className:`block w-full cursor-pointer text-left`,children:(0,S.jsx)(b,{className:r(`transition-colors hover:border-primary/50`,n&&`border-primary/60 bg-primary/5`),children:(0,S.jsxs)(v,{className:`space-y-3 p-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-start justify-between gap-2`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`truncate text-sm font-medium`,children:e.title||E(e.id)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]}),(0,S.jsx)(m,{variant:`outline`,className:_e[a],children:a})]}),(0,S.jsxs)(`div`,{className:`grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-muted-foreground`,children:[(0,S.jsx)(`span`,{children:`Coordinator`}),o?(0,S.jsxs)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),Ie(o),Fe(`chat`)},className:`flex min-w-0 items-center justify-end gap-1 text-right text-primary hover:underline`,title:`Chat with ${o}`,children:[(0,S.jsx)(ee,{className:`size-3 shrink-0`}),(0,S.jsx)(`span`,{className:`truncate`,children:K(o)})]}):(0,S.jsx)(`span`,{className:`text-right text-muted-foreground`,children:`—`}),(0,S.jsx)(`span`,{children:`Members`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,children:i??`-`}),(0,S.jsx)(`span`,{children:`Created`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,title:p(e.createdAt),children:d(P,e.createdAt)}),e.dissolvedAt?(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`span`,{children:`Dissolved`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,title:p(e.dissolvedAt),children:d(P,e.dissolvedAt)})]}):null]}),a===`orphaned`&&e.zeroMembersSince!=null?(0,S.jsx)(`div`,{className:`flex items-center gap-1.5 border-t border-border pt-2 text-xs text-amber-400`,title:p(e.zeroMembersSince),children:(0,S.jsxs)(`span`,{children:[`No live members since `,d(P,e.zeroMembersSince),` — auto-dissolving`]})}):null,e.status===`dissolved`?(0,S.jsxs)(`div`,{className:`space-y-1 border-t border-border pt-2`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[e.outcomeStatus?(0,S.jsx)(m,{variant:`outline`,className:ve[e.outcomeStatus],children:e.outcomeStatus}):(0,S.jsx)(m,{variant:`outline`,children:`no outcome`}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:be(e.wallClockMs)})]}),e.outcomeSummary?(0,S.jsx)(`p`,{className:`line-clamp-2 text-xs text-muted-foreground`,children:e.outcomeSummary}):null]}):null]})})},e.id)})})})})]}),(0,S.jsxs)(`div`,{className:`min-h-[520px] min-w-0 space-y-4`,children:[(0,S.jsx)(fe,{data:ke,loading:Ae,error:je}),X?(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Issues`}),(0,S.jsxs)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:[Q.length,` associated`]})]}),Oe?(0,S.jsx)(m,{variant:`secondary`,children:`loading`}):null]})}),(0,S.jsx)(v,{children:H?(0,S.jsx)(`div`,{className:`text-xs text-destructive`,children:H}):Q.length===0?(0,S.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No associated issues`}):(0,S.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:Q.map(({association:e,issueRef:t})=>(0,S.jsx)(ue,{issueRef:t,now:P,showSync:!0},`${e.issueRefId}:${e.createdAt}`))})})]}):null,X&&Le?(0,S.jsx)(b,{className:`p-0`,children:(0,S.jsx)(de,{teamId:X.id})}):null,(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Roster`}),(0,S.jsxs)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:[$.length,` member`,$.length===1?``:`s`]})]}),X&&X.status!==`dissolved`?(0,S.jsxs)(h,{variant:`destructive`,size:`sm`,onClick:()=>Pe(`Dissolve Team`,`Dissolve "${X.title||X.id}"? This closes the team working set but keeps its records visible.`,()=>void Ne(X.id)),children:[(0,S.jsx)(i,{className:`size-4`}),` Dissolve`]}):null]})}),(0,S.jsx)(v,{children:X?$.length===0?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No members`}):(0,S.jsx)(`div`,{className:`grid gap-2 md:grid-cols-2`,children:$.map(e=>(0,S.jsxs)(`div`,{className:`flex min-w-0 items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2`,children:[(0,S.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,S.jsx)(`span`,{className:r(`h-2 w-2 shrink-0 rounded-full`,re(e,P))}),(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`truncate text-sm font-medium`,children:f(e)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]})]}),(0,S.jsx)(m,{variant:`outline`,className:Ce(e),children:e.status})]},e.id))}):(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No team selected`})})]}),(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Blackboard`}),X?(0,S.jsx)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:X.title||X.id}):null]}),(0,S.jsxs)(ce,{value:T,onValueChange:e=>D(e),children:[(0,S.jsx)(ae,{size:`sm`,className:`w-36`,children:(0,S.jsx)(ie,{})}),(0,S.jsx)(oe,{children:me.map(e=>(0,S.jsx)(se,{value:e.value,children:e.label},e.value))})]})]})}),(0,S.jsx)(v,{children:X?Te&&Z.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Loading blackboard…`}):Z.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`No blackboard entries`}):(0,S.jsx)(g,{className:`h-[640px] pr-3`,children:(0,S.jsx)(`div`,{className:`space-y-5`,children:ze.map(e=>{let t=Re[e];return t.length===0?null:(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(m,{variant:`secondary`,children:he[e]}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:t.length})]}),(0,S.jsx)(`div`,{className:`space-y-2`,children:t.map(e=>(0,S.jsxs)(`div`,{className:`space-y-3 rounded-md border border-border bg-background p-3`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:e.title}),(0,S.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[`by `,E(e.authorId),` · `,d(P,e.updatedAt||e.createdAt)]})]}),(0,S.jsxs)(`div`,{className:`flex flex-wrap justify-end gap-1`,children:[(0,S.jsx)(m,{variant:`outline`,className:ye[e.status],children:e.status}),e.supersedes?(0,S.jsxs)(m,{variant:`outline`,children:[`supersedes `,E(e.supersedes)]}):null]})]}),e.body?(0,S.jsx)(`p`,{className:`whitespace-pre-wrap text-sm text-muted-foreground`,children:e.body}):null]},e.id))})]},e)})})}):(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`No team selected`})})]})]})]})]})}export{D as TeamsView};
|
|
2
|
-
//# sourceMappingURL=teams-
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{On as t,R as n,g as r,gr as i,ir as a,rr as o,rt as ee,tr as s,u as te}from"./lucide-react-CoU_Xts2.js";import{i as ne,n as c,t as l}from"./store-zFLzd0Jd.js";import{d as u}from"./types-CRptbSYh.js";import{G as re,K as d,d as f,h as p}from"./display-GFbGUG0O.js";import{a as ie,i as ae,n as oe,r as se,t as ce}from"./select-BIfbVmLf.js";import{t as m}from"./badge-XEru6wto.js";import{t as h}from"./button-C3CQSr4p.js";import{t as le}from"./input-CXMbJ848.js";import{G as g,X as ue}from"./index-DnUo0Zqg.js";import{o as _,r as v,s as y,t as b}from"./card-C5XTIPvq.js";import{t as de}from"./PlanGraphPanel-CSdszmGk.js";var x=e(i(),1),S=o();function C(e){if(e==null)return`-`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.round(t/60);if(n<60)return`${n}m`;let r=Math.round(n/60);return r<48?`${r}h`:`${Math.round(r/24)}d`}function w(e){return`${Math.round(e*100)}%`}function T(e){return e===`success`?`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`:e===`partial`?`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`:e===`failed`?`bg-red-500/10 text-red-400 border-red-500/20`:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`}function fe({data:e,loading:n,error:r}){let i=e?.totals.byOutcomeStatus,a=e?.compositionShapes.slice(0,6)??[],o=e?.recent.slice(0,8)??[];return(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`span`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(t,{className:`size-4 text-primary`}),` Outcomes`]}),(0,S.jsxs)(m,{variant:`secondary`,children:[e?.totals.total??0,` dissolved`]})]})}),(0,S.jsxs)(v,{className:`space-y-5`,children:[r?(0,S.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:r}):null,n&&!e?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`Loading outcomes…`}):!e||e.totals.total===0?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No dissolved teams yet`}):(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`div`,{className:`flex flex-wrap gap-2 text-xs`,children:[`success`,`partial`,`failed`,`unknown`].map(e=>{let t=i?.[e]??0;return(0,S.jsxs)(m,{variant:`outline`,className:t===0?`text-muted-foreground`:T(e===`unknown`?null:e),children:[t,` `,e]},e)})}),(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:`Composition win-rate`}),(0,S.jsx)(`div`,{className:`divide-y divide-border rounded-md border border-border`,children:a.map(e=>(0,S.jsxs)(`div`,{className:`grid gap-2 px-3 py-2 text-sm md:grid-cols-[1fr_auto_auto] md:items-center`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`break-words font-medium`,title:e.shape,children:e.shape}),(0,S.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e.count,` team`,e.count===1?``:`s`,` · avg `,C(e.avgWallClockMs)]})]}),(0,S.jsxs)(`span`,{className:`text-muted-foreground`,children:[e.successCount,`/`,e.count]}),(0,S.jsx)(m,{variant:`outline`,className:T(e.successRate>=.5?`success`:e.successRate>0?`partial`:`failed`),children:w(e.successRate)})]},e.shape))})]}),(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:`Recent outcomes`}),(0,S.jsx)(`div`,{className:`overflow-hidden rounded-md border border-border`,children:(0,S.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,S.jsx)(`thead`,{className:`bg-muted/40 text-xs text-muted-foreground`,children:(0,S.jsxs)(`tr`,{children:[(0,S.jsx)(`th`,{className:`px-3 py-2 text-left font-medium`,children:`Team`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-left font-medium`,children:`Outcome`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-right font-medium`,children:`Members`}),(0,S.jsx)(`th`,{className:`px-3 py-2 text-right font-medium`,children:`Duration`})]})}),(0,S.jsx)(`tbody`,{className:`divide-y divide-border`,children:o.map(e=>(0,S.jsxs)(`tr`,{children:[(0,S.jsxs)(`td`,{className:`max-w-[220px] px-3 py-2`,children:[(0,S.jsx)(`div`,{className:`truncate font-medium`,children:e.title||e.id.slice(0,12)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]}),(0,S.jsx)(`td`,{className:`px-3 py-2`,children:(0,S.jsx)(m,{variant:`outline`,className:T(e.outcome),children:e.outcome??`unknown`})}),(0,S.jsx)(`td`,{className:`px-3 py-2 text-right`,children:e.memberCount}),(0,S.jsx)(`td`,{className:`px-3 py-2 text-right text-muted-foreground`,children:C(e.durationMs)})]},e.id))})]})})]})]})]})]})}var pe=[],me=[{value:`all`,label:`All kinds`},{value:`decision`,label:`Decisions`},{value:`fact`,label:`Facts`},{value:`ruled_out`,label:`Ruled out`}],he={decision:`decision`,fact:`fact`,ruled_out:`ruled out`};function ge(e){return e.runtimeState?e.runtimeState:e.status===`dissolved`?`dissolved`:e.zeroMembersSince==null?`active`:`orphaned`}var _e={busy:`bg-blue-500/10 text-blue-400 border-blue-500/20`,idle:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,degraded:`bg-orange-500/10 text-orange-400 border-orange-500/20`,stalled:`bg-amber-500/10 text-amber-400 border-amber-500/20`,active:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,orphaned:`bg-amber-500/10 text-amber-400 border-amber-500/20`,dissolved:`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`},ve={success:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,partial:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,failed:`bg-red-500/10 text-red-400 border-red-500/20`},ye={active:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`,superseded:`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`,retracted:`bg-red-500/10 text-red-400 border-red-500/20`};function E(e){return e?e.slice(0,12):`-`}function be(e){if(e===void 0)return`-`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.round(t/60);if(n<60)return`${n}m`;let r=Math.round(n/60);return r<48?`${r}h`:`${Math.round(r/24)}d`}function xe(e){if(!e.composition)return null;try{let t=JSON.parse(e.composition);return Array.isArray(t)?t.length:null}catch{return null}}function Se(e,t){let n=t.trim().toLowerCase();return n?[e.title,e.id,e.coordinatorId,e.rootCoordinatorId,e.outcomeStatus,e.outcomeSummary].some(e=>String(e??``).toLowerCase().includes(n)):!0}function Ce(e){return e.status===`offline`?`bg-zinc-500/10 text-zinc-400 border-zinc-500/20`:e.status===`busy`?`bg-yellow-500/10 text-yellow-400 border-yellow-500/20`:e.status===`stale`?`bg-red-500/10 text-red-400 border-red-500/20`:`bg-emerald-500/10 text-emerald-400 border-emerald-500/20`}function D(){let[e,t]=(0,x.useState)(null),[i,o]=(0,x.useState)([]),[C,w]=(0,x.useState)(null),[T,D]=(0,x.useState)(`all`),[O,we]=(0,x.useState)(``),[k,A]=(0,x.useState)(!1),[Te,j]=(0,x.useState)(!1),[M,N]=(0,x.useState)(null),[P,Ee]=(0,x.useState)(()=>Date.now()),F=l(e=>e.teams),I=e??F[0]?.id??null,L=I?c(`team`,I):``,R=l(e=>e.teamsLoading),z=l(e=>e.teamsError),De=l(e=>e.fetchTeams),B=l(e=>(I?e.issuesByEntity[L]:void 0)??pe),V=l(e=>e.issueRefsById),Oe=l(e=>L?!!e.issueLoading[L]:!1),H=l(e=>L?e.issueErrors[L]??null:null),U=l(e=>e.fetchIssuesForEntity),ke=l(e=>e.teamOutcomeAggregates),Ae=l(e=>e.teamOutcomeAggregatesLoading),je=l(e=>e.teamOutcomeAggregatesError),Me=l(e=>e.fetchTeamOutcomeAggregates),W=l(e=>e.fetchPlan),Ne=l(e=>e.dissolveTeam),Pe=l(e=>e.openConfirm),G=l(e=>e.agentsById),Fe=l(e=>e.switchView),Ie=l(e=>e.openInboxThread),Le=l(e=>I?e.plansByTeam[ne(I)]??null:null),K=e=>{let t=G[e];return t?f(t):e.slice(-8)};async function q(e,t=T){j(!0),N(null);try{let n=new URLSearchParams({team:e});t!==`all`&&n.set(`kind`,t),o((await a(`GET`,`/blackboard?${n.toString()}`)).entries),w(e)}catch(t){N(u(t)),o([]),w(e)}finally{j(!1)}}async function J(){A(!0),N(null);try{let[t]=await Promise.all([De(),Me()]);Ee(Date.now());let n=e??t[0]?.id;n?(W({teamId:n}),await Promise.all([q(n,T),U(`team`,n)])):(o([]),w(null))}catch(e){N(u(e))}finally{A(!1)}}(0,x.useEffect)(()=>{J()},[]),(0,x.useEffect)(()=>{C&&q(C,T)},[T]);let Y=(0,x.useMemo)(()=>F.filter(e=>Se(e,O)),[F,O]),X=F.find(e=>e.id===I)??null,Z=C===I?i:[],Q=(0,x.useMemo)(()=>B.map(e=>({...e,issueRef:V[e.issueRef.id]??e.issueRef})),[V,B]),$=(0,x.useMemo)(()=>I?Object.values(G).filter(e=>e.teamId===I).sort((e,t)=>f(e).localeCompare(f(t))||e.id.localeCompare(t.id)):[],[G,I]),Re=(0,x.useMemo)(()=>{let e={decision:[],fact:[],ruled_out:[]};for(let t of Z)e[t.kind].push(t);return e},[Z]),ze=T===`all`?[`decision`,`fact`,`ruled_out`]:[T];return(0,S.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(te,{className:`size-5 text-primary`}),(0,S.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Teams`}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`active and dissolved team working sets`})]}),(0,S.jsxs)(h,{variant:`outline`,size:`sm`,onClick:()=>void J(),disabled:k||R,children:[(0,S.jsx)(n,{className:s(`size-4`,(k||R)&&`animate-spin`)}),` Refresh`]})]}),(M||z)&&(0,S.jsx)(`div`,{className:`rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:M||z}),(0,S.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[minmax(320px,420px)_1fr]`,children:[(0,S.jsxs)(`div`,{className:`min-h-[520px] min-w-0 space-y-3`,children:[(0,S.jsxs)(`div`,{className:`space-y-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-center justify-between gap-3`,children:[(0,S.jsx)(`span`,{children:`Team list`}),(0,S.jsx)(m,{variant:`secondary`,children:F.length})]}),(0,S.jsx)(le,{value:O,onChange:e=>we(e.target.value),placeholder:`Filter teams`,className:`h-8`})]}),(0,S.jsx)(`div`,{children:(k||R)&&F.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Loading teams…`}):Y.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:F.length===0?`No teams`:`No teams match`}):(0,S.jsx)(g,{className:`h-[610px] pr-3`,children:(0,S.jsx)(`div`,{className:`space-y-2`,children:Y.map(e=>{let n=e.id===I,r=xe(e),i=ge(e),a=e.coordinatorId,o=()=>{t(e.id),W({teamId:e.id}),U(`team`,e.id),q(e.id,T)};return(0,S.jsx)(`div`,{role:`button`,tabIndex:0,onClick:o,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),o())},className:`block w-full cursor-pointer text-left`,children:(0,S.jsx)(b,{className:s(`transition-colors hover:border-primary/50`,n&&`border-primary/60 bg-primary/5`),children:(0,S.jsxs)(v,{className:`space-y-3 p-3`,children:[(0,S.jsxs)(`div`,{className:`flex items-start justify-between gap-2`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`truncate text-sm font-medium`,children:e.title||E(e.id)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]}),(0,S.jsx)(m,{variant:`outline`,className:_e[i],children:i})]}),(0,S.jsxs)(`div`,{className:`grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-muted-foreground`,children:[(0,S.jsx)(`span`,{children:`Coordinator`}),a?(0,S.jsxs)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),Ie(a),Fe(`chat`)},className:`flex min-w-0 items-center justify-end gap-1 text-right text-primary hover:underline`,title:`Chat with ${a}`,children:[(0,S.jsx)(ee,{className:`size-3 shrink-0`}),(0,S.jsx)(`span`,{className:`truncate`,children:K(a)})]}):(0,S.jsx)(`span`,{className:`text-right text-muted-foreground`,children:`—`}),(0,S.jsx)(`span`,{children:`Members`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,children:r??`-`}),(0,S.jsx)(`span`,{children:`Created`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,title:p(e.createdAt),children:d(P,e.createdAt)}),e.dissolvedAt?(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`span`,{children:`Dissolved`}),(0,S.jsx)(`span`,{className:`text-right text-foreground`,title:p(e.dissolvedAt),children:d(P,e.dissolvedAt)})]}):null]}),i===`orphaned`&&e.zeroMembersSince!=null?(0,S.jsx)(`div`,{className:`flex items-center gap-1.5 border-t border-border pt-2 text-xs text-amber-400`,title:p(e.zeroMembersSince),children:(0,S.jsxs)(`span`,{children:[`No live members since `,d(P,e.zeroMembersSince),` — auto-dissolving`]})}):null,e.status===`dissolved`?(0,S.jsxs)(`div`,{className:`space-y-1 border-t border-border pt-2`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[e.outcomeStatus?(0,S.jsx)(m,{variant:`outline`,className:ve[e.outcomeStatus],children:e.outcomeStatus}):(0,S.jsx)(m,{variant:`outline`,children:`no outcome`}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:be(e.wallClockMs)})]}),e.outcomeSummary?(0,S.jsx)(`p`,{className:`line-clamp-2 text-xs text-muted-foreground`,children:e.outcomeSummary}):null]}):null]})})},e.id)})})})})]}),(0,S.jsxs)(`div`,{className:`min-h-[520px] min-w-0 space-y-4`,children:[(0,S.jsx)(fe,{data:ke,loading:Ae,error:je}),X?(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Issues`}),(0,S.jsxs)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:[Q.length,` associated`]})]}),Oe?(0,S.jsx)(m,{variant:`secondary`,children:`loading`}):null]})}),(0,S.jsx)(v,{children:H?(0,S.jsx)(`div`,{className:`text-xs text-destructive`,children:H}):Q.length===0?(0,S.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:`No associated issues`}):(0,S.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:Q.map(({association:e,issueRef:t})=>(0,S.jsx)(ue,{issueRef:t,now:P,showSync:!0},`${e.issueRefId}:${e.createdAt}`))})})]}):null,X&&Le?(0,S.jsx)(b,{className:`p-0`,children:(0,S.jsx)(de,{teamId:X.id})}):null,(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Roster`}),(0,S.jsxs)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:[$.length,` member`,$.length===1?``:`s`]})]}),X&&X.status!==`dissolved`?(0,S.jsxs)(h,{variant:`destructive`,size:`sm`,onClick:()=>Pe(`Dissolve Team`,`Dissolve "${X.title||X.id}"? This closes the team working set but keeps its records visible.`,()=>void Ne(X.id)),children:[(0,S.jsx)(r,{className:`size-4`}),` Dissolve`]}):null]})}),(0,S.jsx)(v,{children:X?$.length===0?(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No members`}):(0,S.jsx)(`div`,{className:`grid gap-2 md:grid-cols-2`,children:$.map(e=>(0,S.jsxs)(`div`,{className:`flex min-w-0 items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2`,children:[(0,S.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,S.jsx)(`span`,{className:s(`h-2 w-2 shrink-0 rounded-full`,re(e,P))}),(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`truncate text-sm font-medium`,children:f(e)}),(0,S.jsx)(`div`,{className:`truncate text-xs text-muted-foreground`,children:e.id})]})]}),(0,S.jsx)(m,{variant:`outline`,className:Ce(e),children:e.status})]},e.id))}):(0,S.jsx)(`div`,{className:`py-8 text-center text-sm text-muted-foreground`,children:`No team selected`})})]}),(0,S.jsxs)(b,{children:[(0,S.jsx)(_,{children:(0,S.jsxs)(y,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`span`,{children:`Blackboard`}),X?(0,S.jsx)(`span`,{className:`ml-2 text-xs font-normal text-muted-foreground`,children:X.title||X.id}):null]}),(0,S.jsxs)(ce,{value:T,onValueChange:e=>D(e),children:[(0,S.jsx)(ae,{size:`sm`,className:`w-36`,children:(0,S.jsx)(ie,{})}),(0,S.jsx)(oe,{children:me.map(e=>(0,S.jsx)(se,{value:e.value,children:e.label},e.value))})]})]})}),(0,S.jsx)(v,{children:X?Te&&Z.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`Loading blackboard…`}):Z.length===0?(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`No blackboard entries`}):(0,S.jsx)(g,{className:`h-[640px] pr-3`,children:(0,S.jsx)(`div`,{className:`space-y-5`,children:ze.map(e=>{let t=Re[e];return t.length===0?null:(0,S.jsxs)(`section`,{className:`space-y-2`,children:[(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(m,{variant:`secondary`,children:he[e]}),(0,S.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:t.length})]}),(0,S.jsx)(`div`,{className:`space-y-2`,children:t.map(e=>(0,S.jsxs)(`div`,{className:`space-y-3 rounded-md border border-border bg-background p-3`,children:[(0,S.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,S.jsxs)(`div`,{className:`min-w-0`,children:[(0,S.jsx)(`div`,{className:`text-sm font-medium`,children:e.title}),(0,S.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[`by `,E(e.authorId),` · `,d(P,e.updatedAt||e.createdAt)]})]}),(0,S.jsxs)(`div`,{className:`flex flex-wrap justify-end gap-1`,children:[(0,S.jsx)(m,{variant:`outline`,className:ye[e.status],children:e.status}),e.supersedes?(0,S.jsxs)(m,{variant:`outline`,children:[`supersedes `,E(e.supersedes)]}):null]})]}),e.body?(0,S.jsx)(`p`,{className:`whitespace-pre-wrap text-sm text-muted-foreground`,children:e.body}):null]},e.id))})]},e)})})}):(0,S.jsx)(`div`,{className:`py-16 text-center text-sm text-muted-foreground`,children:`No team selected`})})]})]})]})]})}export{D as TeamsView};
|
|
2
|
+
//# sourceMappingURL=teams-EE8RHhHM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-DI-Lhhbl.js","names":[],"sources":["../../dashboard/src/components/teams/TeamOutcomesCard.tsx","../../dashboard/src/components/views/teams.tsx"],"sourcesContent":["import { BarChart3 } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport type { TeamOutcomeAggregates } from '@/store/types'\n\nfunction duration(ms: number | null | undefined): string {\n if (ms == null) return '-'\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n return `${Math.round(hours / 24)}d`\n}\n\nfunction percent(value: number): string {\n return `${Math.round(value * 100)}%`\n}\n\nfunction outcomeTone(outcome: string | null | undefined): string {\n if (outcome === 'success') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'\n if (outcome === 'partial') return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'\n if (outcome === 'failed') return 'bg-red-500/10 text-red-400 border-red-500/20'\n return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n}\n\nexport function TeamOutcomesCard({\n data,\n loading,\n error,\n}: {\n data: TeamOutcomeAggregates | null\n loading: boolean\n error: string | null\n}) {\n const totals = data?.totals.byOutcomeStatus\n const shapes = data?.compositionShapes.slice(0, 6) ?? []\n const recent = data?.recent.slice(0, 8) ?? []\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <span className=\"flex items-center gap-2\"><BarChart3 className=\"size-4 text-primary\" /> Outcomes</span>\n <Badge variant=\"secondary\">{data?.totals.total ?? 0} dissolved</Badge>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {error ? <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div> : null}\n {loading && !data ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading outcomes…</div>\n ) : !data || data.totals.total === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No dissolved teams yet</div>\n ) : (\n <>\n <div className=\"flex flex-wrap gap-2 text-xs\">\n {(['success', 'partial', 'failed', 'unknown'] as const).map((outcome) => {\n const count = totals?.[outcome] ?? 0\n // Number-first to echo the \"N dissolved\" header badge; zero counts stay\n // neutral so empty buckets (e.g. \"0 failed\") don't shout in alarm colors.\n return (\n <Badge key={outcome} variant=\"outline\" className={count === 0 ? 'text-muted-foreground' : outcomeTone(outcome === 'unknown' ? null : outcome)}>\n {count} {outcome}\n </Badge>\n )\n })}\n </div>\n\n <section className=\"space-y-2\">\n <div className=\"text-sm font-medium\">Composition win-rate</div>\n <div className=\"divide-y divide-border rounded-md border border-border\">\n {shapes.map((shape) => (\n <div key={shape.shape} className=\"grid gap-2 px-3 py-2 text-sm md:grid-cols-[1fr_auto_auto] md:items-center\">\n <div className=\"min-w-0\">\n {/* The composition is this table's primary key — wrap it rather than\n truncate so it stays readable at narrow widths instead of hiding\n which roster actually won behind a tooltip. */}\n <div className=\"break-words font-medium\" title={shape.shape}>{shape.shape}</div>\n <div className=\"text-xs text-muted-foreground\">{shape.count} team{shape.count === 1 ? '' : 's'} · avg {duration(shape.avgWallClockMs)}</div>\n </div>\n <span className=\"text-muted-foreground\">{shape.successCount}/{shape.count}</span>\n <Badge variant=\"outline\" className={outcomeTone(shape.successRate >= 0.5 ? 'success' : shape.successRate > 0 ? 'partial' : 'failed')}>\n {percent(shape.successRate)}\n </Badge>\n </div>\n ))}\n </div>\n </section>\n\n <section className=\"space-y-2\">\n <div className=\"text-sm font-medium\">Recent outcomes</div>\n <div className=\"overflow-hidden rounded-md border border-border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/40 text-xs text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left font-medium\">Team</th>\n <th className=\"px-3 py-2 text-left font-medium\">Outcome</th>\n <th className=\"px-3 py-2 text-right font-medium\">Members</th>\n <th className=\"px-3 py-2 text-right font-medium\">Duration</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {recent.map((team) => (\n <tr key={team.id}>\n <td className=\"max-w-[220px] px-3 py-2\">\n <div className=\"truncate font-medium\">{team.title || team.id.slice(0, 12)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{team.id}</div>\n </td>\n <td className=\"px-3 py-2\"><Badge variant=\"outline\" className={outcomeTone(team.outcome)}>{team.outcome ?? 'unknown'}</Badge></td>\n <td className=\"px-3 py-2 text-right\">{team.memberCount}</td>\n <td className=\"px-3 py-2 text-right text-muted-foreground\">{duration(team.durationMs)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </section>\n </>\n )}\n </CardContent>\n </Card>\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { MessageSquare, RefreshCw, Trash2, Users } from 'lucide-react'\nimport { api } from '@/lib/api'\nimport { useRelayStore } from '@/store'\nimport { cn } from '@/lib/utils'\nimport { displayName, fmtTime, statusDotColor, timeAgo } from '@/lib/display'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { PlanGraphPanel } from '@/components/chat/PlanGraphPanel'\nimport { IssueRefChip } from '@/components/shared/issue-ref-chip'\nimport { TeamOutcomesCard } from '@/components/teams/TeamOutcomesCard'\nimport { issueEntityKey } from '@/store/slices/issues'\nimport { planTeamLookupKey } from '@/store/slices/plans'\nimport type { Agent, BlackboardEntry, BlackboardEntryKind, BlackboardEntryStatus, Team } from '@/types'\nimport type { IssueAssociationIssue } from '@/lib/issue-types'\n\ninterface BlackboardResponse {\n entries: BlackboardEntry[]\n}\n\n// Stable reference for the empty case: a selector returning a fresh `[]` each\n// render breaks Zustand v5's useSyncExternalStore snapshot equality → infinite\n// re-render loop. Mirrors EMPTY_AGENT_CHAT_WINDOW in store/slices/chat.ts.\nconst EMPTY_TEAM_ISSUES: IssueAssociationIssue[] = []\n\ntype KindFilter = 'all' | BlackboardEntryKind\n\nconst KIND_OPTIONS: Array<{ value: KindFilter; label: string }> = [\n { value: 'all', label: 'All kinds' },\n { value: 'decision', label: 'Decisions' },\n { value: 'fact', label: 'Facts' },\n { value: 'ruled_out', label: 'Ruled out' },\n]\n\nconst KIND_LABEL: Record<BlackboardEntryKind, string> = {\n decision: 'decision',\n fact: 'fact',\n ruled_out: 'ruled out',\n}\n\n// Display state merges the server-derived runtimeState with the local orphaned signal.\n// runtimeState comes from the server (relay_team_get / GET /teams) and covers busy/idle/\n// degraded/stalled/dissolved. \"orphaned\" is a local fallback for active teams with\n// zeroMembersSince set but no server runtimeState yet.\ntype TeamDisplayState = 'busy' | 'idle' | 'degraded' | 'stalled' | 'active' | 'orphaned' | 'dissolved'\n\nfunction teamDisplayState(team: Team): TeamDisplayState {\n if (team.runtimeState) return team.runtimeState\n if (team.status === 'dissolved') return 'dissolved'\n if (team.zeroMembersSince != null) return 'orphaned'\n return 'active'\n}\n\nconst DISPLAY_STATE_CLASS: Record<TeamDisplayState, string> = {\n busy: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n idle: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n degraded: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n stalled: 'bg-amber-500/10 text-amber-400 border-amber-500/20',\n active: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n orphaned: 'bg-amber-500/10 text-amber-400 border-amber-500/20',\n dissolved: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n}\n\nconst OUTCOME_STATUS_CLASS: Record<NonNullable<Team['outcomeStatus']>, string> = {\n success: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n partial: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n failed: 'bg-red-500/10 text-red-400 border-red-500/20',\n}\n\nconst ENTRY_STATUS_CLASS: Record<BlackboardEntryStatus, string> = {\n active: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n superseded: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n retracted: 'bg-red-500/10 text-red-400 border-red-500/20',\n}\n\nfunction shortId(id: string | undefined): string {\n return id ? id.slice(0, 12) : '-'\n}\n\nfunction duration(ms: number | undefined): string {\n if (ms === undefined) return '-'\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n return `${Math.round(hours / 24)}d`\n}\n\nfunction compositionCount(team: Team): number | null {\n if (!team.composition) return null\n try {\n const parsed = JSON.parse(team.composition) as unknown\n return Array.isArray(parsed) ? parsed.length : null\n } catch {\n return null\n }\n}\n\nfunction matchesTeamFilter(team: Team, filter: string): boolean {\n const q = filter.trim().toLowerCase()\n if (!q) return true\n return [\n team.title,\n team.id,\n team.coordinatorId,\n team.rootCoordinatorId,\n team.outcomeStatus,\n team.outcomeSummary,\n ].some((value) => String(value ?? '').toLowerCase().includes(q))\n}\n\nfunction agentStatusClass(agent: Agent): string {\n if (agent.status === 'offline') return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n if (agent.status === 'busy') return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'\n if (agent.status === 'stale') return 'bg-red-500/10 text-red-400 border-red-500/20'\n return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'\n}\n\nexport function TeamsView() {\n const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null)\n const [entries, setEntries] = useState<BlackboardEntry[]>([])\n const [entriesTeamId, setEntriesTeamId] = useState<string | null>(null)\n const [kindFilter, setKindFilter] = useState<KindFilter>('all')\n const [teamFilter, setTeamFilter] = useState('')\n const [refreshing, setRefreshing] = useState(false)\n const [entriesLoading, setEntriesLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [now, setNow] = useState(() => Date.now())\n const teams = useRelayStore((s) => s.teams)\n const displayedTeamId = selectedTeamId ?? teams[0]?.id ?? null\n const teamIssueKey = displayedTeamId ? issueEntityKey('team', displayedTeamId) : ''\n const teamsLoading = useRelayStore((s) => s.teamsLoading)\n const teamsError = useRelayStore((s) => s.teamsError)\n const fetchTeams = useRelayStore((s) => s.fetchTeams)\n const teamIssues = useRelayStore((s) => (displayedTeamId ? s.issuesByEntity[teamIssueKey] : undefined) ?? EMPTY_TEAM_ISSUES)\n const issueRefsById = useRelayStore((s) => s.issueRefsById)\n const teamIssuesLoading = useRelayStore((s) => teamIssueKey ? Boolean(s.issueLoading[teamIssueKey]) : false)\n const teamIssuesError = useRelayStore((s) => teamIssueKey ? (s.issueErrors[teamIssueKey] ?? null) : null)\n const fetchIssuesForEntity = useRelayStore((s) => s.fetchIssuesForEntity)\n const teamOutcomeAggregates = useRelayStore((s) => s.teamOutcomeAggregates)\n const teamOutcomeAggregatesLoading = useRelayStore((s) => s.teamOutcomeAggregatesLoading)\n const teamOutcomeAggregatesError = useRelayStore((s) => s.teamOutcomeAggregatesError)\n const fetchTeamOutcomeAggregates = useRelayStore((s) => s.fetchTeamOutcomeAggregates)\n const fetchPlan = useRelayStore((s) => s.fetchPlan)\n const dissolveTeam = useRelayStore((s) => s.dissolveTeam)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const agentsById = useRelayStore((s) => s.agentsById)\n const switchView = useRelayStore((s) => s.switchView)\n const openInboxThread = useRelayStore((s) => s.openInboxThread)\n // Read the selected team's plan from the store so the drill-in can gate the\n // plan card on its existence (no empty card for plan-less teams). teams.tsx\n // owns the fetch below; PlanGraphPanel then renders it from the store.\n const teamPlan = useRelayStore((s) => {\n return displayedTeamId ? s.plansByTeam[planTeamLookupKey(displayedTeamId)] ?? null : null\n })\n const coordinatorLabel = (id: string): string => {\n const agent = agentsById[id]\n return agent ? displayName(agent) : id.slice(-8)\n }\n\n async function loadEntries(teamId: string, nextKind: KindFilter = kindFilter) {\n setEntriesLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ team: teamId })\n if (nextKind !== 'all') params.set('kind', nextKind)\n const payload = await api<BlackboardResponse>('GET', `/blackboard?${params.toString()}`)\n setEntries(payload.entries)\n setEntriesTeamId(teamId)\n } catch (e) {\n setError(errMessage(e))\n setEntries([])\n setEntriesTeamId(teamId)\n } finally {\n setEntriesLoading(false)\n }\n }\n\n async function refresh() {\n setRefreshing(true)\n setError(null)\n try {\n const [nextTeams] = await Promise.all([fetchTeams(), fetchTeamOutcomeAggregates()])\n setNow(Date.now())\n const displayedTeamId = selectedTeamId ?? nextTeams[0]?.id\n if (displayedTeamId) {\n void fetchPlan({ teamId: displayedTeamId })\n await Promise.all([\n loadEntries(displayedTeamId, kindFilter),\n fetchIssuesForEntity('team', displayedTeamId),\n ])\n } else {\n setEntries([])\n setEntriesTeamId(null)\n }\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setRefreshing(false)\n }\n }\n\n // Mount: synchronize this local view with Relay's persisted team list.\n useEffect(() => {\n void refresh()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Filter change: re-query the currently loaded blackboard working set.\n useEffect(() => {\n if (entriesTeamId) void loadEntries(entriesTeamId, kindFilter)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [kindFilter])\n\n const visibleTeams = useMemo(() => teams.filter((team) => matchesTeamFilter(team, teamFilter)), [teams, teamFilter])\n const displayedTeam = teams.find((team) => team.id === displayedTeamId) ?? null\n const displayedEntries = entriesTeamId === displayedTeamId ? entries : []\n const displayedTeamIssues = useMemo(() => teamIssues.map((item) => ({\n ...item,\n issueRef: issueRefsById[item.issueRef.id] ?? item.issueRef,\n })), [issueRefsById, teamIssues])\n const teamRoster = useMemo(() => {\n if (!displayedTeamId) return []\n return Object.values(agentsById)\n .filter((agent) => agent.teamId === displayedTeamId)\n .sort((a, b) => displayName(a).localeCompare(displayName(b)) || a.id.localeCompare(b.id))\n }, [agentsById, displayedTeamId])\n\n const groupedEntries = useMemo(() => {\n const groups: Record<BlackboardEntryKind, BlackboardEntry[]> = { decision: [], fact: [], ruled_out: [] }\n for (const entry of displayedEntries) groups[entry.kind].push(entry)\n return groups\n }, [displayedEntries])\n\n const entryKinds = kindFilter === 'all'\n ? (['decision', 'fact', 'ruled_out'] as const)\n : ([kindFilter] as const)\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <Users className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Teams</h1>\n <span className=\"text-xs text-muted-foreground\">active and dissolved team working sets</span>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refresh()} disabled={refreshing || teamsLoading}>\n <RefreshCw className={cn('size-4', (refreshing || teamsLoading) && 'animate-spin')} /> Refresh\n </Button>\n </div>\n\n {(error || teamsError) && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error || teamsError}</div>\n )}\n\n <div className=\"grid gap-4 xl:grid-cols-[minmax(320px,420px)_1fr]\">\n <div className=\"min-h-[520px] min-w-0 space-y-3\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <span>Team list</span>\n <Badge variant=\"secondary\">{teams.length}</Badge>\n </div>\n <Input\n value={teamFilter}\n onChange={(e) => setTeamFilter(e.target.value)}\n placeholder=\"Filter teams\"\n className=\"h-8\"\n />\n </div>\n <div>\n {(refreshing || teamsLoading) && teams.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">Loading teams…</div>\n ) : visibleTeams.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">{teams.length === 0 ? 'No teams' : 'No teams match'}</div>\n ) : (\n <ScrollArea className=\"h-[610px] pr-3\">\n <div className=\"space-y-2\">\n {visibleTeams.map((team) => {\n const selected = team.id === displayedTeamId\n const members = compositionCount(team)\n const state = teamDisplayState(team)\n const coordId = team.coordinatorId\n const selectTeam = () => {\n setSelectedTeamId(team.id)\n void fetchPlan({ teamId: team.id })\n void fetchIssuesForEntity('team', team.id)\n void loadEntries(team.id, kindFilter)\n }\n return (\n // Clickable card, not a <button>: it nests an interactive \"chat with\n // coordinator\" link, and button-in-button is invalid HTML.\n <div\n key={team.id}\n role=\"button\"\n tabIndex={0}\n onClick={selectTeam}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectTeam() } }}\n className=\"block w-full cursor-pointer text-left\"\n >\n <Card className={cn('transition-colors hover:border-primary/50', selected && 'border-primary/60 bg-primary/5')}>\n <CardContent className=\"space-y-3 p-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{team.title || shortId(team.id)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{team.id}</div>\n </div>\n <Badge variant=\"outline\" className={DISPLAY_STATE_CLASS[state]}>{state}</Badge>\n </div>\n <div className=\"grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-muted-foreground\">\n <span>Coordinator</span>\n {coordId ? (\n <button\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); openInboxThread(coordId); switchView('chat') }}\n className=\"flex min-w-0 items-center justify-end gap-1 text-right text-primary hover:underline\"\n title={`Chat with ${coordId}`}\n >\n <MessageSquare className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{coordinatorLabel(coordId)}</span>\n </button>\n ) : (\n <span className=\"text-right text-muted-foreground\">—</span>\n )}\n <span>Members</span>\n <span className=\"text-right text-foreground\">{members ?? '-'}</span>\n <span>Created</span>\n <span className=\"text-right text-foreground\" title={fmtTime(team.createdAt)}>{timeAgo(now, team.createdAt)}</span>\n {team.dissolvedAt ? (\n <>\n <span>Dissolved</span>\n <span className=\"text-right text-foreground\" title={fmtTime(team.dissolvedAt)}>{timeAgo(now, team.dissolvedAt)}</span>\n </>\n ) : null}\n </div>\n {state === 'orphaned' && team.zeroMembersSince != null ? (\n <div className=\"flex items-center gap-1.5 border-t border-border pt-2 text-xs text-amber-400\" title={fmtTime(team.zeroMembersSince)}>\n <span>No live members since {timeAgo(now, team.zeroMembersSince)} — auto-dissolving</span>\n </div>\n ) : null}\n {team.status === 'dissolved' ? (\n <div className=\"space-y-1 border-t border-border pt-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {team.outcomeStatus ? (\n <Badge variant=\"outline\" className={OUTCOME_STATUS_CLASS[team.outcomeStatus]}>{team.outcomeStatus}</Badge>\n ) : (\n <Badge variant=\"outline\">no outcome</Badge>\n )}\n <span className=\"text-xs text-muted-foreground\">{duration(team.wallClockMs)}</span>\n </div>\n {team.outcomeSummary ? <p className=\"line-clamp-2 text-xs text-muted-foreground\">{team.outcomeSummary}</p> : null}\n </div>\n ) : null}\n </CardContent>\n </Card>\n </div>\n )\n })}\n </div>\n </ScrollArea>\n )}\n </div>\n </div>\n\n <div className=\"min-h-[520px] min-w-0 space-y-4\">\n <TeamOutcomesCard\n data={teamOutcomeAggregates}\n loading={teamOutcomeAggregatesLoading}\n error={teamOutcomeAggregatesError}\n />\n\n {displayedTeam ? (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Issues</span>\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{displayedTeamIssues.length} associated</span>\n </div>\n {teamIssuesLoading ? <Badge variant=\"secondary\">loading</Badge> : null}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {teamIssuesError ? (\n <div className=\"text-xs text-destructive\">{teamIssuesError}</div>\n ) : displayedTeamIssues.length === 0 ? (\n <div className=\"text-xs text-muted-foreground\">No associated issues</div>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {displayedTeamIssues.map(({ association, issueRef }) => (\n <IssueRefChip key={`${association.issueRefId}:${association.createdAt}`} issueRef={issueRef} now={now} showSync />\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {displayedTeam && teamPlan ? (\n // Wrap the chat-style plan overlay in a Card so it reads as a peer of\n // the Roster/Blackboard cards (rounded + solid bg + ring) instead of a\n // flat translucent strip. Gated on teamPlan so plan-less teams show no\n // empty card. p-0 lets the panel's own px-3 py-2 govern its padding.\n <Card className=\"p-0\">\n <PlanGraphPanel teamId={displayedTeam.id} />\n </Card>\n ) : null}\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Roster</span>\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{teamRoster.length} member{teamRoster.length === 1 ? '' : 's'}</span>\n </div>\n {displayedTeam && displayedTeam.status !== 'dissolved' ? (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => openConfirm(\n 'Dissolve Team',\n `Dissolve \"${displayedTeam.title || displayedTeam.id}\"? This closes the team working set but keeps its records visible.`,\n () => void dissolveTeam(displayedTeam.id),\n )}\n >\n <Trash2 className=\"size-4\" /> Dissolve\n </Button>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!displayedTeam ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No team selected</div>\n ) : teamRoster.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No members</div>\n ) : (\n <div className=\"grid gap-2 md:grid-cols-2\">\n {teamRoster.map((agent) => (\n <div key={agent.id} className=\"flex min-w-0 items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className={cn('h-2 w-2 shrink-0 rounded-full', statusDotColor(agent, now))} />\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{displayName(agent)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{agent.id}</div>\n </div>\n </div>\n <Badge variant=\"outline\" className={agentStatusClass(agent)}>{agent.status}</Badge>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Blackboard</span>\n {displayedTeam ? (\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{displayedTeam.title || displayedTeam.id}</span>\n ) : null}\n </div>\n <Select value={kindFilter} onValueChange={(value) => setKindFilter(value as KindFilter)}>\n <SelectTrigger size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {KIND_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!displayedTeam ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">No team selected</div>\n ) : entriesLoading && displayedEntries.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">Loading blackboard…</div>\n ) : displayedEntries.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">No blackboard entries</div>\n ) : (\n <ScrollArea className=\"h-[640px] pr-3\">\n <div className=\"space-y-5\">\n {entryKinds.map((kind) => {\n const kindEntries = groupedEntries[kind]\n if (kindEntries.length === 0) return null\n return (\n <section key={kind} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{KIND_LABEL[kind]}</Badge>\n <span className=\"text-xs text-muted-foreground\">{kindEntries.length}</span>\n </div>\n <div className=\"space-y-2\">\n {kindEntries.map((entry) => (\n <div key={entry.id} className=\"space-y-3 rounded-md border border-border bg-background p-3\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">{entry.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n by {shortId(entry.authorId)} · {timeAgo(now, entry.updatedAt || entry.createdAt)}\n </div>\n </div>\n <div className=\"flex flex-wrap justify-end gap-1\">\n <Badge variant=\"outline\" className={ENTRY_STATUS_CLASS[entry.status]}>{entry.status}</Badge>\n {entry.supersedes ? <Badge variant=\"outline\">supersedes {shortId(entry.supersedes)}</Badge> : null}\n </div>\n </div>\n {entry.body ? <p className=\"whitespace-pre-wrap text-sm text-muted-foreground\">{entry.body}</p> : null}\n </div>\n ))}\n </div>\n </section>\n )\n })}\n </div>\n </ScrollArea>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"teams-EE8RHhHM.js","names":[],"sources":["../../dashboard/src/components/teams/TeamOutcomesCard.tsx","../../dashboard/src/components/views/teams.tsx"],"sourcesContent":["import { BarChart3 } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport type { TeamOutcomeAggregates } from '@/store/types'\n\nfunction duration(ms: number | null | undefined): string {\n if (ms == null) return '-'\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n return `${Math.round(hours / 24)}d`\n}\n\nfunction percent(value: number): string {\n return `${Math.round(value * 100)}%`\n}\n\nfunction outcomeTone(outcome: string | null | undefined): string {\n if (outcome === 'success') return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'\n if (outcome === 'partial') return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'\n if (outcome === 'failed') return 'bg-red-500/10 text-red-400 border-red-500/20'\n return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n}\n\nexport function TeamOutcomesCard({\n data,\n loading,\n error,\n}: {\n data: TeamOutcomeAggregates | null\n loading: boolean\n error: string | null\n}) {\n const totals = data?.totals.byOutcomeStatus\n const shapes = data?.compositionShapes.slice(0, 6) ?? []\n const recent = data?.recent.slice(0, 8) ?? []\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <span className=\"flex items-center gap-2\"><BarChart3 className=\"size-4 text-primary\" /> Outcomes</span>\n <Badge variant=\"secondary\">{data?.totals.total ?? 0} dissolved</Badge>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {error ? <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error}</div> : null}\n {loading && !data ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">Loading outcomes…</div>\n ) : !data || data.totals.total === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No dissolved teams yet</div>\n ) : (\n <>\n <div className=\"flex flex-wrap gap-2 text-xs\">\n {(['success', 'partial', 'failed', 'unknown'] as const).map((outcome) => {\n const count = totals?.[outcome] ?? 0\n // Number-first to echo the \"N dissolved\" header badge; zero counts stay\n // neutral so empty buckets (e.g. \"0 failed\") don't shout in alarm colors.\n return (\n <Badge key={outcome} variant=\"outline\" className={count === 0 ? 'text-muted-foreground' : outcomeTone(outcome === 'unknown' ? null : outcome)}>\n {count} {outcome}\n </Badge>\n )\n })}\n </div>\n\n <section className=\"space-y-2\">\n <div className=\"text-sm font-medium\">Composition win-rate</div>\n <div className=\"divide-y divide-border rounded-md border border-border\">\n {shapes.map((shape) => (\n <div key={shape.shape} className=\"grid gap-2 px-3 py-2 text-sm md:grid-cols-[1fr_auto_auto] md:items-center\">\n <div className=\"min-w-0\">\n {/* The composition is this table's primary key — wrap it rather than\n truncate so it stays readable at narrow widths instead of hiding\n which roster actually won behind a tooltip. */}\n <div className=\"break-words font-medium\" title={shape.shape}>{shape.shape}</div>\n <div className=\"text-xs text-muted-foreground\">{shape.count} team{shape.count === 1 ? '' : 's'} · avg {duration(shape.avgWallClockMs)}</div>\n </div>\n <span className=\"text-muted-foreground\">{shape.successCount}/{shape.count}</span>\n <Badge variant=\"outline\" className={outcomeTone(shape.successRate >= 0.5 ? 'success' : shape.successRate > 0 ? 'partial' : 'failed')}>\n {percent(shape.successRate)}\n </Badge>\n </div>\n ))}\n </div>\n </section>\n\n <section className=\"space-y-2\">\n <div className=\"text-sm font-medium\">Recent outcomes</div>\n <div className=\"overflow-hidden rounded-md border border-border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/40 text-xs text-muted-foreground\">\n <tr>\n <th className=\"px-3 py-2 text-left font-medium\">Team</th>\n <th className=\"px-3 py-2 text-left font-medium\">Outcome</th>\n <th className=\"px-3 py-2 text-right font-medium\">Members</th>\n <th className=\"px-3 py-2 text-right font-medium\">Duration</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {recent.map((team) => (\n <tr key={team.id}>\n <td className=\"max-w-[220px] px-3 py-2\">\n <div className=\"truncate font-medium\">{team.title || team.id.slice(0, 12)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{team.id}</div>\n </td>\n <td className=\"px-3 py-2\"><Badge variant=\"outline\" className={outcomeTone(team.outcome)}>{team.outcome ?? 'unknown'}</Badge></td>\n <td className=\"px-3 py-2 text-right\">{team.memberCount}</td>\n <td className=\"px-3 py-2 text-right text-muted-foreground\">{duration(team.durationMs)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </section>\n </>\n )}\n </CardContent>\n </Card>\n )\n}\n","import { useEffect, useMemo, useState } from 'react'\nimport { errMessage } from 'agent-relay-sdk/types'\nimport { MessageSquare, RefreshCw, Trash2, Users } from 'lucide-react'\nimport { api } from '@/lib/api'\nimport { useRelayStore } from '@/store'\nimport { cn } from '@/lib/utils'\nimport { displayName, fmtTime, statusDotColor, timeAgo } from '@/lib/display'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { ScrollArea } from '@/components/ui/scroll-area'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { PlanGraphPanel } from '@/components/chat/PlanGraphPanel'\nimport { IssueRefChip } from '@/components/shared/issue-ref-chip'\nimport { TeamOutcomesCard } from '@/components/teams/TeamOutcomesCard'\nimport { issueEntityKey } from '@/store/slices/issues'\nimport { planTeamLookupKey } from '@/store/slices/plans'\nimport type { Agent, BlackboardEntry, BlackboardEntryKind, BlackboardEntryStatus, Team } from '@/types'\nimport type { IssueAssociationIssue } from '@/lib/issue-types'\n\ninterface BlackboardResponse {\n entries: BlackboardEntry[]\n}\n\n// Stable reference for the empty case: a selector returning a fresh `[]` each\n// render breaks Zustand v5's useSyncExternalStore snapshot equality → infinite\n// re-render loop. Mirrors EMPTY_AGENT_CHAT_WINDOW in store/slices/chat.ts.\nconst EMPTY_TEAM_ISSUES: IssueAssociationIssue[] = []\n\ntype KindFilter = 'all' | BlackboardEntryKind\n\nconst KIND_OPTIONS: Array<{ value: KindFilter; label: string }> = [\n { value: 'all', label: 'All kinds' },\n { value: 'decision', label: 'Decisions' },\n { value: 'fact', label: 'Facts' },\n { value: 'ruled_out', label: 'Ruled out' },\n]\n\nconst KIND_LABEL: Record<BlackboardEntryKind, string> = {\n decision: 'decision',\n fact: 'fact',\n ruled_out: 'ruled out',\n}\n\n// Display state merges the server-derived runtimeState with the local orphaned signal.\n// runtimeState comes from the server (relay_team_get / GET /teams) and covers busy/idle/\n// degraded/stalled/dissolved. \"orphaned\" is a local fallback for active teams with\n// zeroMembersSince set but no server runtimeState yet.\ntype TeamDisplayState = 'busy' | 'idle' | 'degraded' | 'stalled' | 'active' | 'orphaned' | 'dissolved'\n\nfunction teamDisplayState(team: Team): TeamDisplayState {\n if (team.runtimeState) return team.runtimeState\n if (team.status === 'dissolved') return 'dissolved'\n if (team.zeroMembersSince != null) return 'orphaned'\n return 'active'\n}\n\nconst DISPLAY_STATE_CLASS: Record<TeamDisplayState, string> = {\n busy: 'bg-blue-500/10 text-blue-400 border-blue-500/20',\n idle: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n degraded: 'bg-orange-500/10 text-orange-400 border-orange-500/20',\n stalled: 'bg-amber-500/10 text-amber-400 border-amber-500/20',\n active: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n orphaned: 'bg-amber-500/10 text-amber-400 border-amber-500/20',\n dissolved: 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20',\n}\n\nconst OUTCOME_STATUS_CLASS: Record<NonNullable<Team['outcomeStatus']>, string> = {\n success: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n partial: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n failed: 'bg-red-500/10 text-red-400 border-red-500/20',\n}\n\nconst ENTRY_STATUS_CLASS: Record<BlackboardEntryStatus, string> = {\n active: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n superseded: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',\n retracted: 'bg-red-500/10 text-red-400 border-red-500/20',\n}\n\nfunction shortId(id: string | undefined): string {\n return id ? id.slice(0, 12) : '-'\n}\n\nfunction duration(ms: number | undefined): string {\n if (ms === undefined) return '-'\n const seconds = Math.round(ms / 1000)\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n return `${Math.round(hours / 24)}d`\n}\n\nfunction compositionCount(team: Team): number | null {\n if (!team.composition) return null\n try {\n const parsed = JSON.parse(team.composition) as unknown\n return Array.isArray(parsed) ? parsed.length : null\n } catch {\n return null\n }\n}\n\nfunction matchesTeamFilter(team: Team, filter: string): boolean {\n const q = filter.trim().toLowerCase()\n if (!q) return true\n return [\n team.title,\n team.id,\n team.coordinatorId,\n team.rootCoordinatorId,\n team.outcomeStatus,\n team.outcomeSummary,\n ].some((value) => String(value ?? '').toLowerCase().includes(q))\n}\n\nfunction agentStatusClass(agent: Agent): string {\n if (agent.status === 'offline') return 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'\n if (agent.status === 'busy') return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20'\n if (agent.status === 'stale') return 'bg-red-500/10 text-red-400 border-red-500/20'\n return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'\n}\n\nexport function TeamsView() {\n const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null)\n const [entries, setEntries] = useState<BlackboardEntry[]>([])\n const [entriesTeamId, setEntriesTeamId] = useState<string | null>(null)\n const [kindFilter, setKindFilter] = useState<KindFilter>('all')\n const [teamFilter, setTeamFilter] = useState('')\n const [refreshing, setRefreshing] = useState(false)\n const [entriesLoading, setEntriesLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [now, setNow] = useState(() => Date.now())\n const teams = useRelayStore((s) => s.teams)\n const displayedTeamId = selectedTeamId ?? teams[0]?.id ?? null\n const teamIssueKey = displayedTeamId ? issueEntityKey('team', displayedTeamId) : ''\n const teamsLoading = useRelayStore((s) => s.teamsLoading)\n const teamsError = useRelayStore((s) => s.teamsError)\n const fetchTeams = useRelayStore((s) => s.fetchTeams)\n const teamIssues = useRelayStore((s) => (displayedTeamId ? s.issuesByEntity[teamIssueKey] : undefined) ?? EMPTY_TEAM_ISSUES)\n const issueRefsById = useRelayStore((s) => s.issueRefsById)\n const teamIssuesLoading = useRelayStore((s) => teamIssueKey ? Boolean(s.issueLoading[teamIssueKey]) : false)\n const teamIssuesError = useRelayStore((s) => teamIssueKey ? (s.issueErrors[teamIssueKey] ?? null) : null)\n const fetchIssuesForEntity = useRelayStore((s) => s.fetchIssuesForEntity)\n const teamOutcomeAggregates = useRelayStore((s) => s.teamOutcomeAggregates)\n const teamOutcomeAggregatesLoading = useRelayStore((s) => s.teamOutcomeAggregatesLoading)\n const teamOutcomeAggregatesError = useRelayStore((s) => s.teamOutcomeAggregatesError)\n const fetchTeamOutcomeAggregates = useRelayStore((s) => s.fetchTeamOutcomeAggregates)\n const fetchPlan = useRelayStore((s) => s.fetchPlan)\n const dissolveTeam = useRelayStore((s) => s.dissolveTeam)\n const openConfirm = useRelayStore((s) => s.openConfirm)\n const agentsById = useRelayStore((s) => s.agentsById)\n const switchView = useRelayStore((s) => s.switchView)\n const openInboxThread = useRelayStore((s) => s.openInboxThread)\n // Read the selected team's plan from the store so the drill-in can gate the\n // plan card on its existence (no empty card for plan-less teams). teams.tsx\n // owns the fetch below; PlanGraphPanel then renders it from the store.\n const teamPlan = useRelayStore((s) => {\n return displayedTeamId ? s.plansByTeam[planTeamLookupKey(displayedTeamId)] ?? null : null\n })\n const coordinatorLabel = (id: string): string => {\n const agent = agentsById[id]\n return agent ? displayName(agent) : id.slice(-8)\n }\n\n async function loadEntries(teamId: string, nextKind: KindFilter = kindFilter) {\n setEntriesLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ team: teamId })\n if (nextKind !== 'all') params.set('kind', nextKind)\n const payload = await api<BlackboardResponse>('GET', `/blackboard?${params.toString()}`)\n setEntries(payload.entries)\n setEntriesTeamId(teamId)\n } catch (e) {\n setError(errMessage(e))\n setEntries([])\n setEntriesTeamId(teamId)\n } finally {\n setEntriesLoading(false)\n }\n }\n\n async function refresh() {\n setRefreshing(true)\n setError(null)\n try {\n const [nextTeams] = await Promise.all([fetchTeams(), fetchTeamOutcomeAggregates()])\n setNow(Date.now())\n const displayedTeamId = selectedTeamId ?? nextTeams[0]?.id\n if (displayedTeamId) {\n void fetchPlan({ teamId: displayedTeamId })\n await Promise.all([\n loadEntries(displayedTeamId, kindFilter),\n fetchIssuesForEntity('team', displayedTeamId),\n ])\n } else {\n setEntries([])\n setEntriesTeamId(null)\n }\n } catch (e) {\n setError(errMessage(e))\n } finally {\n setRefreshing(false)\n }\n }\n\n // Mount: synchronize this local view with Relay's persisted team list.\n useEffect(() => {\n void refresh()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Filter change: re-query the currently loaded blackboard working set.\n useEffect(() => {\n if (entriesTeamId) void loadEntries(entriesTeamId, kindFilter)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [kindFilter])\n\n const visibleTeams = useMemo(() => teams.filter((team) => matchesTeamFilter(team, teamFilter)), [teams, teamFilter])\n const displayedTeam = teams.find((team) => team.id === displayedTeamId) ?? null\n const displayedEntries = entriesTeamId === displayedTeamId ? entries : []\n const displayedTeamIssues = useMemo(() => teamIssues.map((item) => ({\n ...item,\n issueRef: issueRefsById[item.issueRef.id] ?? item.issueRef,\n })), [issueRefsById, teamIssues])\n const teamRoster = useMemo(() => {\n if (!displayedTeamId) return []\n return Object.values(agentsById)\n .filter((agent) => agent.teamId === displayedTeamId)\n .sort((a, b) => displayName(a).localeCompare(displayName(b)) || a.id.localeCompare(b.id))\n }, [agentsById, displayedTeamId])\n\n const groupedEntries = useMemo(() => {\n const groups: Record<BlackboardEntryKind, BlackboardEntry[]> = { decision: [], fact: [], ruled_out: [] }\n for (const entry of displayedEntries) groups[entry.kind].push(entry)\n return groups\n }, [displayedEntries])\n\n const entryKinds = kindFilter === 'all'\n ? (['decision', 'fact', 'ruled_out'] as const)\n : ([kindFilter] as const)\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <Users className=\"size-5 text-primary\" />\n <h1 className=\"text-lg font-semibold\">Teams</h1>\n <span className=\"text-xs text-muted-foreground\">active and dissolved team working sets</span>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void refresh()} disabled={refreshing || teamsLoading}>\n <RefreshCw className={cn('size-4', (refreshing || teamsLoading) && 'animate-spin')} /> Refresh\n </Button>\n </div>\n\n {(error || teamsError) && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">{error || teamsError}</div>\n )}\n\n <div className=\"grid gap-4 xl:grid-cols-[minmax(320px,420px)_1fr]\">\n <div className=\"min-h-[520px] min-w-0 space-y-3\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <span>Team list</span>\n <Badge variant=\"secondary\">{teams.length}</Badge>\n </div>\n <Input\n value={teamFilter}\n onChange={(e) => setTeamFilter(e.target.value)}\n placeholder=\"Filter teams\"\n className=\"h-8\"\n />\n </div>\n <div>\n {(refreshing || teamsLoading) && teams.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">Loading teams…</div>\n ) : visibleTeams.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">{teams.length === 0 ? 'No teams' : 'No teams match'}</div>\n ) : (\n <ScrollArea className=\"h-[610px] pr-3\">\n <div className=\"space-y-2\">\n {visibleTeams.map((team) => {\n const selected = team.id === displayedTeamId\n const members = compositionCount(team)\n const state = teamDisplayState(team)\n const coordId = team.coordinatorId\n const selectTeam = () => {\n setSelectedTeamId(team.id)\n void fetchPlan({ teamId: team.id })\n void fetchIssuesForEntity('team', team.id)\n void loadEntries(team.id, kindFilter)\n }\n return (\n // Clickable card, not a <button>: it nests an interactive \"chat with\n // coordinator\" link, and button-in-button is invalid HTML.\n <div\n key={team.id}\n role=\"button\"\n tabIndex={0}\n onClick={selectTeam}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectTeam() } }}\n className=\"block w-full cursor-pointer text-left\"\n >\n <Card className={cn('transition-colors hover:border-primary/50', selected && 'border-primary/60 bg-primary/5')}>\n <CardContent className=\"space-y-3 p-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{team.title || shortId(team.id)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{team.id}</div>\n </div>\n <Badge variant=\"outline\" className={DISPLAY_STATE_CLASS[state]}>{state}</Badge>\n </div>\n <div className=\"grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-muted-foreground\">\n <span>Coordinator</span>\n {coordId ? (\n <button\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); openInboxThread(coordId); switchView('chat') }}\n className=\"flex min-w-0 items-center justify-end gap-1 text-right text-primary hover:underline\"\n title={`Chat with ${coordId}`}\n >\n <MessageSquare className=\"size-3 shrink-0\" />\n <span className=\"truncate\">{coordinatorLabel(coordId)}</span>\n </button>\n ) : (\n <span className=\"text-right text-muted-foreground\">—</span>\n )}\n <span>Members</span>\n <span className=\"text-right text-foreground\">{members ?? '-'}</span>\n <span>Created</span>\n <span className=\"text-right text-foreground\" title={fmtTime(team.createdAt)}>{timeAgo(now, team.createdAt)}</span>\n {team.dissolvedAt ? (\n <>\n <span>Dissolved</span>\n <span className=\"text-right text-foreground\" title={fmtTime(team.dissolvedAt)}>{timeAgo(now, team.dissolvedAt)}</span>\n </>\n ) : null}\n </div>\n {state === 'orphaned' && team.zeroMembersSince != null ? (\n <div className=\"flex items-center gap-1.5 border-t border-border pt-2 text-xs text-amber-400\" title={fmtTime(team.zeroMembersSince)}>\n <span>No live members since {timeAgo(now, team.zeroMembersSince)} — auto-dissolving</span>\n </div>\n ) : null}\n {team.status === 'dissolved' ? (\n <div className=\"space-y-1 border-t border-border pt-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {team.outcomeStatus ? (\n <Badge variant=\"outline\" className={OUTCOME_STATUS_CLASS[team.outcomeStatus]}>{team.outcomeStatus}</Badge>\n ) : (\n <Badge variant=\"outline\">no outcome</Badge>\n )}\n <span className=\"text-xs text-muted-foreground\">{duration(team.wallClockMs)}</span>\n </div>\n {team.outcomeSummary ? <p className=\"line-clamp-2 text-xs text-muted-foreground\">{team.outcomeSummary}</p> : null}\n </div>\n ) : null}\n </CardContent>\n </Card>\n </div>\n )\n })}\n </div>\n </ScrollArea>\n )}\n </div>\n </div>\n\n <div className=\"min-h-[520px] min-w-0 space-y-4\">\n <TeamOutcomesCard\n data={teamOutcomeAggregates}\n loading={teamOutcomeAggregatesLoading}\n error={teamOutcomeAggregatesError}\n />\n\n {displayedTeam ? (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Issues</span>\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{displayedTeamIssues.length} associated</span>\n </div>\n {teamIssuesLoading ? <Badge variant=\"secondary\">loading</Badge> : null}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {teamIssuesError ? (\n <div className=\"text-xs text-destructive\">{teamIssuesError}</div>\n ) : displayedTeamIssues.length === 0 ? (\n <div className=\"text-xs text-muted-foreground\">No associated issues</div>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {displayedTeamIssues.map(({ association, issueRef }) => (\n <IssueRefChip key={`${association.issueRefId}:${association.createdAt}`} issueRef={issueRef} now={now} showSync />\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {displayedTeam && teamPlan ? (\n // Wrap the chat-style plan overlay in a Card so it reads as a peer of\n // the Roster/Blackboard cards (rounded + solid bg + ring) instead of a\n // flat translucent strip. Gated on teamPlan so plan-less teams show no\n // empty card. p-0 lets the panel's own px-3 py-2 govern its padding.\n <Card className=\"p-0\">\n <PlanGraphPanel teamId={displayedTeam.id} />\n </Card>\n ) : null}\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Roster</span>\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{teamRoster.length} member{teamRoster.length === 1 ? '' : 's'}</span>\n </div>\n {displayedTeam && displayedTeam.status !== 'dissolved' ? (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => openConfirm(\n 'Dissolve Team',\n `Dissolve \"${displayedTeam.title || displayedTeam.id}\"? This closes the team working set but keeps its records visible.`,\n () => void dissolveTeam(displayedTeam.id),\n )}\n >\n <Trash2 className=\"size-4\" /> Dissolve\n </Button>\n ) : null}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!displayedTeam ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No team selected</div>\n ) : teamRoster.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">No members</div>\n ) : (\n <div className=\"grid gap-2 md:grid-cols-2\">\n {teamRoster.map((agent) => (\n <div key={agent.id} className=\"flex min-w-0 items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className={cn('h-2 w-2 shrink-0 rounded-full', statusDotColor(agent, now))} />\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{displayName(agent)}</div>\n <div className=\"truncate text-xs text-muted-foreground\">{agent.id}</div>\n </div>\n </div>\n <Badge variant=\"outline\" className={agentStatusClass(agent)}>{agent.status}</Badge>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <span>Blackboard</span>\n {displayedTeam ? (\n <span className=\"ml-2 text-xs font-normal text-muted-foreground\">{displayedTeam.title || displayedTeam.id}</span>\n ) : null}\n </div>\n <Select value={kindFilter} onValueChange={(value) => setKindFilter(value as KindFilter)}>\n <SelectTrigger size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {KIND_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!displayedTeam ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">No team selected</div>\n ) : entriesLoading && displayedEntries.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">Loading blackboard…</div>\n ) : displayedEntries.length === 0 ? (\n <div className=\"py-16 text-center text-sm text-muted-foreground\">No blackboard entries</div>\n ) : (\n <ScrollArea className=\"h-[640px] pr-3\">\n <div className=\"space-y-5\">\n {entryKinds.map((kind) => {\n const kindEntries = groupedEntries[kind]\n if (kindEntries.length === 0) return null\n return (\n <section key={kind} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{KIND_LABEL[kind]}</Badge>\n <span className=\"text-xs text-muted-foreground\">{kindEntries.length}</span>\n </div>\n <div className=\"space-y-2\">\n {kindEntries.map((entry) => (\n <div key={entry.id} className=\"space-y-3 rounded-md border border-border bg-background p-3\">\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">{entry.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n by {shortId(entry.authorId)} · {timeAgo(now, entry.updatedAt || entry.createdAt)}\n </div>\n </div>\n <div className=\"flex flex-wrap justify-end gap-1\">\n <Badge variant=\"outline\" className={ENTRY_STATUS_CLASS[entry.status]}>{entry.status}</Badge>\n {entry.supersedes ? <Badge variant=\"outline\">supersedes {shortId(entry.supersedes)}</Badge> : null}\n </div>\n </div>\n {entry.body ? <p className=\"whitespace-pre-wrap text-sm text-muted-foreground\">{entry.body}</p> : null}\n </div>\n ))}\n </div>\n </section>\n )\n })}\n </div>\n </ScrollArea>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|