@hotmeshio/long-tail 0.10.10 → 0.10.11
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/build/api/escalations/list.js +11 -4
- package/build/api/roles.d.ts +2 -0
- package/build/api/roles.js +4 -0
- package/build/lib/db/schemas/025_ops_home_default.sql +11 -0
- package/build/modules/features.d.ts +9 -0
- package/build/modules/features.js +1 -0
- package/build/services/role/index.js +1 -0
- package/build/services/role/sql.d.ts +6 -2
- package/build/services/role/sql.js +11 -2
- package/build/services/role/types.d.ts +9 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +3 -0
- package/build/system/mcp-servers/admin/schemas.js +1 -0
- package/build/system/mcp-servers/admin/users.js +1 -0
- package/build/system/seed/tool-manifests-admin.d.ts +82 -0
- package/build/system/seed/tool-manifests-admin.js +1 -1
- package/build/types/startup.d.ts +8 -0
- package/dashboard/dist/assets/{AdminDashboard-cGYuppUP.js → AdminDashboard-CkTbRfY9.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-cGYuppUP.js.map → AdminDashboard-CkTbRfY9.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-DwaUyoCy.js → AgentConfigPage-Cm8FRpXi.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-DwaUyoCy.js.map → AgentConfigPage-Cm8FRpXi.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-DxpFaltM.js → AgentDetailPage-sQ1-UT46.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-DxpFaltM.js.map → AgentDetailPage-sQ1-UT46.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-ByOoA8oT.js → AgentsPage-bSx_mdae.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-ByOoA8oT.js.map → AgentsPage-bSx_mdae.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-8pMC7Xv6.js.map +1 -0
- package/dashboard/dist/assets/{BotPicker-Dohv8dhR.js → BotPicker-C4uGH2x9.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-Dohv8dhR.js.map → BotPicker-C4uGH2x9.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DeijFG3J.js → CapabilitiesPage-BRoOeKcc.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DeijFG3J.js.map → CapabilitiesPage-BRoOeKcc.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-BPNwS4eO.js → CollapsibleSection-CIj8Adc9.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-BPNwS4eO.js.map → CollapsibleSection-CIj8Adc9.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js → ConfirmDeleteModal-Ugi_9Ukq.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-DD7SEMeX.js.map → ConfirmDeleteModal-Ugi_9Ukq.js.map} +1 -1
- package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js → CopyableId-GUEdiiL5.js} +2 -2
- package/dashboard/dist/assets/{CopyableId-BpB-NvDh.js.map → CopyableId-GUEdiiL5.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-D3t0XMhE.js +2 -0
- package/dashboard/dist/assets/{CountdownTimer-BF71ldGF.js.map → CountdownTimer-D3t0XMhE.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-oeNmMzTM.js → CredentialsPage-B73Si6K1.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-oeNmMzTM.js.map → CredentialsPage-B73Si6K1.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-CebR1m2k.js → CronLabel-Ca1aHolt.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-CebR1m2k.js.map → CronLabel-Ca1aHolt.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-Bh9QdBVw.js → CustomDurationPicker-_9E9x1YN.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-Bh9QdBVw.js.map → CustomDurationPicker-_9E9x1YN.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-Bavzt2QR.js → DropZone-NYn6TWHz.js} +2 -2
- package/dashboard/dist/assets/{DropZone-Bavzt2QR.js.map → DropZone-NYn6TWHz.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-Caa6CU_y.js → ElapsedCell-karxia93.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-Caa6CU_y.js.map → ElapsedCell-karxia93.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CAGfq0eK.js → EscalationListSchemaPage-CDaNmbUA.js} +2 -2
- package/dashboard/dist/assets/{EscalationListSchemaPage-CAGfq0eK.js.map → EscalationListSchemaPage-CDaNmbUA.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-qDD4ur6P.js → EscalationSchemaPage-k-2hFT2t.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-qDD4ur6P.js.map → EscalationSchemaPage-k-2hFT2t.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-CC1wRTzy.js → EscalationsOverview-C1e7IPn_.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-CC1wRTzy.js.map → EscalationsOverview-C1e7IPn_.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-4JeTcXr_.js → EventTable-CIrrtL7_.js} +2 -2
- package/dashboard/dist/assets/{EventTable-4JeTcXr_.js.map → EventTable-CIrrtL7_.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-BOV8DWIO.js → FilterBar-CzjtH74v.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-BOV8DWIO.js.map → FilterBar-CzjtH74v.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-C0i1speW.js → GraphInvokePage-LMpWw8z1.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-C0i1speW.js.map → GraphInvokePage-LMpWw8z1.js.map} +1 -1
- package/dashboard/dist/assets/HomePage-Kl2DBG6l.js +2 -0
- package/dashboard/dist/assets/HomePage-Kl2DBG6l.js.map +1 -0
- package/dashboard/dist/assets/ListToolbar-B2fub0DY.js +2 -0
- package/dashboard/dist/assets/ListToolbar-B2fub0DY.js.map +1 -0
- package/dashboard/dist/assets/{McpOverview-Chn2zIXr.js → McpOverview-odYnCZCC.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Chn2zIXr.js.map → McpOverview-odYnCZCC.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-Cv1FKrvw.js → McpQueryDetailPage-BtqwHUAg.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-Cv1FKrvw.js.map → McpQueryDetailPage-BtqwHUAg.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-Dzn8JE4E.js → McpQueryPage-CPkVwNIK.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-Dzn8JE4E.js.map → McpQueryPage-CPkVwNIK.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-CuNtR4Ed.js → McpRunDetailPage-D-Rz9MRw.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-CuNtR4Ed.js.map → McpRunDetailPage-D-Rz9MRw.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-C8iifeKN.js → McpRunsPage-rdpZ2yMh.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-C8iifeKN.js.map → McpRunsPage-rdpZ2yMh.js.map} +1 -1
- package/dashboard/dist/assets/{Modal-CA2Iv02H.js → Modal-Dr1Xy0j0.js} +2 -2
- package/dashboard/dist/assets/{Modal-CA2Iv02H.js.map → Modal-Dr1Xy0j0.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-ep9Zmokr.js → NamespacePill-GwO-WfhX.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-ep9Zmokr.js.map → NamespacePill-GwO-WfhX.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js +2 -0
- package/dashboard/dist/assets/OperationsPage-DlyGLPAs.js.map +1 -0
- package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js +2 -0
- package/dashboard/dist/assets/OperatorDashboard-Csei5vj-.js.map +1 -0
- package/dashboard/dist/assets/{PageHeader-Bw8RNkqR.js → PageHeader-Bo9BU9pK.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-Bw8RNkqR.js.map → PageHeader-Bo9BU9pK.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-NR_4idn6.js → PageHeaderWithStats-8_5cOSKL.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-NR_4idn6.js.map → PageHeaderWithStats-8_5cOSKL.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-DKILoUqL.js → PersonaDetailPage--ZL6Or6v.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-DKILoUqL.js.map → PersonaDetailPage--ZL6Or6v.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-qvzHTrFw.js → PersonasPage-Bdcoay3P.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-qvzHTrFw.js.map → PersonasPage-Bdcoay3P.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-C3LfeKvi.js → ProcessDetailPage-HrmVQq7S.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-C3LfeKvi.js.map → ProcessDetailPage-HrmVQq7S.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-OoZ_u4ty.js → ProcessesListPage-_xmj6Bo-.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-OoZ_u4ty.js.map → ProcessesListPage-_xmj6Bo-.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-C0ju2-4K.js.map +1 -0
- package/dashboard/dist/assets/{RolePill-TySZq7dB.js → RolePill-CQlnEWkK.js} +2 -2
- package/dashboard/dist/assets/{RolePill-TySZq7dB.js.map → RolePill-CQlnEWkK.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-CKluVSpB.js +2 -0
- package/dashboard/dist/assets/{RolesPage-CNueMVuJ.js.map → RolesPage-CKluVSpB.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-CwWEpiSF.js → RunAsSelector-DdfecQ1U.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-CwWEpiSF.js.map → RunAsSelector-DdfecQ1U.js.map} +1 -1
- package/dashboard/dist/assets/{ScopeBadge-C7Uixtmk.js → ScopeBadge-BBYkevuR.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-C7Uixtmk.js.map → ScopeBadge-BBYkevuR.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-DvIF3kQK.js → StickyPagination-cLddIdtT.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-DvIF3kQK.js.map → StickyPagination-cLddIdtT.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-DkFK_XGD.js → StreamMessageDetail-CZXWDawC.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-DkFK_XGD.js.map → StreamMessageDetail-CZXWDawC.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-CMw-Et6h.js → SwimlaneTimeline-fWY0gaf3.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-CMw-Et6h.js.map → SwimlaneTimeline-fWY0gaf3.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-D8gSp48D.js → TagInput-CDKbslLx.js} +2 -2
- package/dashboard/dist/assets/{TagInput-D8gSp48D.js.map → TagInput-CDKbslLx.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BhXuChOf.js → TaskDetailPage-BjE4G2yn.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BhXuChOf.js.map → TaskDetailPage-BjE4G2yn.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-CHqolBmU.js → TaskQueuePill-G3RuYRNZ.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-CHqolBmU.js.map → TaskQueuePill-G3RuYRNZ.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-6JHQQ5QZ.js → TasksListPage-KAhmCWB2.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-6JHQQ5QZ.js.map → TasksListPage-KAhmCWB2.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-CInx-vzY.js → TimeAgo-C_H2TyZw.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-CInx-vzY.js.map → TimeAgo-C_H2TyZw.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-Fq17Zrsu.js → TimestampCell-COG_4Flg.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-Fq17Zrsu.js.map → TimestampCell-COG_4Flg.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-BbgMDhaE.js → ToolPill-ChSyRJZC.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-BbgMDhaE.js.map → ToolPill-ChSyRJZC.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-DhjEe59s.js → ToolTestPanel-BPdJECW0.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-DhjEe59s.js.map → ToolTestPanel-BPdJECW0.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-DbIdmQpQ.js → TopicDetailPage-DAqx5JqA.js} +3 -3
- package/dashboard/dist/assets/{TopicDetailPage-DbIdmQpQ.js.map → TopicDetailPage-DAqx5JqA.js.map} +1 -1
- package/dashboard/dist/assets/TopicsPage-DoGqAdPm.js +2 -0
- package/dashboard/dist/assets/{TopicsPage-CAP-GUbr.js.map → TopicsPage-DoGqAdPm.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DggcLkSc.js → UserName-CKO_rk1R.js} +2 -2
- package/dashboard/dist/assets/{UserName-DggcLkSc.js.map → UserName-CKO_rk1R.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bmra5fVt.js → WorkflowExecutionPage-D3VQ9iQV.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-Bmra5fVt.js.map → WorkflowExecutionPage-D3VQ9iQV.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-CcUUVugA.js → WorkflowPill-CNbwug5p.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-CcUUVugA.js.map → WorkflowPill-CNbwug5p.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-BQsZAGYU.js → WorkflowsDashboard-DdJ86We6.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BQsZAGYU.js.map → WorkflowsDashboard-DdJ86We6.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CeliraHZ.js → WorkflowsOverview-ZfFBzePp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CeliraHZ.js.map → WorkflowsOverview-ZfFBzePp.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DGxlonrV.js → YamlWorkflowDetailPage-4bd5yXdA.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DGxlonrV.js.map → YamlWorkflowDetailPage-4bd5yXdA.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-DnT8UiyQ.js → YamlWorkflowsPage-iHPNXjQj.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-DnT8UiyQ.js.map → YamlWorkflowsPage-iHPNXjQj.js.map} +1 -1
- package/dashboard/dist/assets/{agents-mRV7m4sh.js → agents-DjD5U3Wb.js} +2 -2
- package/dashboard/dist/assets/{agents-mRV7m4sh.js.map → agents-DjD5U3Wb.js.map} +1 -1
- package/dashboard/dist/assets/{bots-SONCZlCM.js → bots-DiljVapH.js} +2 -2
- package/dashboard/dist/assets/{bots-SONCZlCM.js.map → bots-DiljVapH.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-DZjCMN32.js → capabilities-DV_5ghHT.js} +2 -2
- package/dashboard/dist/assets/{capabilities-DZjCMN32.js.map → capabilities-DV_5ghHT.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-RQNXjna2.js → controlplane-BKMvfWCB.js} +2 -2
- package/dashboard/dist/assets/{controlplane-RQNXjna2.js.map → controlplane-BKMvfWCB.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-BQpCaJId.js +2 -0
- package/dashboard/dist/assets/escalation-columns-BQpCaJId.js.map +1 -0
- package/dashboard/dist/assets/{index-iHV3d7o3.js → index-B-WkiNRN.js} +2 -2
- package/dashboard/dist/assets/{index-iHV3d7o3.js.map → index-B-WkiNRN.js.map} +1 -1
- package/dashboard/dist/assets/{index-DY_hlZCQ.js → index-BO-7S9Xw.js} +2 -2
- package/dashboard/dist/assets/{index-DY_hlZCQ.js.map → index-BO-7S9Xw.js.map} +1 -1
- package/dashboard/dist/assets/{index-CK-6m8e_.js → index-BaV3l0UE.js} +2 -2
- package/dashboard/dist/assets/{index-CK-6m8e_.js.map → index-BaV3l0UE.js.map} +1 -1
- package/dashboard/dist/assets/{index-CxVGlkK6.js → index-BsOxMMmb.js} +2 -2
- package/dashboard/dist/assets/{index-CxVGlkK6.js.map → index-BsOxMMmb.js.map} +1 -1
- package/dashboard/dist/assets/{index-CkeSxuZ0.js → index-CI8rrKjx.js} +2 -2
- package/dashboard/dist/assets/{index-CkeSxuZ0.js.map → index-CI8rrKjx.js.map} +1 -1
- package/dashboard/dist/assets/{index-DYKRtstT.js → index-CItsikDw.js} +2 -2
- package/dashboard/dist/assets/{index-DYKRtstT.js.map → index-CItsikDw.js.map} +1 -1
- package/dashboard/dist/assets/{index-C6GsrvX1.js → index-CStR4uIU.js} +2 -2
- package/dashboard/dist/assets/{index-C6GsrvX1.js.map → index-CStR4uIU.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dh3CVChT.js → index-DbTD-dio.js} +26 -26
- package/dashboard/dist/assets/index-DbTD-dio.js.map +1 -0
- package/dashboard/dist/assets/{index-DqsLfO-0.js → index-DhATO1cS.js} +2 -2
- package/dashboard/dist/assets/{index-DqsLfO-0.js.map → index-DhATO1cS.js.map} +1 -1
- package/dashboard/dist/assets/index-DnlsDaAw.css +1 -0
- package/dashboard/dist/assets/{index-ymm58cTW.js → index-Tx_ETzcH.js} +2 -2
- package/dashboard/dist/assets/{index-ymm58cTW.js.map → index-Tx_ETzcH.js.map} +1 -1
- package/dashboard/dist/assets/{index-D2CfSx_x.js → index-kzIIeVbx.js} +4 -4
- package/dashboard/dist/assets/{index-D2CfSx_x.js.map → index-kzIIeVbx.js.map} +1 -1
- package/dashboard/dist/assets/{index-DFJF8aWz.js → index-nIrYejxe.js} +2 -2
- package/dashboard/dist/assets/{index-DFJF8aWz.js.map → index-nIrYejxe.js.map} +1 -1
- package/dashboard/dist/assets/{index-DbZpL10z.js → index-xZqQxqu2.js} +2 -2
- package/dashboard/dist/assets/{index-DbZpL10z.js.map → index-xZqQxqu2.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-Bawo8u7l.js → knowledge-BocTnSiA.js} +2 -2
- package/dashboard/dist/assets/{knowledge-Bawo8u7l.js.map → knowledge-BocTnSiA.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CKIpbpRZ.js → mcp-C1KBjKQ_.js} +2 -2
- package/dashboard/dist/assets/{mcp-CKIpbpRZ.js.map → mcp-C1KBjKQ_.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-vld4hYxQ.js → mcp-query-B3cjukkA.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-vld4hYxQ.js.map → mcp-query-B3cjukkA.js.map} +1 -1
- package/dashboard/dist/assets/{personas-BsARc3df.js → personas-DiA2bJaX.js} +2 -2
- package/dashboard/dist/assets/{personas-BsARc3df.js.map → personas-DiA2bJaX.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BTSL-5ko.js → pipelines-Dt2d2EFR.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BTSL-5ko.js.map → pipelines-Dt2d2EFR.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-DggQdNbj.js → tasks-cDh2hNml.js} +2 -2
- package/dashboard/dist/assets/{tasks-DggQdNbj.js.map → tasks-cDh2hNml.js.map} +1 -1
- package/dashboard/dist/assets/{topics-DzAKa34X.js → topics-yFlGF-u1.js} +2 -2
- package/dashboard/dist/assets/{topics-DzAKa34X.js.map → topics-yFlGF-u1.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-5uI9oI5W.js → useEventHooks-D2R_ckS8.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-5uI9oI5W.js.map → useEventHooks-D2R_ckS8.js.map} +1 -1
- package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js → useFilterParams-B7suPsIK.js} +2 -2
- package/dashboard/dist/assets/{useFilterParams-CvRyYHJN.js.map → useFilterParams-B7suPsIK.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CEKzFIqe.js → useNamespace-u-V1GVUD.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CEKzFIqe.js.map → useNamespace-u-V1GVUD.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-hHgX1Z50.js → useYamlActivityEvents-BhOZeqgE.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-hHgX1Z50.js.map → useYamlActivityEvents-BhOZeqgE.js.map} +1 -1
- package/dashboard/dist/assets/{users-AO8lUyNr.js → users-BoXPwdee.js} +2 -2
- package/dashboard/dist/assets/{users-AO8lUyNr.js.map → users-BoXPwdee.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-DN9yGSuM.js → vendor-icons-D71XjkNy.js} +131 -136
- package/dashboard/dist/assets/vendor-icons-D71XjkNy.js.map +1 -0
- package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js → vendor-react-C1YOyCSV.js} +2 -2
- package/dashboard/dist/assets/{vendor-react-BpdAFEQc.js.map → vendor-react-C1YOyCSV.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-PtnlRK09.js → workflows-mCoyKuLZ.js} +2 -2
- package/dashboard/dist/assets/{workflows-PtnlRK09.js.map → workflows-mCoyKuLZ.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DPu3Y8vG.js → yaml-workflows-CWxXGDpn.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DPu3Y8vG.js.map → yaml-workflows-CWxXGDpn.js.map} +1 -1
- package/dashboard/dist/index.html +4 -4
- package/docs/dashboard.md +9 -2
- package/docs/design-principles.md +4 -0
- package/docs/hitl/pinned-views.md +3 -3
- package/docs/operations.md +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-C1vtTvqP.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-C1vtTvqP.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-BF71ldGF.js +0 -2
- package/dashboard/dist/assets/HomePage-HPCd8SjF.js +0 -2
- package/dashboard/dist/assets/HomePage-HPCd8SjF.js.map +0 -1
- package/dashboard/dist/assets/ListToolbar-BFHVOba6.js +0 -2
- package/dashboard/dist/assets/ListToolbar-BFHVOba6.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-flrZ-EJ9.js +0 -2
- package/dashboard/dist/assets/OperationsPage-flrZ-EJ9.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-Dhs2eo8p.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-Dhs2eo8p.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-D70jTLZm.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-CNueMVuJ.js +0 -2
- package/dashboard/dist/assets/TopicsPage-CAP-GUbr.js +0 -2
- package/dashboard/dist/assets/escalation-columns-XLEcZRCg.js +0 -2
- package/dashboard/dist/assets/escalation-columns-XLEcZRCg.js.map +0 -1
- package/dashboard/dist/assets/index-2BwHxjCw.css +0 -1
- package/dashboard/dist/assets/index-Dh3CVChT.js.map +0 -1
- package/dashboard/dist/assets/role-display-Dvo2lDpU.js +0 -2
- package/dashboard/dist/assets/role-display-Dvo2lDpU.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-DN9yGSuM.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ListToolbar-BFHVOba6.js","sources":["../../src/components/common/data/ListToolbar.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { RefreshCw, Link2, Terminal, Check } from 'lucide-react';\nimport { getToken } from '../../../api/client';\nimport { LT_BASE } from '../../../lib/base-path';\n\ninterface ListToolbarProps {\n onRefresh: () => void;\n isFetching?: boolean;\n /** API path (e.g. \"/workflow-states/jobs?limit=50\") for URL/curl copy. */\n apiPath?: string;\n /**\n * Set when the toolbar renders outside a `@container/filters` ancestor\n * (e.g. a page header) — skips the narrow-container fold classes, which\n * would otherwise never match and hide the copy actions.\n */\n standalone?: boolean;\n}\n\ntype CopiedState = 'url' | 'curl' | null;\n\n/**\n * Toolbar for list pages: refresh, copy API URL, copy curl command.\n * Designed to sit in FilterBar `actions` slot.\n */\nexport function ListToolbar({ onRefresh, isFetching = false, apiPath, standalone = false }: ListToolbarProps) {\n const [copied, setCopied] = useState<CopiedState>(null);\n\n const copyToClipboard = (text: string, type: CopiedState) => {\n navigator.clipboard.writeText(text).then(() => {\n setCopied(type);\n setTimeout(() => setCopied(null), 2000);\n });\n };\n\n const handleCopyUrl = () => {\n if (!apiPath) return;\n const url = `${window.location.origin}${LT_BASE}/api${apiPath}`;\n copyToClipboard(url, 'url');\n };\n\n const handleCopyCurl = () => {\n if (!apiPath) return;\n const url = `${window.location.origin}${LT_BASE}/api${apiPath}`;\n const token = getToken();\n const cmd = token\n ? `curl -H \"Authorization: Bearer ${token}\" \"${url}\"`\n : `curl \"${url}\"`;\n copyToClipboard(cmd, 'curl');\n };\n\n const BTN = 'p-1 text-text-quaternary hover:text-accent transition-colors rounded';\n const ICON = 'w-2.5 h-2.5';\n\n return (\n <div className=\"flex items-center gap-0.5\">\n {apiPath && (\n // Developer copy actions yield first at narrow containers — refresh\n // is the affordance the floor actually uses.\n <div className={standalone ? 'flex items-center gap-0.5' : 'hidden @filters/filters:flex items-center gap-0.5'}>\n <button onClick={handleCopyUrl} className={BTN} title=\"Copy API URL\">\n {copied === 'url' ? <Check className={`${ICON} text-status-success`} /> : <Link2 className={ICON} />}\n </button>\n <button onClick={handleCopyCurl} className={BTN} title=\"Copy curl (includes auth token)\">\n {copied === 'curl' ? <Check className={`${ICON} text-status-success`} /> : <Terminal className={ICON} />}\n </button>\n </div>\n )}\n <button onClick={onRefresh} disabled={isFetching} className={`${BTN} disabled:opacity-50`} title=\"Refresh\">\n <RefreshCw className={`${ICON} ${isFetching ? 'animate-spin' : ''}`} />\n </button>\n </div>\n );\n}\n"],"names":["ListToolbar","onRefresh","isFetching","apiPath","standalone","copied","setCopied","useState","copyToClipboard","text","type","handleCopyUrl","url","LT_BASE","handleCopyCurl","token","getToken","cmd","BTN","ICON","jsxs","jsx","Check","Link2","Terminal","RefreshCw"],"mappings":"+KAwBO,SAASA,EAAY,CAAE,UAAAC,EAAW,WAAAC,EAAa,GAAO,QAAAC,EAAS,WAAAC,EAAa,IAA2B,CAC5G,KAAM,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAsB,IAAI,EAEhDC,EAAkB,CAACC,EAAcC,IAAsB,CAC3D,UAAU,UAAU,UAAUD,CAAI,EAAE,KAAK,IAAM,CAC7CH,EAAUI,CAAI,EACd,WAAW,IAAMJ,EAAU,IAAI,EAAG,GAAI,CACxC,CAAC,CACH,EAEMK,EAAgB,IAAM,CAC1B,GAAI,CAACR,EAAS,OACd,MAAMS,EAAM,GAAG,OAAO,SAAS,MAAM,GAAGC,CAAO,OAAOV,CAAO,GAC7DK,EAAgBI,EAAK,KAAK,CAC5B,EAEME,EAAiB,IAAM,CAC3B,GAAI,CAACX,EAAS,OACd,MAAMS,EAAM,GAAG,OAAO,SAAS,MAAM,GAAGC,CAAO,OAAOV,CAAO,GACvDY,EAAQC,EAAA,EACRC,EAAMF,EACR,kCAAkCA,CAAK,MAAMH,CAAG,IAChD,SAASA,CAAG,IAChBJ,EAAgBS,EAAK,MAAM,CAC7B,EAEMC,EAAM,uEACNC,EAAO,cAEb,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,4BACZ,SAAA,CAAAjB,GAGCiB,EAAAA,KAAC,MAAA,CAAI,UAAWhB,EAAa,4BAA8B,oDACzD,SAAA,CAAAiB,EAAAA,IAAC,SAAA,CAAO,QAASV,EAAe,UAAWO,EAAK,MAAM,eACnD,aAAW,MAAQG,EAAAA,IAACC,GAAM,UAAW,GAAGH,CAAI,sBAAA,CAAwB,QAAMI,EAAA,CAAM,UAAWJ,EAAM,CAAA,CACpG,EACAE,EAAAA,IAAC,UAAO,QAASP,EAAgB,UAAWI,EAAK,MAAM,kCACpD,SAAAb,IAAW,aAAUiB,EAAA,CAAM,UAAW,GAAGH,CAAI,sBAAA,CAAwB,EAAKE,EAAAA,IAACG,EAAA,CAAS,UAAWL,CAAA,CAAM,CAAA,CACxG,CAAA,EACF,EAEFE,EAAAA,IAAC,UAAO,QAASpB,EAAW,SAAUC,EAAY,UAAW,GAAGgB,CAAG,uBAAwB,MAAM,UAC/F,SAAAG,MAACI,EAAA,CAAU,UAAW,GAAGN,CAAI,IAAIjB,EAAa,eAAiB,EAAE,EAAA,CAAI,CAAA,CACvE,CAAA,EACF,CAEJ"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as j,j as e}from"./vendor-query-B2UbickB.js";import{ai as Re,G as Ie,a4 as De}from"./index-Dh3CVChT.js";import{i as Be}from"./useEventHooks-5uI9oI5W.js";import{P as ze}from"./PageHeader-Bw8RNkqR.js";import{a as re}from"./role-display-Dvo2lDpU.js";import{X as He,O as Ue,aP as Ye,ar as Ve,aN as Ge,r as Ke,aQ as Qe,E as Xe,ai as Le}from"./vendor-icons-DN9yGSuM.js";import{L as O,f as Ze,a as Je}from"./vendor-react-BpdAFEQc.js";const Me=800,Pe=270,F=28,et=60,me=24,tt=72,he=Me-F-et,fe=Pe-me-tt,L=me+fe,pe=F+he,v="0.5s cubic-bezier(0.4, 0, 0.2, 1)",Ne=9;function st(t){return`M ${(t.x-Ne).toFixed(1)} ${t.y.toFixed(1)} L ${(t.x+Ne).toFixed(1)} ${t.y.toFixed(1)}`}function H(t){if(t.length===0)return"";if(t.length===1)return st(t[0]);const a=[];for(let r=0;r<t.length-1;r++){const d=t[Math.max(0,r-1)],c=t[r],h=t[r+1],n=t[Math.min(t.length-1,r+2)],x=(c.x+(h.x-d.x)/6).toFixed(1),u=(c.y+(h.y-d.y)/6).toFixed(1),o=(h.x-(n.x-c.x)/6).toFixed(1),i=(h.y-(n.y-c.y)/6).toFixed(1);r===0&&a.push(`M ${c.x.toFixed(1)} ${c.y.toFixed(1)}`),a.push(`C ${x} ${u}, ${o} ${i}, ${h.x.toFixed(1)} ${h.y.toFixed(1)}`)}return a.join(" ")}function nt(t,a){if(t.length<2||a.length<2)return"";const r=H(t),d=[...a].reverse(),c=H(d),h=c.slice(c.indexOf("C"));return`${r} L ${d[0].x.toFixed(1)} ${d[0].y.toFixed(1)} ${h} Z`}function ke(t){return t>=1e4?`${Math.round(t/1e3)}k`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)}function xe(t){return t.length>0?t[t.length-1]:void 0}function at(t){return[1,2,5,10,20,50,100,200,500,1e3,2e3,5e3,1e4,2e4,5e4].filter(r=>r>0&&r<t*.98)}function rt(t){return[.25,.5,.75].map(r=>Math.round(r*t)).filter(r=>r>0)}const W="rgb(var(--lt-status-claimed-graphic))",U="rgb(var(--lt-status-queued-graphic))",E="rgb(var(--lt-status-success-graphic))",T="rgb(var(--lt-text-quaternary))",Y="rgb(var(--lt-status-error))";function V(t,a){return t.replace(/\)$/,` / ${a})`)}const ot=13;function lt(t,a){const r=[...t].sort((n,x)=>n.y-x.y);let d=-1/0;const c=r.map(n=>{const x=Math.max(n.y+3,d+ot);return d=x,{...n,labelY:x}}),h=c.length>0?c[c.length-1].labelY-a:0;if(h>0)for(const n of c)n.labelY-=h;return c}function it({stations:t,selectedRole:a,onSelect:r,onUpstreamSelect:d,onCmdClick:c,periodHours:h,logScale:n=!1,fill:x=!1}){const[u,o]=j.useState(null),i=t.length;if(i===0)return null;const p=s=>i===1?F+he/2:F+s/(i-1)*he,q=new Map(t.map((s,m)=>[s.role,p(m)])),N=t.map((s,m)=>{var C,te,se,ne;const y=s.target_per_hour??null,A=y!=null?y*h:null;return{idx:m,x:p(m),expected:A,actual:((C=s.metric)==null?void 0:C.resolved)??0,active:((te=s.metric)==null?void 0:te.claimed)??0,pending:((se=s.metric)==null?void 0:se.pending)??0,priorityCount:((ne=s.metric)==null?void 0:ne.priority_count)??0}}),k=N.some(s=>s.expected!=null),S=Math.max(1,...N.map(s=>s.expected??0),...N.map(s=>s.actual),...N.map(s=>s.expected!=null?s.pending:0))*1.15,f=n?s=>{const m=Math.log(1+S);return me+fe*(1-Math.log(1+Math.max(0,s))/m)}:s=>me+fe*(1-Math.max(0,Math.min(s,S))/S),g=n?at(S):rt(S),M=N.filter(s=>s.expected!=null),G=M.map(s=>({...s,y:f(s.expected)})),R=M.map(s=>({...s,y:f(s.actual)})),P=M.map(s=>({...s,y:f(s.active)})),_=M.map(s=>({...s,y:f(s.pending)})),K=H(G),Q=H(R),X=H(P),oe=H(_),D=xe(R),le=xe(G),B=xe(P),ie=xe(_),Z=R.length>=2&&D?`${Q} L ${D.x.toFixed(1)} ${L} L ${R[0].x.toFixed(1)} ${L} Z`:"",J=M.some(s=>s.pending>0||s.active>0),ee=J&&P.length>=2&&B?`${X} L ${B.x.toFixed(1)} ${L} L ${P[0].x.toFixed(1)} ${L} Z`:"",l=J?nt(_,P):"",b=M.some(s=>s.active>0),w=M.some(s=>s.pending>s.active),z=lt([le?{key:"target",text:"Target",color:T,y:le.y}:null,D?{key:"actual",text:"Resolved",color:E,y:D.y}:null,b&&B?{key:"active",text:"Claimed",color:W,y:B.y}:null,w&&ie?{key:"queued",text:"Pending",color:U,y:ie.y}:null].filter(Boolean),L+4);return e.jsxs("svg",{viewBox:`0 0 ${Me} ${Pe}`,className:x?"w-full h-full":"w-full",preserveAspectRatio:"xMidYMid meet",style:{display:"block",...x?{}:{height:"auto"}},children:[g.map(s=>{const m=f(s);return e.jsxs("g",{children:[e.jsx("line",{x1:F,y1:m,x2:pe,y2:m,className:"stroke-surface-border",strokeWidth:.5,opacity:n?.45:.3,strokeDasharray:n?"2 5":void 0,style:{transition:`y1 ${v}, y2 ${v}`}}),e.jsx("text",{x:F-4,y:m+2.5,textAnchor:"end",fontSize:6,fontFamily:"ui-monospace, monospace",opacity:.35,className:"fill-text-quaternary",children:ke(s)})]},`grid-${s}`)}),t.map(s=>{if(!s.parent_role)return null;const m=q.get(s.parent_role);if(m==null)return null;const y=p(t.findIndex(A=>A.role===s.role));return e.jsx("line",{x1:m,y1:L,x2:y,y2:L,className:"stroke-surface-border",strokeWidth:.75},`dep-${s.role}`)}),Z&&e.jsx("path",{d:Z,opacity:.05,style:{fill:E,transition:`d ${v}`}}),ee&&e.jsx("path",{d:ee,opacity:.12,style:{fill:W,transition:`d ${v}`}}),l&&e.jsx("path",{d:l,opacity:.12,style:{fill:U,transition:`d ${v}`}}),K&&e.jsx("path",{d:K,fill:"none",strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.7,style:{stroke:T,transition:`d ${v}`}}),G.map(s=>e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${v}`},children:[e.jsx("circle",{r:2,opacity:.8,style:{fill:T}}),e.jsx("text",{y:-5,textAnchor:"middle",fontSize:6.5,fontFamily:"ui-monospace, monospace",fontWeight:"500",opacity:.7,style:{fill:T},children:Math.round(s.expected)})]},`tval-${s.idx}`)),J&&oe&&e.jsx("path",{d:oe,fill:"none",strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.55,style:{stroke:U,transition:`d ${v}`}}),X&&e.jsx("path",{d:X,fill:"none",strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.5,style:{stroke:W,transition:`d ${v}`}}),Q&&e.jsx("path",{d:Q,fill:"none",strokeWidth:.6,strokeLinejoin:"round",strokeLinecap:"round",opacity:.65,style:{stroke:E,transition:`d ${v}`}}),P.map((s,m)=>{if(s.pending<=0&&s.active<=0)return null;const y=Math.max(2.5,Math.min(5,2.5+s.active/10)),A=s.pending>s.active,C=Math.min(_[m].y-4,s.y-y-4);return e.jsxs("g",{children:[s.pending>0&&e.jsx("text",{x:s.x,y:C,textAnchor:"middle",fontSize:7.5,fontFamily:"ui-monospace, monospace",fontWeight:"600",style:{fill:A?U:"rgb(var(--lt-text-tertiary))",transition:`y ${v}`},children:s.pending}),e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${v}`},children:[e.jsx("circle",{r:y,opacity:.85,style:{fill:W}}),s.active>0&&e.jsx("text",{y:y+9,textAnchor:"middle",fontSize:7.5,fontFamily:"ui-monospace, monospace",fontWeight:"600",style:{fill:W},children:s.active})]})]},`active-${s.idx}`)}),N.map(s=>{var ve;const m=t[s.idx],y=s.x,A=s.expected!=null?f(s.actual):L,C=Math.max(2.25,Math.min(4,2.25+s.actual/160)),te=m.role===a,se=u===s.idx,ne=s.expected==null?"idle · set a target rate to plot pace":`${s.actual} resolved · target ${Math.round(s.expected)}`+(s.pending>s.active?` · ${s.pending-s.active} pending`:"")+(s.active>0?` · ${s.active} claimed`:"")+(s.priorityCount>0?` · ${s.priorityCount} in jeopardy`:""),ce=ne.length*5.5+20,je=Math.max(F+ce/2+4,Math.min(pe-ce/2-4,y)),be=A-C-10;return e.jsxs("g",{onClick:ae=>{ae.metaKey||ae.ctrlKey?(ae.stopPropagation(),c==null||c(m.role)):r(m.role)},onMouseEnter:()=>o(s.idx),onMouseLeave:()=>o(null),style:{cursor:"pointer"},children:[e.jsxs("g",{transform:`translate(${y} ${A})`,style:{transition:`transform ${v}`},children:[e.jsx("circle",{r:22,fill:"transparent"}),e.jsx("circle",{r:C,opacity:.9,style:{fill:E,transition:`r ${v}`}}),s.expected!=null&&e.jsx("text",{y:-C-5,textAnchor:"middle",fontSize:9.5,fontFamily:"ui-monospace, monospace",fontWeight:"500",style:{fill:E},children:ke(s.actual)}),se&&!te&&e.jsx("circle",{r:C+3,fill:"none",strokeWidth:1,opacity:.4,style:{stroke:E}}),te&&e.jsx("circle",{r:C+4,fill:"none",className:"stroke-accent",strokeWidth:2})]}),(((ve=m.upstream_roles)==null?void 0:ve.length)??0)>0&&e.jsxs("g",{transform:`translate(${y-20} ${L})`,onClick:ae=>{ae.stopPropagation(),d==null||d(m.upstream_roles[0])},style:{cursor:"pointer"},children:[e.jsx("title",{children:`Fed by ${m.upstream_roles.join(", ")} — click to view that sequence`}),e.jsx("circle",{r:9,cy:-6,fill:"transparent"}),e.jsx("path",{d:"M -7 -14 C -3 -14 -1 -9 0 -2",fill:"none",className:"stroke-text-quaternary",strokeWidth:1,strokeDasharray:"2 2",strokeLinecap:"round"}),e.jsx("path",{d:"M 0 -2 L 8 -2",className:"stroke-text-quaternary",strokeWidth:1,strokeLinecap:"round"}),e.jsx("path",{d:"M 8 -2 l -3.5 -2 v 4 z",className:"fill-text-quaternary"})]}),e.jsx("text",{x:y,y:L+(s.idx%2===0?14:27),textAnchor:"middle",fontSize:9.5,className:"fill-text-tertiary",fontFamily:"ui-sans-serif, sans-serif",fontWeight:"500",children:re(m)}),se&&e.jsxs("g",{children:[e.jsx("rect",{x:je-ce/2,y:be-16,width:ce,height:19,rx:3,className:"fill-text-primary",opacity:.88}),e.jsx("text",{x:je,y:be-3,textAnchor:"middle",fontSize:8.5,className:"fill-surface",fontFamily:"ui-monospace, monospace",children:ne})]})]},m.role)}),!k&&e.jsx("text",{x:F+he/2,y:L+48,textAnchor:"middle",fontSize:8.5,className:"fill-text-quaternary",fontFamily:"ui-sans-serif, sans-serif",children:"Set target_per_hour on each role in Admin → Roles to enable the pace chart"}),z.map(s=>e.jsx("text",{x:pe+6,y:s.labelY,fontSize:8,fontFamily:"ui-sans-serif, sans-serif",fontWeight:s.key==="actual"?700:500,opacity:s.key==="active"?.85:1,style:{fill:s.color,transition:`y ${v}`},children:s.text},s.key))]})}function Ce(t){const a=t.priority_facet?`metadata.${t.priority_facet}`:"created_at",r=JSON.stringify([{field:a,direction:"asc"}]);return`/escalations/available?role=${encodeURIComponent(t.role)}&jeopardy=1&view=table&orderBy=${encodeURIComponent(r)}`}const qe=["15m","1h","24h","7d","30d"],ct=t=>qe.includes(t);function de(t){if(t==null)return"—";if(t<1){const a=Math.round(t*60);return a<=0?"< 1s":`${a}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function ue({label:t,value:a}){return e.jsxs("div",{className:"flex items-center justify-between py-1.5",children:[e.jsx("span",{className:"text-2xs text-text-tertiary uppercase tracking-wider",children:t}),e.jsx("span",{className:"text-xs font-mono font-medium text-text-primary tabular-nums",children:a})]})}function xt({period:t,onChange:a}){return e.jsx("div",{className:"flex items-center gap-1 mb-4",children:qe.map(r=>e.jsx("button",{onClick:()=>a(r),className:`px-2 py-0.5 text-2xs font-mono rounded transition-colors ${t===r?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:r},r))})}function dt({role:t,globalPeriod:a,onClose:r}){const[d,c]=j.useState(ct(a)?a:"24h"),{data:h}=Re(d),n=h==null?void 0:h.stations.find(i=>i.role===t.role),x=t.sla_minutes??void 0,u=t.target_per_hour??void 0,o=t.worker_count??void 0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-start justify-between mb-4",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-baseline gap-1.5 flex-wrap",children:[e.jsx("span",{className:"text-sm font-bold text-text-primary",children:re(t)}),e.jsx("span",{className:"text-2xs font-mono text-text-secondary",children:t.role})]}),t.description&&e.jsx("p",{className:"text-2xs text-text-tertiary mt-1 line-clamp-2 leading-relaxed",children:t.description})]}),e.jsx("button",{onClick:r,className:"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2",children:e.jsx(He,{className:"w-4 h-4"})})]}),e.jsxs("div",{className:"flex items-center gap-3 mb-6",children:[e.jsxs(O,{to:`/admin/roles/${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-2xs text-accent hover:underline",children:[e.jsx(Ue,{className:"w-2.5 h-2.5"}),"Edit in Roles"]}),e.jsxs(O,{to:`/escalations/available?role=${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-2xs text-accent hover:underline",children:[e.jsx(Ye,{className:"w-2.5 h-2.5"}),"View full queue"]})]}),e.jsx(xt,{period:d,onChange:c}),(u||x||o)&&e.jsxs("div",{className:"flex items-center gap-6 mb-6 text-2xs border-b border-surface-border/30 pb-5",children:[u&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary text-sm",children:[u,"/h"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5",children:"target"})]}),x&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary text-sm",children:[x,"m"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5",children:"SLA"})]}),o&&e.jsxs("div",{children:[e.jsx("div",{className:"font-mono font-semibold text-text-primary text-sm",children:o}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5",children:"workers"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-4 mb-2",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-2xs text-text-quaternary uppercase tracking-wider mb-2",children:"Wait (queue)"}),e.jsx(ue,{label:"P99",value:de((n==null?void 0:n.wait.p99)??null)}),e.jsx(ue,{label:"avg",value:de((n==null?void 0:n.wait.avg)??null)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-2xs text-text-quaternary uppercase tracking-wider mb-2",children:"Work (proc.)"}),e.jsx(ue,{label:"P99",value:de((n==null?void 0:n.work.p99)??null)}),e.jsx(ue,{label:"avg",value:de((n==null?void 0:n.work.avg)??null)})]})]}),e.jsxs("div",{className:"border-t border-surface-border/40 pt-4 mt-5 space-y-2.5",children:[x&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-2xs text-text-tertiary",children:"SLA target"}),e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:[x,"m"]})]}),n&&n.priority_count>0&&e.jsxs(O,{to:Ce(t),className:"flex items-center justify-between group",children:[e.jsxs("span",{className:"text-2xs",style:{color:Y},children:[n.priority_count," in jeopardy — pull oldest first"]}),e.jsx(Ve,{className:"w-3 h-3 transition-colors",style:{color:Y}})]})]})]})}function ut({allMetrics:t,orderedRoles:a,period:r}){const d=t.reduce((o,i)=>o+i.pending,0),c=t.reduce((o,i)=>o+i.resolved,0),h=t.reduce((o,i)=>o+i.priority_count,0),n=new Map(t.map(o=>[o.role,o])),x=t.filter(o=>o.priority_count>0).length,u=t.filter(o=>o.pending>0).length;return e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"text-2xs text-text-quaternary uppercase tracking-wider mb-3",children:["Pipeline · ",r]}),e.jsx("div",{className:"mb-4",children:x>0?e.jsxs("p",{className:"text-2xs",style:{color:Y},children:[x," station",x>1?"s":""," with items in jeopardy"]}):u>0?e.jsxs("p",{className:"text-2xs text-status-warning",children:[u," station",u>1?"s":""," with backlog"]}):c>0?e.jsx("p",{className:"text-2xs text-status-success",children:"Flowing — queue clear"}):e.jsx("p",{className:"text-2xs text-text-quaternary",children:"Quiet — awaiting work"})}),e.jsxs("div",{className:"space-y-1.5 mb-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider",children:"Pending"}),e.jsx("span",{className:`text-xs font-mono tabular-nums ${d>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:d})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider",children:["Resolved · ",r]}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:c})]}),h>0&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider",children:"Priority"}),e.jsx("span",{className:"text-xs font-mono tabular-nums font-semibold",style:{color:Y},children:h})]})]}),e.jsx("div",{className:"border-t border-surface-border/40 pt-2 mb-1",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider flex-1",children:"Station"}),e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"pend"}),e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"clmd"}),e.jsx("span",{className:"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"res"})]})}),e.jsx("div",{className:"space-y-0.5",children:a.map(o=>{const i=n.get(o.role),p=(i==null?void 0:i.pending)??0,q=(i==null?void 0:i.claimed)??0,N=(i==null?void 0:i.resolved)??0,k=(i==null?void 0:i.priority_count)??0,$=o.title||o.role,S=k>0,f=p>0;return e.jsxs("div",{className:"flex items-center gap-2 py-0.5",children:[e.jsxs("span",{className:`text-2xs font-mono flex-1 truncate ${f?"text-text-primary":"text-text-quaternary"}`,children:[$,S&&e.jsxs("span",{className:"ml-1 text-2xs font-semibold",style:{color:Y},children:[k," PRIORITY"]})]}),e.jsx("span",{className:`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${f?"text-text-primary font-semibold":"text-text-quaternary"}`,children:p>0?p:"—"}),e.jsx("span",{className:`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${q>0?"text-accent font-semibold":"text-text-quaternary"}`,children:q>0?q:"—"}),e.jsx("span",{className:`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${N>0?"text-text-secondary":"text-text-quaternary"}`,children:N>0?N:"—"})]},o.role)})}),e.jsx("p",{className:"text-2xs text-text-quaternary mt-5",children:"Select a station for queue detail."})]})}function ht({role:t,allMetrics:a,orderedRoles:r,globalPeriod:d,onClose:c}){return e.jsx("div",{className:"w-[280px] shrink-0 px-7 py-10 flex flex-col overflow-y-auto min-h-0",children:t?e.jsx(dt,{role:t,globalPeriod:d,onClose:c}):e.jsx(ut,{allMetrics:a,orderedRoles:r,period:d})})}const mt=T,ge="rgb(var(--lt-accent))",Ee=V(T,.09),Oe=V(T,.09),Ae=V(ge,.07),Fe=V(U,.08),We=V(W,.08),Te=V(E,.08),pt=["15m","1h","24h","7d","30d"],we={"15m":.25,"1h":1,"24h":24,"7d":168,"30d":720};function ye(t){const a=t.filter(n=>n.ops_visible),r=new Map(a.map(n=>[n.role,n])),d=new Set,h=a.filter(n=>!n.parent_role||!r.has(n.parent_role)).map(n=>{const x=[],u=[{role:n,depth:0}];for(;u.length>0;){const o=u.shift();d.has(o.role.role)||(d.add(o.role.role),x.push(o),a.filter(i=>i.parent_role===o.role.role&&!d.has(i.role)).forEach(i=>u.push({role:i,depth:o.depth+1})))}return{origin:n,stations:x}});return a.filter(n=>!d.has(n.role)).forEach(n=>h.push({origin:n,stations:[{role:n,depth:0}]})),h.sort((n,x)=>x.stations.length-n.stations.length||n.origin.role.localeCompare(x.origin.role))}function $e(t){if(t==null)return"—";if(t<1){const a=Math.round(t*60);return a<=0?"< 1s":`${a}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function ft(t,a){if(!a||t===0)return{pct:null,color:"bg-surface-border"};const r=t/a,d=Math.round(r*100),c=r>1?"bg-status-warning":r<.2?"bg-status-draft":"bg-status-success";return{pct:d,color:c}}function _e({value:t,onSave:a}){const[r,d]=j.useState(t!=null?String(t):""),c=j.useRef(void 0),h=n=>{const x=parseInt(n,10);a(!n.trim()||isNaN(x)||x<0?null:x)};return e.jsx("input",{type:"number",min:0,step:1,value:r,onChange:n=>{const x=n.target.value;d(x),clearTimeout(c.current),c.current=setTimeout(()=>h(x),400)},onBlur:n=>{clearTimeout(c.current),h(n.target.value)},onClick:n=>n.stopPropagation(),onKeyDown:n=>n.stopPropagation(),className:"w-full bg-transparent text-xs font-mono tabular-nums text-right focus:outline-none"})}function yt({role:t,metric:a,selected:r,periodHours:d,onClick:c}){var f;const h=De(),n=(a==null?void 0:a.pending)??0,x=(a==null?void 0:a.claimed)??0,u=(a==null?void 0:a.resolved)??0,o=(a==null?void 0:a.priority_count)??0,i=t.target_per_hour??null,{pct:p,color:q}=ft(n,i),N=p!=null?Math.min(100,p):0,k=bt(t.target_per_hour??null,t.sla_minutes??null,n,d),$=g=>h.mutate({role:t.role,target_per_hour:g}),S=g=>h.mutate({role:t.role,sla_minutes:g});return e.jsxs("tr",{tabIndex:0,onClick:c,onKeyDown:g=>{g.key==="Enter"&&c()},className:`cursor-pointer border-b border-surface-border/30 last:border-b-0 transition-colors ${r?"border-l-2 !border-l-accent":""}`,children:[e.jsx("td",{className:"pl-3 pr-2 py-1.5 overflow-hidden",children:e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"font-bold text-text-primary truncate text-xs",children:re(t)}),(((f=t.upstream_roles)==null?void 0:f.length)??0)>0&&e.jsx("span",{title:`Fed by ${t.upstream_roles.join(", ")}`,className:"shrink-0 leading-none",children:e.jsx(Qe,{className:"w-3 h-3 text-text-quaternary"})})]})}),e.jsx("td",{className:"hidden xl:table-cell pr-2 py-1.5 overflow-hidden",children:e.jsx("span",{className:"block font-mono text-2xs text-text-tertiary truncate",children:t.role})}),e.jsx("td",{className:"px-2 py-1.5",style:{backgroundColor:Ee},children:e.jsx(_e,{value:t.target_per_hour??null,onSave:$})}),e.jsx("td",{className:"px-2 py-1.5",style:{backgroundColor:Oe},children:e.jsx(_e,{value:t.sla_minutes??null,onSave:S})}),e.jsx("td",{className:"px-2 py-1.5 text-right",style:{backgroundColor:Ae},title:k!=null?`~${k} concurrent worker${k===1?"":"s"} to sustain ${t.target_per_hour}/h within ${t.sla_minutes}m SLA`:void 0,children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${k!=null?"":"text-text-quaternary"}`,style:k!=null?{color:ge}:void 0,children:k??"—"})}),e.jsx("td",{className:"px-2 py-1.5 text-right",style:{backgroundColor:Fe},children:e.jsx(O,{to:`/escalations/available?role=${encodeURIComponent(t.role)}&status=available`,className:`text-xs font-mono tabular-nums hover:underline ${n>0?"text-text-primary font-semibold":"text-text-quaternary"}`,onClick:g=>g.stopPropagation(),children:n})}),e.jsx("td",{className:"px-2 py-1.5 text-right",style:{backgroundColor:We},children:e.jsx(O,{to:`/escalations/available?role=${encodeURIComponent(t.role)}&status=claimed`,className:`text-xs font-mono tabular-nums hover:underline ${x>0?"font-semibold":"text-text-quaternary"}`,style:x>0?{color:W}:void 0,onClick:g=>g.stopPropagation(),children:x})}),e.jsx("td",{className:"px-2 py-1.5 text-right",style:{backgroundColor:Te},children:e.jsx(O,{to:`/escalations/available?role=${encodeURIComponent(t.role)}&status=resolved`,className:`text-xs font-mono tabular-nums hover:underline ${u>0?"text-text-secondary":"text-text-quaternary"}`,style:u>0?{color:E}:void 0,onClick:g=>g.stopPropagation(),children:u})}),e.jsx("td",{className:"hidden xl:table-cell px-2 py-1.5 text-right",children:e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:$e((a==null?void 0:a.wait.p99)??null)})}),e.jsx("td",{className:"hidden xl:table-cell px-2 py-1.5 text-right",children:e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:$e((a==null?void 0:a.work.p99)??null)})}),e.jsx("td",{className:"hidden lg:table-cell px-2 py-1.5",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${q}`,style:{width:`${N}%`}})}),e.jsx("span",{className:`text-2xs font-mono tabular-nums ${p!=null&&p>100?"text-status-warning font-semibold":"text-text-quaternary"}`,children:p!=null?`${p}%`:"—"})]})}),e.jsx("td",{className:"py-1.5",children:e.jsxs("div",{className:"flex items-center justify-center gap-1.5",children:[e.jsx(O,{to:`/escalations/available?role=${encodeURIComponent(t.role)}&status=all`,className:"text-text-quaternary hover:text-accent transition-colors",title:"View all items in queue",onClick:g=>g.stopPropagation(),children:e.jsx(Xe,{className:"w-3.5 h-3.5"})}),e.jsx("span",{className:"w-3.5 flex items-center justify-center",children:o>0&&e.jsx(O,{to:Ce(t),className:"text-status-error hover:opacity-70 transition-opacity",title:`${o} item${o===1?"":"s"} in jeopardy — pull oldest first`,onClick:g=>g.stopPropagation(),children:e.jsx(Le,{className:"w-3.5 h-3.5",strokeWidth:2})})})]})})]})}function Se({n:t}){return t<=0?null:e.jsxs("span",{className:"inline-flex items-center gap-0.5 font-semibold tabular-nums",style:{color:Y},title:`${t} in jeopardy`,children:[e.jsx(Le,{className:"w-3 h-3",strokeWidth:2.25}),t>9?"9+":t]})}function gt({fragments:t,aggregates:a,activeOrigin:r,onSelect:d}){const[c,h]=j.useState(!1),n=j.useRef(null);j.useEffect(()=>{if(!c)return;const o=p=>{n.current&&!n.current.contains(p.target)&&h(!1)},i=p=>{p.key==="Escape"&&h(!1)};return document.addEventListener("mousedown",o),document.addEventListener("keydown",i),()=>{document.removeEventListener("mousedown",o),document.removeEventListener("keydown",i)}},[c]);const x=t.find(o=>o.origin.role===r)??t[0];if(!x)return null;const u=a.get(x.origin.role);return e.jsxs("div",{ref:n,className:"relative px-4 pt-2",children:[e.jsxs("button",{type:"button",onClick:()=>h(o=>!o),className:"group flex items-baseline gap-2.5","aria-expanded":c,"aria-haspopup":"listbox",children:[e.jsx("span",{className:"text-sm font-medium text-text-primary group-hover:text-accent transition-colors",children:re(x.origin)}),e.jsxs("span",{className:"text-2xs font-mono text-text-quaternary tabular-nums",children:[(u==null?void 0:u.stations)??x.stations.length," station",((u==null?void 0:u.stations)??x.stations.length)===1?"":"s"," · ",(u==null?void 0:u.pending)??0," pending"]}),e.jsx(Se,{n:(u==null?void 0:u.jeopardy)??0}),e.jsx(Ke,{className:`w-3.5 h-3.5 self-center shrink-0 text-text-tertiary group-hover:text-accent transition-transform ${c?"rotate-180":""}`,strokeWidth:1.5})]}),c&&e.jsx("div",{role:"listbox",className:"absolute z-[100] top-full left-4 mt-1.5 min-w-[20rem] max-h-80 overflow-y-auto bg-surface-raised border border-surface-border rounded-md shadow-lg py-1",children:t.map(o=>{const i=a.get(o.origin.role),p=o.origin.role===x.origin.role;return e.jsxs("button",{type:"button",role:"option","aria-selected":p,onClick:()=>{d(o.origin.role),h(!1)},className:`w-full text-left px-3 py-1.5 flex items-baseline gap-2.5 transition-colors ${p?"text-accent bg-accent/5":"text-text-primary hover:bg-surface-hover"}`,children:[e.jsx("span",{className:"text-xs font-medium truncate",children:re(o.origin)}),e.jsxs("span",{className:"ml-auto text-2xs font-mono text-text-quaternary tabular-nums shrink-0",children:[(i==null?void 0:i.stations)??o.stations.length," st · ",(i==null?void 0:i.pending)??0," pending"]}),e.jsx("span",{className:"w-12 shrink-0 text-left text-2xs",children:e.jsx(Se,{n:(i==null?void 0:i.jeopardy)??0})})]},o.origin.role)})})]})}const jt=[{label:"TARGET/H",band:Ee,hue:T,w:"w-[8%]"},{label:"SLA/M",band:Oe,hue:mt,w:"w-[7%]"},{label:"WORKERS",band:Ae,hue:ge,w:"w-[8%]"},{label:"PENDING",band:Fe,hue:U,w:"w-[8%]"},{label:"CLAIMED",band:We,hue:W,w:"w-[8%]"},{label:"RESOLVED",band:Te,hue:E,w:"w-[8%]"}];function bt(t,a,r,d){if(!t||!a)return null;const c=t*a/60,h=r>0?r*a/(60*d):0;return Math.ceil(c+h)}const I="sticky top-0 z-10 bg-surface border-b border-surface-border py-1.5 text-2xs font-semibold uppercase tracking-wider whitespace-nowrap";function vt(){return e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:`${I} pl-3 pr-2 text-left text-text-quaternary`,children:"NAME"}),e.jsx("th",{className:`${I} hidden xl:table-cell w-[12%] pr-2 text-left text-text-quaternary`,children:"ROLE"}),jt.map(t=>e.jsx("th",{className:`${I} ${t.w} px-2 text-right`,style:{color:t.hue,backgroundImage:`linear-gradient(${t.band}, ${t.band})`},children:t.label},t.label)),e.jsx("th",{className:`${I} hidden xl:table-cell w-[8%] px-2 text-right text-text-quaternary`,children:"P99 WAIT"}),e.jsx("th",{className:`${I} hidden xl:table-cell w-[8%] px-2 text-right text-text-quaternary`,children:"P99 WORK"}),e.jsx("th",{className:`${I} hidden lg:table-cell w-[11%] px-2 text-left text-text-quaternary`,children:"TREND"}),e.jsx("th",{className:`${I} w-14 px-1 text-center text-text-quaternary`,children:"ACTIONS"})]})})}function Nt(){var ee;const[t,a]=j.useState("1h"),[r,d]=j.useState(null),[c,h]=j.useState(!1),[n,x]=j.useState(!0);Be();const{data:u,isLoading:o,refetch:i}=Ie(),{data:p,isLoading:q,refetch:N}=Re(t),k=(u==null?void 0:u.roles)??[],$=(p==null?void 0:p.stations)??[],S=j.useMemo(()=>{const l=new Map($.map(w=>[w.role,w])),b=new Map;for(const w of ye((u==null?void 0:u.roles)??[])){let z=0,s=0;for(const m of w.stations){const y=l.get(m.role.role);z+=(y==null?void 0:y.pending)??0,s+=(y==null?void 0:y.priority_count)??0}b.set(w.origin.role,{stations:w.stations.length,pending:z,jeopardy:s})}return b},[$,u]),f=j.useMemo(()=>ye(k),[k]),[g,M]=Ze(),G=g.get("fragment"),R=f.find(l=>l.origin.role===G)??f[0]??null,P=j.useCallback(l=>{d(null),M(b=>{const w=new URLSearchParams(b);return w.set("fragment",l),w})},[M]),_=(R==null?void 0:R.stations)??[],K=j.useMemo(()=>new Set(_.map(({role:l})=>l.role)),[_]),Q=j.useMemo(()=>$.filter(l=>K.has(l.role)),[$,K]),X=j.useMemo(()=>_.map(({role:l})=>({role:l.role,title:l.title,parent_role:l.parent_role,target_per_hour:l.target_per_hour??null,upstream_roles:l.upstream_roles??[],metric:$.find(b=>b.role===l.role)})),[_,$]),oe=j.useCallback(l=>{const b=f.find(w=>w.stations.some(({role:z})=>z.role===l));b&&P(b.origin.role)},[f,P]),D=Je(),le=((ee=_.find(({role:l})=>l.role===r))==null?void 0:ee.role)??null,B=o||q,ie=async()=>{h(!0),await Promise.all([i(),N()]),h(!1)},Z=l=>d(b=>b===l?null:l),J=e.jsx("div",{className:"flex items-center gap-0.5",children:pt.map(l=>e.jsx("button",{onClick:()=>a(l),className:`px-2.5 py-1 text-2xs font-mono rounded transition-colors ${t===l?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:l},l))});return e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsx(ze,{title:"Pace Board",docsHash:"#docs:dashboard.md:pace-board",center:J,actions:e.jsx("button",{onClick:ie,disabled:c,className:"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40",children:e.jsx(Ge,{className:`w-3.5 h-3.5 ${c?"animate-spin":""}`})})}),B?e.jsxs("div",{className:"animate-pulse space-y-6 mt-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded w-full"}),e.jsx("div",{className:"space-y-3",children:[1,2,3].map(l=>e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-full"},l))})]}):f.length===0?e.jsxs("div",{className:"mt-8",children:[e.jsx("p",{className:"text-sm text-text-secondary mb-2",children:"Stations appear here once roles are marked visible in Operations."}),e.jsxs("p",{className:"text-xs text-text-tertiary",children:["Go to"," ",e.jsx(O,{to:"/admin/roles",className:"text-accent hover:underline",children:"Roles"})," ","and enable ",e.jsx("strong",{children:"Visible in Operations"})," on roles that represent pipeline stations."]})]}):e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsxs("div",{className:"flex items-end justify-between",children:[e.jsx("div",{className:"flex-1",children:f.length>1&&e.jsx(gt,{fragments:f,aggregates:S,activeOrigin:(R==null?void 0:R.origin.role)??null,onSelect:P})}),e.jsx("div",{className:"flex items-center gap-0.5 px-4 pt-2 pb-0.5",children:["lin","log"].map(l=>e.jsx("button",{onClick:()=>x(l==="log"),className:`px-2 py-0.5 text-2xs font-mono rounded transition-colors ${(n?"log":"lin")===l?"text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,title:l==="lin"?"Linear Y axis":"Logarithmic Y axis — reveals shape across wide value ranges",children:l},l))})]}),e.jsxs("div",{className:"flex-1 min-h-[40vh] flex items-stretch overflow-hidden",children:[e.jsx("div",{className:"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden px-2 py-4",children:e.jsx(it,{stations:X,selectedRole:r,onSelect:Z,onUpstreamSelect:oe,onCmdClick:l=>D(`/escalations/available?role=${encodeURIComponent(l)}`),periodHours:we[t],logScale:n})}),e.jsx("div",{className:"w-px bg-surface-border shrink-0 self-stretch"}),e.jsx(ht,{role:le,allMetrics:Q,orderedRoles:_.map(l=>l.role),globalPeriod:t,onClose:()=>d(null)})]}),e.jsx("div",{className:"max-h-[30vh] flex-none overflow-y-auto border-t border-surface-border",children:e.jsxs("table",{className:"w-full table-fixed",children:[e.jsx(vt,{}),e.jsx("tbody",{children:_.map(({role:l})=>e.jsx(yt,{role:l,metric:$.find(b=>b.role===l.role),selected:r===l.role,periodHours:we[t],onClick:()=>Z(l.role)},l.role))})]})})]})]})}const Mt=Object.freeze(Object.defineProperty({__proto__:null,OperationsPage:Nt,buildFragments:ye},Symbol.toStringTag,{value:"Module"}));export{Mt as O,it as P,ye as b,Ce as j};
|
|
2
|
-
//# sourceMappingURL=OperationsPage-flrZ-EJ9.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OperationsPage-flrZ-EJ9.js","sources":["../../src/pages/operations/PaceChart.tsx","../../src/pages/operations/priority-link.ts","../../src/pages/operations/StationDetailPanel.tsx","../../src/pages/operations/OperationsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { StationMetric } from '../../api/escalations';\nimport { displayRoleTitle } from '../../lib/role-display';\n\nexport interface ChartStation {\n role: string;\n title: string | null;\n target_per_hour: number | null;\n parent_role: string | null;\n /** Roles feeding this station from other sequences — drawn as a merge glyph, not a bend in the line. */\n upstream_roles?: string[];\n metric: StationMetric | undefined;\n}\n\ninterface PaceChartProps {\n stations: ChartStation[];\n selectedRole: string | null;\n onSelect: (role: string) => void;\n /** Merge-glyph click — jump to the sequence that feeds this station. */\n onUpstreamSelect?: (upstreamRole: string) => void;\n /** Cmd/meta+click on a resolved dot — navigate to the role's full queue. */\n onCmdClick?: (role: string) => void;\n /** Selected window length in hours — target count = target_per_hour × this. */\n periodHours: number;\n /** Use a logarithmic Y axis. Reveals shape across a wide dynamic range (7d/30d). */\n logScale?: boolean;\n /** Fill the parent container — SVG uses h-full + preserveAspectRatio meet.\n Default: auto-height (scales by width). Use when the parent has a defined height. */\n fill?: boolean;\n}\n\n// ── SVG layout ────────────────────────────────────────────────────────────────\n\nconst W = 800;\nconst H = 270;\nconst ML = 28;\nconst MR = 60;\nconst MT = 24;\nconst MB = 72;\n\nconst chartW = W - ML - MR;\nconst chartH = H - MT - MB;\nconst bottom = MT + chartH;\nconst right = ML + chartW;\n\n// Smooth transition applied when the window changes and values re-scale.\nconst EASE = '0.5s cubic-bezier(0.4, 0, 0.2, 1)';\n\n// ── Path builders ─────────────────────────────────────────────────────────────\n\n// A one-point curve still needs a stroke: a short segment — slightly longer\n// than an em dash — centered through the point. It anchors the dot and its\n// end label in vertical space when a sequence has a single station (or a\n// single station carries a target).\nconst SOLO_HALF = 9;\nfunction soloSegment(p: { x: number; y: number }): string {\n return `M ${(p.x - SOLO_HALF).toFixed(1)} ${p.y.toFixed(1)} L ${(p.x + SOLO_HALF).toFixed(1)} ${p.y.toFixed(1)}`;\n}\n\n/** Catmull-Rom → cubic bezier — smooth curves for the target, actual, and active lines. */\nfunction catmullPath(pts: { x: number; y: number }[]): string {\n if (pts.length === 0) return '';\n if (pts.length === 1) return soloSegment(pts[0]);\n const segs: string[] = [];\n for (let i = 0; i < pts.length - 1; i++) {\n const p0 = pts[Math.max(0, i - 1)];\n const p1 = pts[i];\n const p2 = pts[i + 1];\n const p3 = pts[Math.min(pts.length - 1, i + 2)];\n const cp1x = (p1.x + (p2.x - p0.x) / 6).toFixed(1);\n const cp1y = (p1.y + (p2.y - p0.y) / 6).toFixed(1);\n const cp2x = (p2.x - (p3.x - p1.x) / 6).toFixed(1);\n const cp2y = (p2.y - (p3.y - p1.y) / 6).toFixed(1);\n if (i === 0) segs.push(`M ${p1.x.toFixed(1)} ${p1.y.toFixed(1)}`);\n segs.push(`C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x.toFixed(1)} ${p2.y.toFixed(1)}`);\n }\n return segs.join(' ');\n}\n\n/**\n * Closed region between two curves — upper spline forward, lower spline back.\n * Used for the queue-composition bands: the vertical thickness at any station\n * is the count difference between the two curves.\n */\nfunction bandPath(upper: { x: number; y: number }[], lower: { x: number; y: number }[]): string {\n if (upper.length < 2 || lower.length < 2) return '';\n const forward = catmullPath(upper);\n const rev = [...lower].reverse();\n const back = catmullPath(rev);\n // Drop the back path's leading M — the L below connects the two curves.\n const backSegs = back.slice(back.indexOf('C'));\n return `${forward} L ${rev[0].x.toFixed(1)} ${rev[0].y.toFixed(1)} ${backSegs} Z`;\n}\n\nfunction compact(n: number): string {\n return n >= 10000 ? `${Math.round(n / 1000)}k` : n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);\n}\n\nfunction last<T>(a: T[]): T | undefined {\n return a.length > 0 ? a[a.length - 1] : undefined;\n}\n\n/** Nice round tick values for a logarithmic Y axis up to maxVal. */\nfunction logTicks(maxVal: number): number[] {\n const candidates = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000];\n return candidates.filter((v) => v > 0 && v < maxVal * 0.98);\n}\n\n/** Nice round tick values for a linear Y axis up to maxVal. */\nfunction linearTicks(maxVal: number): number[] {\n const raw = [0.25, 0.5, 0.75].map((f) => Math.round(f * maxVal));\n return raw.filter((v) => v > 0);\n}\n\n// ── Colors ────────────────────────────────────────────────────────────────────\n\n// Shared queue-state palette — the chart bands and the station table columns\n// use the same hues so the two views read as one. Each hue resolves through\n// a --lt-* theme token so registered themes (including dark) restyle the\n// chart; the navy default keeps the original sky/orange/green/slate look.\nexport const ACTIVE_COLOR = 'rgb(var(--lt-status-claimed-graphic))'; // claimed, being worked right now — orange\nexport const QUEUED_COLOR = 'rgb(var(--lt-status-queued-graphic))'; // pending and unclaimed, waiting in the queue — sky\nexport const RESOLVED_COLOR = 'rgb(var(--lt-status-success-graphic))'; // done — green\nexport const TARGET_COLOR = 'rgb(var(--lt-text-quaternary))'; // target pace line — muted gray\n\n// Priority — unclaimed items past the role's age threshold, the count the\n// floor pulls to the front of the rack.\n// Jeopardy shares one red across the product (the pill, the table sub-row,\n// this chart's badge) — the hard-limit alarm, distinct from every pace hue.\nexport const PRIORITY_COLOR = 'rgb(var(--lt-status-error))';\nexport const PRIORITY_TEXT_COLOR = 'rgb(var(--lt-status-error))';\n\n/** Translucent tint of a token-based `rgb(var(--…))` color, for band fills. */\nexport function withAlpha(color: string, alpha: number): string {\n return color.replace(/\\)$/, ` / ${alpha})`);\n}\n\n// ── End-label stacking — spread close labels so text doesn't collide ────────────\n\nconst MIN_GAP = 13;\nfunction spreadLabels<T extends { y: number }>(labels: T[], maxY: number): (T & { labelY: number })[] {\n const sorted = [...labels].sort((a, b) => a.y - b.y);\n let prev = -Infinity;\n const placed = sorted.map((l) => {\n const labelY = Math.max(l.y + 3, prev + MIN_GAP);\n prev = labelY;\n return { ...l, labelY };\n });\n // On a quiet window every curve sits at zero and the stack would descend\n // past the floor into the station titles — lift the whole stack back above\n // maxY instead.\n const overflow = placed.length > 0 ? placed[placed.length - 1].labelY - maxY : 0;\n if (overflow > 0) {\n for (const l of placed) l.labelY -= overflow;\n }\n return placed;\n}\n\n// ── Chart ─────────────────────────────────────────────────────────────────────\n\nexport function PaceChart({ stations, selectedRole, onSelect, onUpstreamSelect, onCmdClick, periodHours, logScale = false, fill = false }: PaceChartProps) {\n const [hoveredIdx, setHoveredIdx] = useState<number | null>(null);\n\n const n = stations.length;\n if (n === 0) return null;\n\n const xOf = (i: number) => (n === 1 ? ML + chartW / 2 : ML + (i / (n - 1)) * chartW);\n const xMap = new Map(stations.map((s, i) => [s.role, xOf(i)]));\n\n // Per-station counts for the selected window:\n // - target : expected units = target_per_hour × periodHours (e.g. 22/h · 15m ≈ 5).\n // - actual : units resolved in the window (\"the number we see\").\n // - active : units in the queue being worked right now (claimed).\n const rows = stations.map((s, i) => {\n const target = s.target_per_hour ?? null;\n const expected = target != null ? target * periodHours : null;\n return {\n idx: i,\n x: xOf(i),\n expected,\n actual: s.metric?.resolved ?? 0,\n active: s.metric?.claimed ?? 0,\n pending: s.metric?.pending ?? 0,\n priorityCount: s.metric?.priority_count ?? 0,\n };\n });\n\n const hasTargets = rows.some((r) => r.expected != null);\n\n const dataMax = Math.max(\n 1,\n ...rows.map((r) => r.expected ?? 0),\n ...rows.map((r) => r.actual),\n ...rows.map((r) => (r.expected != null ? r.pending : 0)),\n );\n const maxVal = dataMax * 1.15;\n\n const yScale = logScale\n ? (v: number) => {\n const logMax = Math.log(1 + maxVal);\n return MT + chartH * (1 - Math.log(1 + Math.max(0, v)) / logMax);\n }\n : (v: number) => MT + chartH * (1 - Math.max(0, Math.min(v, maxVal)) / maxVal);\n\n const gridTicks = logScale ? logTicks(maxVal) : linearTicks(maxVal);\n\n const withTarget = rows.filter((r) => r.expected != null);\n const targetPts = withTarget.map((r) => ({ ...r, y: yScale(r.expected as number) }));\n const actualPts = withTarget.map((r) => ({ ...r, y: yScale(r.actual) }));\n const activePts = withTarget.map((r) => ({ ...r, y: yScale(r.active) }));\n const pendingPts = withTarget.map((r) => ({ ...r, y: yScale(r.pending) }));\n\n const targetLinePath = catmullPath(targetPts);\n const actualSplinePath = catmullPath(actualPts);\n const activeSplinePath = catmullPath(activePts);\n const pendingSplinePath = catmullPath(pendingPts);\n\n const lastActual = last(actualPts);\n const lastTarget = last(targetPts);\n const lastActive = last(activePts);\n const lastPending = last(pendingPts);\n\n const areaPath =\n actualPts.length >= 2 && lastActual\n ? `${actualSplinePath} L ${lastActual.x.toFixed(1)} ${bottom} L ${actualPts[0].x.toFixed(1)} ${bottom} Z`\n : '';\n\n // Queue composition — pending splits into two stacked bands:\n // floor → active curve = claimed, being worked (indigo)\n // active → pending curve = waiting, unclaimed (sky)\n // When workers claim everything the instant it lands, the sky band collapses\n // to zero and the whole queue reads as indigo.\n const hasQueue = withTarget.some((r) => r.pending > 0 || r.active > 0);\n const workedBandPath =\n hasQueue && activePts.length >= 2 && lastActive\n ? `${activeSplinePath} L ${lastActive.x.toFixed(1)} ${bottom} L ${activePts[0].x.toFixed(1)} ${bottom} Z`\n : '';\n const queuedBandPath = hasQueue ? bandPath(pendingPts, activePts) : '';\n\n // Quiet lines keep their curves (flat at zero) but skip their end labels —\n // a stack of names pointing at nothing is noise.\n const showActiveLabel = withTarget.some((r) => r.active > 0);\n const showQueuedLabel = withTarget.some((r) => r.pending > r.active);\n const endLabels = spreadLabels(\n [\n lastTarget ? { key: 'target', text: 'Target', color: TARGET_COLOR, y: lastTarget.y } : null,\n lastActual ? { key: 'actual', text: 'Resolved', color: RESOLVED_COLOR, y: lastActual.y } : null,\n showActiveLabel && lastActive\n ? { key: 'active', text: 'Claimed', color: ACTIVE_COLOR, y: lastActive.y }\n : null,\n showQueuedLabel && lastPending\n ? { key: 'queued', text: 'Pending', color: QUEUED_COLOR, y: lastPending.y }\n : null,\n ].filter(Boolean) as { key: string; text: string; color: string; y: number }[],\n bottom + 4,\n );\n\n return (\n <svg\n viewBox={`0 0 ${W} ${H}`}\n className={fill ? 'w-full h-full' : 'w-full'}\n preserveAspectRatio=\"xMidYMid meet\"\n style={{ display: 'block', ...(fill ? {} : { height: 'auto' }) }}\n >\n {/* Y-axis reference lines — very faint horizontal marks that communicate\n the scale. Log mode uses dashed lines at round values; linear uses\n solid hairlines at even fractions. */}\n {gridTicks.map((v) => {\n const gy = yScale(v);\n return (\n <g key={`grid-${v}`}>\n <line\n x1={ML}\n y1={gy}\n x2={right}\n y2={gy}\n className=\"stroke-surface-border\"\n strokeWidth={0.5}\n opacity={logScale ? 0.45 : 0.3}\n strokeDasharray={logScale ? '2 5' : undefined}\n style={{ transition: `y1 ${EASE}, y2 ${EASE}` }}\n />\n <text\n x={ML - 4}\n y={gy + 2.5}\n textAnchor=\"end\"\n fontSize={6}\n fontFamily=\"ui-monospace, monospace\"\n opacity={0.35}\n className=\"fill-text-quaternary\"\n >\n {compact(v)}\n </text>\n </g>\n );\n })}\n\n {/* Dependency connector lines (parent → child), drawn at chart floor */}\n {stations.map((s) => {\n if (!s.parent_role) return null;\n const px = xMap.get(s.parent_role);\n if (px == null) return null;\n const cx = xOf(stations.findIndex((r) => r.role === s.role));\n return <line key={`dep-${s.role}`} x1={px} y1={bottom} x2={cx} y2={bottom} className=\"stroke-surface-border\" strokeWidth={0.75} />;\n })}\n\n {/* Area under the actual curve */}\n {areaPath && <path d={areaPath} opacity={0.05} style={{ fill: RESOLVED_COLOR, transition: `d ${EASE}` }} />}\n\n {/* Queue composition bands — the band heights split each station's\n pending total into claimed-and-worked (indigo, floor→active) and\n waiting-unclaimed (sky, active→pending). */}\n {workedBandPath && (\n <path d={workedBandPath} opacity={0.12} style={{ fill: ACTIVE_COLOR, transition: `d ${EASE}` }} />\n )}\n {queuedBandPath && (\n <path d={queuedBandPath} opacity={0.12} style={{ fill: QUEUED_COLOR, transition: `d ${EASE}` }} />\n )}\n\n {/* Target — dashed reference curve at the window's expected count */}\n {targetLinePath && (\n <path d={targetLinePath} fill=\"none\" strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.7} style={{ stroke: TARGET_COLOR, transition: `d ${EASE}` }} />\n )}\n\n {/* Per-station target markers — a small emerald dot on the target line\n (mirroring the Resolved line's dots) with the window's expected count\n (target_per_hour × duration) just above it */}\n {targetPts.map((tp) => (\n <g key={`tval-${tp.idx}`} transform={`translate(${tp.x} ${tp.y})`} style={{ transition: `transform ${EASE}` }}>\n <circle r={2} opacity={0.8} style={{ fill: TARGET_COLOR }} />\n <text\n y={-5}\n textAnchor=\"middle\"\n fontSize={6.5}\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"500\"\n opacity={0.7}\n style={{ fill: TARGET_COLOR }}\n >\n {Math.round(tp.expected as number)}\n </text>\n </g>\n ))}\n\n {/* Pending — thin dotted sky edge along the queue total (top of the bands) */}\n {hasQueue && pendingSplinePath && (\n <path d={pendingSplinePath} fill=\"none\" strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.55} style={{ stroke: QUEUED_COLOR, transition: `d ${EASE}` }} />\n )}\n\n {/* Active — thin dotted indigo, how many are claimed and being worked */}\n {activeSplinePath && (\n <path d={activeSplinePath} fill=\"none\" strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.5} style={{ stroke: ACTIVE_COLOR, transition: `d ${EASE}` }} />\n )}\n\n {/* Actual — thin solid, primary line */}\n {actualSplinePath && (\n <path d={actualSplinePath} fill=\"none\" strokeWidth={0.6} strokeLinejoin=\"round\" strokeLinecap=\"round\" opacity={0.65} style={{ stroke: RESOLVED_COLOR, transition: `d ${EASE}` }} />\n )}\n\n {/* Queue markers — a dot on the active line per station.\n Above the dot, at the pending edge: the queue total (sky when part of\n it is still waiting, slate when everything is claimed).\n Below the dot: active (claimed, being worked right now). */}\n {activePts.map((ap, i) => {\n if (ap.pending <= 0 && ap.active <= 0) return null;\n const ar = Math.max(2.5, Math.min(5, 2.5 + ap.active / 10));\n const hasWaiting = ap.pending > ap.active;\n // Anchor the queue-total label to the pending edge so it rides the top\n // of the band; keep it clear of the dot when the two curves touch.\n const pendingLabelY = Math.min(pendingPts[i].y - 4, ap.y - ar - 4);\n return (\n <g key={`active-${ap.idx}`}>\n {ap.pending > 0 && (\n <text\n x={ap.x}\n y={pendingLabelY}\n textAnchor=\"middle\"\n fontSize={7.5}\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"600\"\n style={{ fill: hasWaiting ? QUEUED_COLOR : 'rgb(var(--lt-text-tertiary))', transition: `y ${EASE}` }}\n >\n {ap.pending}\n </text>\n )}\n <g transform={`translate(${ap.x} ${ap.y})`} style={{ transition: `transform ${EASE}` }}>\n <circle r={ar} opacity={0.85} style={{ fill: ACTIVE_COLOR }} />\n {ap.active > 0 && (\n <text y={ar + 9} textAnchor=\"middle\" fontSize={7.5} fontFamily=\"ui-monospace, monospace\" fontWeight=\"600\" style={{ fill: ACTIVE_COLOR }}>\n {ap.active}\n </text>\n )}\n </g>\n </g>\n );\n })}\n\n {/* Station nodes — actual (resolved) as a dot on the actual line, styled like\n the active dot but a bit larger (actual is the most important number).\n The count sits below the dot so it stays legible as numbers grow. */}\n {rows.map((row) => {\n const s = stations[row.idx];\n const x = row.x;\n const cy = row.expected != null ? yScale(row.actual) : bottom;\n // Same small scale as the active dot — a hair larger.\n const r = Math.max(2.25, Math.min(4, 2.25 + row.actual / 160));\n const isSelected = s.role === selectedRole;\n const isHovered = hoveredIdx === row.idx;\n\n const tooltip =\n row.expected == null\n ? 'idle · set a target rate to plot pace'\n : `${row.actual} resolved · target ${Math.round(row.expected)}`\n + (row.pending > row.active ? ` · ${row.pending - row.active} pending` : '')\n + (row.active > 0 ? ` · ${row.active} claimed` : '')\n + (row.priorityCount > 0 ? ` · ${row.priorityCount} in jeopardy` : '');\n const tipW = tooltip.length * 5.5 + 20;\n const tipX = Math.max(ML + tipW / 2 + 4, Math.min(right - tipW / 2 - 4, x));\n const tipY = cy - r - 10;\n\n return (\n <g\n key={s.role}\n onClick={(e) => {\n if (e.metaKey || e.ctrlKey) {\n e.stopPropagation();\n onCmdClick?.(s.role);\n } else {\n onSelect(s.role);\n }\n }}\n onMouseEnter={() => setHoveredIdx(row.idx)}\n onMouseLeave={() => setHoveredIdx(null)}\n style={{ cursor: 'pointer' }}\n >\n {/* Animated marker group — slides vertically as the window rescales */}\n <g transform={`translate(${x} ${cy})`} style={{ transition: `transform ${EASE}` }}>\n <circle r={22} fill=\"transparent\" />\n <circle r={r} opacity={0.9} style={{ fill: RESOLVED_COLOR, transition: `r ${EASE}` }} />\n {row.expected != null && (\n <text y={-r - 5} textAnchor=\"middle\" fontSize={9.5} fontFamily=\"ui-monospace, monospace\" fontWeight=\"500\" style={{ fill: RESOLVED_COLOR }}>\n {compact(row.actual)}\n </text>\n )}\n {isHovered && !isSelected && <circle r={r + 3} fill=\"none\" strokeWidth={1} opacity={0.4} style={{ stroke: RESOLVED_COLOR }} />}\n {isSelected && <circle r={r + 4} fill=\"none\" className=\"stroke-accent\" strokeWidth={2} />}\n </g>\n\n\n {/* Merge affordance — this station also receives input from another\n sequence. The dashed drop into the floor says \"a side-quest\n lands here\"; clicking it opens that sequence. It is a symbol,\n deliberately not a line — the upstream is NOT a descendant. */}\n {(s.upstream_roles?.length ?? 0) > 0 && (\n <g\n transform={`translate(${x - 20} ${bottom})`}\n onClick={(e) => {\n e.stopPropagation();\n onUpstreamSelect?.(s.upstream_roles![0]);\n }}\n style={{ cursor: 'pointer' }}\n >\n <title>{`Fed by ${s.upstream_roles!.join(', ')} — click to view that sequence`}</title>\n <circle r={9} cy={-6} fill=\"transparent\" />\n <path d=\"M -7 -14 C -3 -14 -1 -9 0 -2\" fill=\"none\" className=\"stroke-text-quaternary\" strokeWidth={1} strokeDasharray=\"2 2\" strokeLinecap=\"round\" />\n <path d=\"M 0 -2 L 8 -2\" className=\"stroke-text-quaternary\" strokeWidth={1} strokeLinecap=\"round\" />\n <path d=\"M 8 -2 l -3.5 -2 v 4 z\" className=\"fill-text-quaternary\" />\n </g>\n )}\n\n {/* Station label — the display name (user-set title, or derived\n from the role id). The exact id lives in the table below.\n Adjacent labels alternate depth from the baseline so dense\n sequences don't collide. */}\n <text\n x={x}\n y={bottom + (row.idx % 2 === 0 ? 14 : 27)}\n textAnchor=\"middle\"\n fontSize={9.5}\n className=\"fill-text-tertiary\"\n fontFamily=\"ui-sans-serif, sans-serif\"\n fontWeight=\"500\"\n >\n {displayRoleTitle(s)}\n </text>\n\n {/* Tooltip */}\n {isHovered && (\n <g>\n <rect x={tipX - tipW / 2} y={tipY - 16} width={tipW} height={19} rx={3} className=\"fill-text-primary\" opacity={0.88} />\n <text x={tipX} y={tipY - 3} textAnchor=\"middle\" fontSize={8.5} className=\"fill-surface\" fontFamily=\"ui-monospace, monospace\">\n {tooltip}\n </text>\n </g>\n )}\n </g>\n );\n })}\n\n {/* No-target hint */}\n {!hasTargets && (\n <text x={ML + chartW / 2} y={bottom + 48} textAnchor=\"middle\" fontSize={8.5} className=\"fill-text-quaternary\" fontFamily=\"ui-sans-serif, sans-serif\">\n Set target_per_hour on each role in Admin → Roles to enable the pace chart\n </text>\n )}\n\n {/* End labels — text + count, rendered last so they sit above everything */}\n {endLabels.map((l) => (\n <text\n key={l.key}\n x={right + 6}\n y={l.labelY}\n fontSize={8}\n fontFamily=\"ui-sans-serif, sans-serif\"\n fontWeight={l.key === 'actual' ? 700 : 500}\n opacity={l.key === 'active' ? 0.85 : 1}\n style={{ fill: l.color, transition: `y ${EASE}` }}\n >\n {l.text}\n </text>\n ))}\n </svg>\n );\n}\n","import type { RoleDetail } from '../../api/roles';\n\n/**\n * The jeopardy deep link — the plant manager's \"focus on exactly these n items\".\n * One builder for every jeopardy pill (Pace Board chart, Home mini board, the\n * operator's Task Queue cards), producing a URL that reproduces the pill's set\n * precisely:\n *\n * - `jeopardy=1` — the server-side threshold predicate (the SAME expression\n * that produced the pill's count), so the list total equals the pill.\n * - `orderBy` on the role's age origin, ascending — oldest first. The metadata\n * facet when configured, else created_at; the sort control labels the field.\n * - `view=table` — a discrete, countable list, never the timeline.\n */\nexport function jeopardyQueueLink(role: Pick<RoleDetail, 'role' | 'priority_facet'>): string {\n const field = role.priority_facet ? `metadata.${role.priority_facet}` : 'created_at';\n const orderBy = JSON.stringify([{ field, direction: 'asc' }]);\n return `/escalations/available?role=${encodeURIComponent(role.role)}&jeopardy=1&view=table&orderBy=${encodeURIComponent(orderBy)}`;\n}\n","import { useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { X, ExternalLink, Settings, List } from 'lucide-react';\nimport { useStationMetrics } from '../../api/escalations';\nimport type { StationMetric } from '../../api/escalations';\nimport type { RoleDetail } from '../../api/roles';\nimport { PRIORITY_TEXT_COLOR } from './PaceChart';\nimport { jeopardyQueueLink } from './priority-link';\nimport { displayRoleTitle } from '../../lib/role-display';\n\ninterface StationDetailPanelProps {\n role: RoleDetail | null;\n allMetrics: StationMetric[];\n orderedRoles: RoleDetail[];\n globalPeriod: string;\n onClose: () => void;\n}\n\n// Mirrors OperationsPage's selector — the panel reports the same windows the\n// chart can show, and opens on whichever one the chart has selected.\nconst PERIODS = ['15m', '1h', '24h', '7d', '30d'] as const;\ntype Period = (typeof PERIODS)[number];\n\nconst isPeriod = (p: string): p is Period => (PERIODS as readonly string[]).includes(p);\n\nfunction fmt(min: number | null): string {\n if (min == null) return '—';\n if (min < 1) {\n const s = Math.round(min * 60);\n return s <= 0 ? '< 1s' : `${s}s`;\n }\n if (min < 60) return `${min.toFixed(0)}m`;\n return `${(min / 60).toFixed(1)}h`;\n}\n\nfunction MetricRow({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between py-1.5\">\n <span className=\"text-2xs text-text-tertiary uppercase tracking-wider\">{label}</span>\n <span className=\"text-xs font-mono font-medium text-text-primary tabular-nums\">{value}</span>\n </div>\n );\n}\n\nfunction PeriodSelector({ period, onChange }: { period: Period; onChange: (p: Period) => void }) {\n return (\n <div className=\"flex items-center gap-1 mb-4\">\n {PERIODS.map((p) => (\n <button\n key={p}\n onClick={() => onChange(p)}\n className={`px-2 py-0.5 text-2xs font-mono rounded transition-colors ${\n period === p\n ? 'bg-accent/10 text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n );\n}\n\nfunction RoleView({ role, globalPeriod, onClose }: { role: RoleDetail; globalPeriod: string; onClose: () => void }) {\n // Open on the chart's selected window so the panel's numbers agree with the\n // chart beside it; the local selector still lets the viewer drill around.\n const [period, setPeriod] = useState<Period>(isPeriod(globalPeriod) ? globalPeriod : '24h');\n const { data } = useStationMetrics(period);\n const metric = data?.stations.find((s) => s.role === role.role);\n const slaMinutes = role.sla_minutes ?? undefined;\n const targetPerHour = role.target_per_hour ?? undefined;\n const workerCount = role.worker_count ?? undefined;\n\n return (\n <>\n {/* Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline gap-1.5 flex-wrap\">\n <span className=\"text-sm font-bold text-text-primary\">{displayRoleTitle(role)}</span>\n <span className=\"text-2xs font-mono text-text-secondary\">{role.role}</span>\n </div>\n {role.description && (\n <p className=\"text-2xs text-text-tertiary mt-1 line-clamp-2 leading-relaxed\">\n {role.description}\n </p>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n <div className=\"flex items-center gap-3 mb-6\">\n <Link\n to={`/admin/roles/${encodeURIComponent(role.role)}`}\n className=\"flex items-center gap-1 text-2xs text-accent hover:underline\"\n >\n <Settings className=\"w-2.5 h-2.5\" />\n Edit in Roles\n </Link>\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}`}\n className=\"flex items-center gap-1 text-2xs text-accent hover:underline\"\n >\n <List className=\"w-2.5 h-2.5\" />\n View full queue\n </Link>\n </div>\n\n <PeriodSelector period={period} onChange={setPeriod} />\n\n {/* Capacity settings */}\n {(targetPerHour || slaMinutes || workerCount) && (\n <div className=\"flex items-center gap-6 mb-6 text-2xs border-b border-surface-border/30 pb-5\">\n {targetPerHour && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary text-sm\">{targetPerHour}/h</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5\">target</div>\n </div>\n )}\n {slaMinutes && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary text-sm\">{slaMinutes}m</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5\">SLA</div>\n </div>\n )}\n {workerCount && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary text-sm\">{workerCount}</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px] mt-0.5\">workers</div>\n </div>\n )}\n </div>\n )}\n\n {/* Two-column wait / work grid */}\n <div className=\"grid grid-cols-2 gap-x-4 mb-2\">\n <div>\n <p className=\"text-2xs text-text-quaternary uppercase tracking-wider mb-2\">Wait (queue)</p>\n <MetricRow label=\"P99\" value={fmt(metric?.wait.p99 ?? null)} />\n <MetricRow label=\"avg\" value={fmt(metric?.wait.avg ?? null)} />\n </div>\n <div>\n <p className=\"text-2xs text-text-quaternary uppercase tracking-wider mb-2\">Work (proc.)</p>\n <MetricRow label=\"P99\" value={fmt(metric?.work.p99 ?? null)} />\n <MetricRow label=\"avg\" value={fmt(metric?.work.avg ?? null)} />\n </div>\n </div>\n\n {/* SLA + priority */}\n <div className=\"border-t border-surface-border/40 pt-4 mt-5 space-y-2.5\">\n {slaMinutes && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xs text-text-tertiary\">SLA target</span>\n <span className=\"text-xs font-mono text-text-secondary\">{slaMinutes}m</span>\n </div>\n )}\n {metric && metric.priority_count > 0 && (\n <Link\n to={jeopardyQueueLink(role)}\n className=\"flex items-center justify-between group\"\n >\n <span className=\"text-2xs\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {metric.priority_count} in jeopardy — pull oldest first\n </span>\n <ExternalLink className=\"w-3 h-3 transition-colors\" style={{ color: PRIORITY_TEXT_COLOR }} />\n </Link>\n )}\n </div>\n\n </>\n );\n}\n\nfunction OverviewPanel({\n allMetrics,\n orderedRoles,\n period,\n}: {\n allMetrics: StationMetric[];\n orderedRoles: RoleDetail[];\n period: string;\n}) {\n const totalPending = allMetrics.reduce((s, m) => s + m.pending, 0);\n const totalResolved = allMetrics.reduce((s, m) => s + m.resolved, 0);\n const totalPriority = allMetrics.reduce((s, m) => s + m.priority_count, 0);\n\n const metricByRole = new Map(allMetrics.map((m) => [m.role, m]));\n const stationsAtRisk = allMetrics.filter((m) => m.priority_count > 0).length;\n const stationsWithLoad = allMetrics.filter((m) => m.pending > 0).length;\n\n return (\n <>\n <p className=\"text-2xs text-text-quaternary uppercase tracking-wider mb-3\">\n Pipeline · {period}\n </p>\n\n {/* Health headline */}\n <div className=\"mb-4\">\n {stationsAtRisk > 0 ? (\n <p className=\"text-2xs\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {stationsAtRisk} station{stationsAtRisk > 1 ? 's' : ''} with items in jeopardy\n </p>\n ) : stationsWithLoad > 0 ? (\n <p className=\"text-2xs text-status-warning\">\n {stationsWithLoad} station{stationsWithLoad > 1 ? 's' : ''} with backlog\n </p>\n ) : totalResolved > 0 ? (\n <p className=\"text-2xs text-status-success\">Flowing — queue clear</p>\n ) : (\n <p className=\"text-2xs text-text-quaternary\">Quiet — awaiting work</p>\n )}\n </div>\n\n {/* Key counters */}\n <div className=\"space-y-1.5 mb-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider\">Pending</span>\n <span className={`text-xs font-mono tabular-nums ${totalPending > 0 ? 'text-text-primary font-semibold' : 'text-text-quaternary'}`}>\n {totalPending}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider\">Resolved · {period}</span>\n <span className=\"text-xs font-mono tabular-nums text-text-secondary\">{totalResolved}</span>\n </div>\n {totalPriority > 0 && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider\">Priority</span>\n <span className=\"text-xs font-mono tabular-nums font-semibold\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {totalPriority}\n </span>\n </div>\n )}\n </div>\n\n {/* Divider + column headers */}\n <div className=\"border-t border-surface-border/40 pt-2 mb-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider flex-1\">Station</span>\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">pend</span>\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">clmd</span>\n <span className=\"text-2xs text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">res</span>\n </div>\n </div>\n\n {/* Station list in pipeline order */}\n <div className=\"space-y-0.5\">\n {orderedRoles.map((r) => {\n const m = metricByRole.get(r.role);\n const pending = m?.pending ?? 0;\n const claimed = m?.claimed ?? 0;\n const resolved = m?.resolved ?? 0;\n const priorityCount = m?.priority_count ?? 0;\n const label = r.title || r.role;\n const hasAlert = priorityCount > 0;\n const hasLoad = pending > 0;\n\n return (\n <div key={r.role} className=\"flex items-center gap-2 py-0.5\">\n <span\n className={`text-2xs font-mono flex-1 truncate ${\n hasLoad ? 'text-text-primary' : 'text-text-quaternary'\n }`}\n >\n {label}\n {hasAlert && (\n <span className=\"ml-1 text-2xs font-semibold\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {priorityCount} PRIORITY\n </span>\n )}\n </span>\n <span\n className={`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${\n hasLoad ? 'text-text-primary font-semibold' : 'text-text-quaternary'\n }`}\n >\n {pending > 0 ? pending : '—'}\n </span>\n <span className={`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${claimed > 0 ? 'text-accent font-semibold' : 'text-text-quaternary'}`}>\n {claimed > 0 ? claimed : '—'}\n </span>\n <span className={`text-2xs font-mono tabular-nums w-8 text-right shrink-0 ${resolved > 0 ? 'text-text-secondary' : 'text-text-quaternary'}`}>\n {resolved > 0 ? resolved : '—'}\n </span>\n </div>\n );\n })}\n </div>\n\n <p className=\"text-2xs text-text-quaternary mt-5\">\n Select a station for queue detail.\n </p>\n </>\n );\n}\n\nexport function StationDetailPanel({\n role,\n allMetrics,\n orderedRoles,\n globalPeriod,\n onClose,\n}: StationDetailPanelProps) {\n return (\n <div className=\"w-[280px] shrink-0 px-7 py-10 flex flex-col overflow-y-auto min-h-0\">\n {role ? (\n <RoleView role={role} globalPeriod={globalPeriod} onClose={onClose} />\n ) : (\n <OverviewPanel allMetrics={allMetrics} orderedRoles={orderedRoles} period={globalPeriod} />\n )}\n </div>\n );\n}\n","import { useState, useEffect, useMemo, useCallback, useRef } from 'react';\nimport { Link, useNavigate, useSearchParams } from 'react-router-dom';\nimport { GitMerge, RefreshCw, Eye, TriangleAlert, ChevronDown } from 'lucide-react';\nimport { useRoleDetails, useUpdateRole, type RoleDetail } from '../../api/roles';\nimport { useStationMetrics } from '../../api/escalations';\nimport type { StationMetric } from '../../api/escalations';\nimport { useStationMetricsEvents } from '../../hooks/useEventHooks';\nimport { PageHeader } from '../../components/common/layout/PageHeader';\nimport {\n PaceChart,\n ACTIVE_COLOR,\n QUEUED_COLOR,\n RESOLVED_COLOR,\n TARGET_COLOR,\n PRIORITY_TEXT_COLOR,\n withAlpha,\n type ChartStation,\n} from './PaceChart';\nimport { StationDetailPanel } from './StationDetailPanel';\nimport { jeopardyQueueLink } from './priority-link';\nimport { displayRoleTitle } from '../../lib/role-display';\n\n// Column band tints — same hues as the chart bands (~8% alpha).\n// Semantic palette: pending=sky, claimed=orange, resolved=green, target=slate, workers=accent.\nconst SLA_COLOR = TARGET_COLOR;\nconst WORKERS_COLOR = 'rgb(var(--lt-accent))';\nconst TARGET_BAND = withAlpha(TARGET_COLOR, 0.09);\nconst SLA_BAND = withAlpha(TARGET_COLOR, 0.09);\nconst WORKERS_BAND = withAlpha(WORKERS_COLOR, 0.07);\nconst PENDING_BAND = withAlpha(QUEUED_COLOR, 0.08);\nconst ACTIVE_BAND = withAlpha(ACTIVE_COLOR, 0.08);\nconst RESOLVED_BAND = withAlpha(RESOLVED_COLOR, 0.08);\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nconst PERIODS = ['15m', '1h', '24h', '7d', '30d'] as const;\ntype Period = (typeof PERIODS)[number];\n\n// Window length in hours — used to express the target as a count for the\n// selected window (target_per_hour × hours), e.g. 22/h over 15m ≈ 5 expected.\nconst PERIOD_HOURS: Record<Period, number> = {\n '15m': 0.25,\n '1h': 1,\n '24h': 24,\n '7d': 168,\n '30d': 720,\n};\n\ninterface OrderedStation {\n role: RoleDetail;\n depth: number;\n}\n\n/**\n * One sequence of stations, named by its origin role. Execution is a graph;\n * each fragment is the human-readable line the user composed via parent_role.\n * Cross-fragment edges live in upstream_roles and render as a merge affordance\n * on the chart, never as a bend in the line.\n */\ninterface SequenceFragment {\n origin: RoleDetail;\n stations: OrderedStation[];\n}\n\n// ── Fragment builder — BFS per parent_role root, tracking actual depth ────────\n\nexport function buildFragments(roles: RoleDetail[]): SequenceFragment[] {\n const opsRoles = roles.filter((r) => r.ops_visible);\n const roleMap = new Map(opsRoles.map((r) => [r.role, r]));\n const visited = new Set<string>();\n\n // Roots: no parent, or parent not in ops set — each roots its own fragment.\n const roots = opsRoles.filter((r) => !r.parent_role || !roleMap.has(r.parent_role));\n\n const fragments: SequenceFragment[] = roots.map((root) => {\n const stations: OrderedStation[] = [];\n const queue: OrderedStation[] = [{ role: root, depth: 0 }];\n while (queue.length > 0) {\n const item = queue.shift()!;\n if (visited.has(item.role.role)) continue;\n visited.add(item.role.role);\n stations.push(item);\n opsRoles\n .filter((c) => c.parent_role === item.role.role && !visited.has(c.role))\n .forEach((c) => queue.push({ role: c, depth: item.depth + 1 }));\n }\n return { origin: root, stations };\n });\n\n // Unreachable (cycles / dangling) — each stands alone rather than vanishing.\n opsRoles\n .filter((r) => !visited.has(r.role))\n .forEach((r) => fragments.push({ origin: r, stations: [{ role: r, depth: 0 }] }));\n\n // Longest line first — the primary sequence leads; side-quests follow.\n return fragments.sort(\n (a, b) => b.stations.length - a.stations.length || a.origin.role.localeCompare(b.origin.role),\n );\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction fmtMin(v: number | null): string {\n if (v == null) return '—';\n if (v < 1) {\n const s = Math.round(v * 60);\n return s <= 0 ? '< 1s' : `${s}s`;\n }\n if (v < 60) return `${v.toFixed(0)}m`;\n return `${(v / 60).toFixed(1)}h`;\n}\n\nfunction loadBar(pending: number, target: number | null) {\n // No target, or nothing pending — there is no live load to trend. Show the\n // empty state rather than inventing a rate from historical throughput.\n if (!target || pending === 0) return { pct: null, color: 'bg-surface-border' };\n\n const ratio = pending / target;\n const pct = Math.round(ratio * 100);\n // Amber: backlog. Orange: some work but well under pace. Green: healthy.\n const color = ratio > 1.0 ? 'bg-status-warning' : ratio < 0.2 ? 'bg-status-draft' : 'bg-status-success';\n return { pct, color };\n}\n\n// ── Editable numeric cell ─────────────────────────────────────────────────────\n\nfunction EditableNumber({ value, onSave }: { value: number | null; onSave: (n: number | null) => void }) {\n const [local, setLocal] = useState(value != null ? String(value) : '');\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const commit = (val: string) => {\n const n = parseInt(val, 10);\n onSave(!val.trim() || isNaN(n) || n < 0 ? null : n);\n };\n\n return (\n <input\n type=\"number\"\n min={0}\n step={1}\n value={local}\n onChange={(e) => {\n const val = e.target.value;\n setLocal(val);\n clearTimeout(timer.current);\n timer.current = setTimeout(() => commit(val), 400);\n }}\n onBlur={(e) => { clearTimeout(timer.current); commit(e.target.value); }}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n className=\"w-full bg-transparent text-xs font-mono tabular-nums text-right focus:outline-none\"\n />\n );\n}\n\n// ── Station table row ─────────────────────────────────────────────────────────\n\nfunction StationRow({\n role,\n metric,\n selected,\n periodHours,\n onClick,\n}: {\n role: RoleDetail;\n metric: StationMetric | undefined;\n selected: boolean;\n periodHours: number;\n onClick: () => void;\n}) {\n const updateRole = useUpdateRole();\n const pending = metric?.pending ?? 0;\n const claimed = metric?.claimed ?? 0;\n const resolved = metric?.resolved ?? 0;\n const priorityCount = metric?.priority_count ?? 0;\n const target = role.target_per_hour ?? null;\n const { pct, color } = loadBar(pending, target);\n const barWidth = pct != null ? Math.min(100, pct) : 0;\n\n const workers = calcWorkers(role.target_per_hour ?? null, role.sla_minutes ?? null, pending, periodHours);\n\n const saveTarget = (n: number | null) => updateRole.mutate({ role: role.role, target_per_hour: n });\n const saveSla = (n: number | null) => updateRole.mutate({ role: role.role, sla_minutes: n });\n\n return (\n <tr\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => { if (e.key === 'Enter') onClick(); }}\n className={`cursor-pointer border-b border-surface-border/30 last:border-b-0 transition-colors ${\n selected ? 'border-l-2 !border-l-accent' : ''\n }`}\n >\n {/* Name — user-set title, or derived from the role id */}\n <td className=\"pl-3 pr-2 py-1.5 overflow-hidden\">\n <div className=\"flex items-center gap-1.5 min-w-0\">\n <span className=\"font-bold text-text-primary truncate text-xs\">\n {displayRoleTitle(role)}\n </span>\n {(role.upstream_roles?.length ?? 0) > 0 && (\n <span title={`Fed by ${role.upstream_roles.join(', ')}`} className=\"shrink-0 leading-none\">\n <GitMerge className=\"w-3 h-3 text-text-quaternary\" />\n </span>\n )}\n </div>\n </td>\n\n {/* Role id */}\n <td className=\"hidden xl:table-cell pr-2 py-1.5 overflow-hidden\">\n <span className=\"block font-mono text-2xs text-text-tertiary truncate\">{role.role}</span>\n </td>\n\n {/* Target/h — editable */}\n <td className=\"px-2 py-1.5\" style={{ backgroundColor: TARGET_BAND }}>\n <EditableNumber value={role.target_per_hour ?? null} onSave={saveTarget} />\n </td>\n\n {/* SLA/m — editable */}\n <td className=\"px-2 py-1.5\" style={{ backgroundColor: SLA_BAND }}>\n <EditableNumber value={role.sla_minutes ?? null} onSave={saveSla} />\n </td>\n\n {/* Workers — calculated (Little's Law + backlog) */}\n <td\n className=\"px-2 py-1.5 text-right\"\n style={{ backgroundColor: WORKERS_BAND }}\n title={workers != null ? `~${workers} concurrent worker${workers === 1 ? '' : 's'} to sustain ${role.target_per_hour}/h within ${role.sla_minutes}m SLA` : undefined}\n >\n <span className={`text-xs font-mono tabular-nums ${workers != null ? '' : 'text-text-quaternary'}`} style={workers != null ? { color: WORKERS_COLOR } : undefined}>\n {workers ?? '—'}\n </span>\n </td>\n\n {/* Pending */}\n <td className=\"px-2 py-1.5 text-right\" style={{ backgroundColor: PENDING_BAND }}>\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}&status=available`}\n className={`text-xs font-mono tabular-nums hover:underline ${\n pending > 0 ? 'text-text-primary font-semibold' : 'text-text-quaternary'\n }`}\n onClick={(e) => e.stopPropagation()}\n >\n {pending}\n </Link>\n </td>\n\n {/* Claimed */}\n <td className=\"px-2 py-1.5 text-right\" style={{ backgroundColor: ACTIVE_BAND }}>\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}&status=claimed`}\n className={`text-xs font-mono tabular-nums hover:underline ${\n claimed > 0 ? 'font-semibold' : 'text-text-quaternary'\n }`}\n style={claimed > 0 ? { color: ACTIVE_COLOR } : undefined}\n onClick={(e) => e.stopPropagation()}\n >\n {claimed}\n </Link>\n </td>\n\n {/* Resolved */}\n <td className=\"px-2 py-1.5 text-right\" style={{ backgroundColor: RESOLVED_BAND }}>\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}&status=resolved`}\n className={`text-xs font-mono tabular-nums hover:underline ${\n resolved > 0 ? 'text-text-secondary' : 'text-text-quaternary'\n }`}\n style={resolved > 0 ? { color: RESOLVED_COLOR } : undefined}\n onClick={(e) => e.stopPropagation()}\n >\n {resolved}\n </Link>\n </td>\n\n {/* P99 wait */}\n <td className=\"hidden xl:table-cell px-2 py-1.5 text-right\">\n <span className=\"text-xs font-mono tabular-nums text-text-secondary\">\n {fmtMin(metric?.wait.p99 ?? null)}\n </span>\n </td>\n\n {/* P99 work */}\n <td className=\"hidden xl:table-cell px-2 py-1.5 text-right\">\n <span className=\"text-xs font-mono tabular-nums text-text-secondary\">\n {fmtMin(metric?.work.p99 ?? null)}\n </span>\n </td>\n\n {/* Load mini-bar */}\n <td className=\"hidden lg:table-cell px-2 py-1.5\">\n <div className=\"flex items-center gap-2\">\n <div className=\"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0\">\n <div className={`h-full rounded-full ${color}`} style={{ width: `${barWidth}%` }} />\n </div>\n <span\n className={`text-2xs font-mono tabular-nums ${\n pct != null && pct > 100\n ? 'text-status-warning font-semibold'\n : 'text-text-quaternary'\n }`}\n >\n {pct != null ? `${pct}%` : '—'}\n </span>\n </div>\n </td>\n\n {/* Actions — eye always anchored first; jeopardy occupies a fixed-width\n slot after it so the eye never shifts when the alert appears. */}\n <td className=\"py-1.5\">\n <div className=\"flex items-center justify-center gap-1.5\">\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}&status=all`}\n className=\"text-text-quaternary hover:text-accent transition-colors\"\n title=\"View all items in queue\"\n onClick={(e) => e.stopPropagation()}\n >\n <Eye className=\"w-3.5 h-3.5\" />\n </Link>\n {/* Fixed-width slot — always occupies space so the eye never shifts */}\n <span className=\"w-3.5 flex items-center justify-center\">\n {priorityCount > 0 && (\n <Link\n to={jeopardyQueueLink(role)}\n className=\"text-status-error hover:opacity-70 transition-opacity\"\n title={`${priorityCount} item${priorityCount === 1 ? '' : 's'} in jeopardy — pull oldest first`}\n onClick={(e) => e.stopPropagation()}\n >\n <TriangleAlert className=\"w-3.5 h-3.5\" strokeWidth={2} />\n </Link>\n )}\n </span>\n </div>\n </td>\n </tr>\n );\n}\n\n// ── Sequence menu ─────────────────────────────────────────────────────────────\n\ninterface SequenceAggregate {\n stations: number;\n pending: number;\n jeopardy: number;\n}\n\n/** Red jeopardy count, capped at 9+ — the same shorthand as the queue pills. */\nfunction JeopardyCount({ n }: { n: number }) {\n if (n <= 0) return null;\n return (\n <span\n className=\"inline-flex items-center gap-0.5 font-semibold tabular-nums\"\n style={{ color: PRIORITY_TEXT_COLOR }}\n title={`${n} in jeopardy`}\n >\n <TriangleAlert className=\"w-3 h-3\" strokeWidth={2.25} />\n {n > 9 ? '9+' : n}\n </span>\n );\n}\n\n/**\n * The segment selector for the 30k-foot view — compact at any segment count.\n * Collapsed, it names the ACTIVE sequence large with its aggregate story\n * (stations · pending · jeopardy) and a caret; expanded, it lists every\n * sequence with the same breakdown so trouble in an unselected segment is\n * visible from here and one click away. Metrics span every visible role, so\n * the numbers are live for all segments, not just the chosen one.\n */\nfunction SequenceMenu({ fragments, aggregates, activeOrigin, onSelect }: {\n fragments: SequenceFragment[];\n aggregates: Map<string, SequenceAggregate>;\n activeOrigin: string | null;\n onSelect: (origin: string) => void;\n}) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false); };\n document.addEventListener('mousedown', onClick);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onClick);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n const active = fragments.find((f) => f.origin.role === activeOrigin) ?? fragments[0];\n if (!active) return null;\n const agg = aggregates.get(active.origin.role);\n\n return (\n <div ref={ref} className=\"relative px-4 pt-2\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"group flex items-baseline gap-2.5\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n >\n <span className=\"text-sm font-medium text-text-primary group-hover:text-accent transition-colors\">\n {displayRoleTitle(active.origin)}\n </span>\n <span className=\"text-2xs font-mono text-text-quaternary tabular-nums\">\n {agg?.stations ?? active.stations.length} station{(agg?.stations ?? active.stations.length) === 1 ? '' : 's'} · {agg?.pending ?? 0} pending\n </span>\n <JeopardyCount n={agg?.jeopardy ?? 0} />\n <ChevronDown\n className={`w-3.5 h-3.5 self-center shrink-0 text-text-tertiary group-hover:text-accent transition-transform ${open ? 'rotate-180' : ''}`}\n strokeWidth={1.5}\n />\n </button>\n\n {open && (\n <div\n role=\"listbox\"\n className=\"absolute z-[100] top-full left-4 mt-1.5 min-w-[20rem] max-h-80 overflow-y-auto bg-surface-raised border border-surface-border rounded-md shadow-lg py-1\"\n >\n {fragments.map((f) => {\n const a = aggregates.get(f.origin.role);\n const isActive = f.origin.role === active.origin.role;\n return (\n <button\n key={f.origin.role}\n type=\"button\"\n role=\"option\"\n aria-selected={isActive}\n onClick={() => { onSelect(f.origin.role); setOpen(false); }}\n className={`w-full text-left px-3 py-1.5 flex items-baseline gap-2.5 transition-colors ${\n isActive ? 'text-accent bg-accent/5' : 'text-text-primary hover:bg-surface-hover'\n }`}\n >\n <span className=\"text-xs font-medium truncate\">{displayRoleTitle(f.origin)}</span>\n <span className=\"ml-auto text-2xs font-mono text-text-quaternary tabular-nums shrink-0\">\n {a?.stations ?? f.stations.length} st · {a?.pending ?? 0} pending\n </span>\n {/* Fixed-width, left-aligned jeopardy column: every ⚠ starts at\n the same x, so the icons stack vertically whether the count\n is 1 or 9+. */}\n <span className=\"w-12 shrink-0 text-left text-2xs\">\n <JeopardyCount n={a?.jeopardy ?? 0} />\n </span>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n// ── Colored column specs — bands shared by header and rows ───────────────────\n// Percentage widths: under `table-fixed` the header row's widths govern every\n// column, so these scale with the window instead of forcing a minimum table\n// width. NAME (widthless) absorbs the remainder.\nconst COLORED_COLS = [\n { label: 'TARGET/H', band: TARGET_BAND, hue: TARGET_COLOR, w: 'w-[8%]' },\n { label: 'SLA/M', band: SLA_BAND, hue: SLA_COLOR, w: 'w-[7%]' },\n { label: 'WORKERS', band: WORKERS_BAND, hue: WORKERS_COLOR, w: 'w-[8%]' },\n { label: 'PENDING', band: PENDING_BAND, hue: QUEUED_COLOR, w: 'w-[8%]' },\n { label: 'CLAIMED', band: ACTIVE_BAND, hue: ACTIVE_COLOR, w: 'w-[8%]' },\n { label: 'RESOLVED', band: RESOLVED_BAND, hue: RESOLVED_COLOR, w: 'w-[8%]' },\n] as const;\n\n/**\n * Little's Law staffing estimate: how many concurrent workers are needed to\n * sustain the target throughput within the SLA, accounting for any current\n * backlog that must be cleared within the selected period.\n *\n * steady-state = target_per_hour × sla_minutes / 60\n * backlog_extra = pending × sla_minutes / (60 × period_hours)\n *\n * Shorter periods with a backlog require more concurrency. The result is\n * rounded up — you can't staff 1.3 workers.\n */\nfunction calcWorkers(\n targetPerHour: number | null,\n slaMinutes: number | null,\n pending: number,\n periodHours: number,\n): number | null {\n if (!targetPerHour || !slaMinutes) return null;\n const steadyState = (targetPerHour * slaMinutes) / 60;\n const backlogExtra = pending > 0 ? (pending * slaMinutes) / (60 * periodHours) : 0;\n return Math.ceil(steadyState + backlogExtra);\n}\n\n// ── Table header ──────────────────────────────────────────────────────────────\n\n// Header cells stick inside the table's own scroll container (the bottom\n// console row). Band cells layer their tint over the opaque surface with a\n// gradient so scrolling rows never show through the sticky header.\nconst TH_BASE =\n 'sticky top-0 z-10 bg-surface border-b border-surface-border py-1.5 text-2xs font-semibold uppercase tracking-wider whitespace-nowrap';\n\nfunction TableHead() {\n return (\n <thead>\n <tr>\n <th className={`${TH_BASE} pl-3 pr-2 text-left text-text-quaternary`}>NAME</th>\n <th className={`${TH_BASE} hidden xl:table-cell w-[12%] pr-2 text-left text-text-quaternary`}>ROLE</th>\n {COLORED_COLS.map((col) => (\n <th\n key={col.label}\n className={`${TH_BASE} ${col.w} px-2 text-right`}\n style={{ color: col.hue, backgroundImage: `linear-gradient(${col.band}, ${col.band})` }}\n >\n {col.label}\n </th>\n ))}\n <th className={`${TH_BASE} hidden xl:table-cell w-[8%] px-2 text-right text-text-quaternary`}>P99 WAIT</th>\n <th className={`${TH_BASE} hidden xl:table-cell w-[8%] px-2 text-right text-text-quaternary`}>P99 WORK</th>\n <th className={`${TH_BASE} hidden lg:table-cell w-[11%] px-2 text-left text-text-quaternary`}>TREND</th>\n <th className={`${TH_BASE} w-14 px-1 text-center text-text-quaternary`}>ACTIONS</th>\n </tr>\n </thead>\n );\n}\n\n\n// ── Page ──────────────────────────────────────────────────────────────────────\n\nexport function OperationsPage() {\n // 1h default: long enough to show a whole simulation shift's shape,\n // short enough that stale history doesn't drown the current run.\n const [period, setPeriod] = useState<Period>('1h');\n const [selectedRole, setSelectedRole] = useState<string | null>(null);\n const [refreshing, setRefreshing] = useState(false);\n // Log default: station volumes span orders of magnitude; log keeps every\n // curve's shape visible instead of flattening the small ones.\n const [logScale, setLogScale] = useState(true);\n\n // Push-driven refresh: every escalation event invalidates ['stationMetrics']\n // through the central event system (debounced, transport-agnostic). The\n // header's refresh button covers user-initiated reloads.\n useStationMetricsEvents();\n\n const { data: roleData, isLoading: rolesLoading, refetch: refetchRoles } = useRoleDetails();\n const {\n data: metricsData,\n isLoading: metricsLoading,\n refetch: refetchMetrics,\n } = useStationMetrics(period);\n\n const roles = roleData?.roles ?? [];\n const metrics = metricsData?.stations ?? [];\n\n // Per-sequence aggregates for the segment menu — computed over ALL visible\n // roles, so unselected segments report their live pending/jeopardy too.\n const fragmentAggregates = useMemo(() => {\n const metricByRole = new Map(metrics.map((m) => [m.role, m]));\n const out = new Map<string, SequenceAggregate>();\n for (const f of buildFragments(roleData?.roles ?? [])) {\n let pending = 0, jeopardy = 0;\n for (const s of f.stations) {\n const m = metricByRole.get(s.role.role);\n pending += m?.pending ?? 0;\n jeopardy += m?.priority_count ?? 0;\n }\n out.set(f.origin.role, { stations: f.stations.length, pending, jeopardy });\n }\n return out;\n }, [metrics, roleData]);\n\n // Sequence fragments, one per parent_role root. The active one is\n // DEEP-LINKED (?fragment=<origin role>) and each switch is a history entry,\n // so a shared link opens the same sequence and back/forward walks them.\n const fragments = useMemo((): SequenceFragment[] => buildFragments(roles), [roles]);\n const [searchParams, setSearchParams] = useSearchParams();\n const fragmentParam = searchParams.get('fragment');\n const activeFragment =\n fragments.find((f) => f.origin.role === fragmentParam) ?? fragments[0] ?? null;\n const selectFragment = useCallback(\n (origin: string) => {\n setSelectedRole(null);\n setSearchParams((prev) => {\n const p = new URLSearchParams(prev);\n p.set('fragment', origin);\n return p;\n });\n },\n [setSearchParams],\n );\n\n const ordered = activeFragment?.stations ?? [];\n const fragmentRoleSet = useMemo(\n () => new Set(ordered.map(({ role }) => role.role)),\n [ordered],\n );\n const fragmentMetrics = useMemo(\n () => metrics.filter((m) => fragmentRoleSet.has(m.role)),\n [metrics, fragmentRoleSet],\n );\n\n const chartStations = useMemo(\n (): ChartStation[] =>\n ordered.map(({ role: r }) => ({\n role: r.role,\n title: r.title,\n parent_role: r.parent_role,\n target_per_hour: r.target_per_hour ?? null,\n upstream_roles: r.upstream_roles ?? [],\n metric: metrics.find((m) => m.role === r.role),\n })),\n [ordered, metrics],\n );\n\n // Jump to the sequence that feeds this station — the merge glyph's click.\n const handleUpstreamSelect = useCallback(\n (upstreamRole: string) => {\n const target = fragments.find((f) =>\n f.stations.some(({ role }) => role.role === upstreamRole),\n );\n if (target) selectFragment(target.origin.role);\n },\n [fragments, selectFragment],\n );\n\n const navigate = useNavigate();\n\n const selectedRoleDetail =\n ordered.find(({ role }) => role.role === selectedRole)?.role ?? null;\n\n const isLoading = rolesLoading || metricsLoading;\n\n const handleRefresh = async () => {\n setRefreshing(true);\n await Promise.all([refetchRoles(), refetchMetrics()]);\n setRefreshing(false);\n };\n\n const handleSelect = (role: string) =>\n setSelectedRole((prev) => (prev === role ? null : role));\n\n const periodSelector = (\n <div className=\"flex items-center gap-0.5\">\n {PERIODS.map((p) => (\n <button\n key={p}\n onClick={() => setPeriod(p)}\n className={`px-2.5 py-1 text-2xs font-mono rounded transition-colors ${\n period === p\n ? 'bg-accent/10 text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n >\n {p}\n </button>\n ))}\n </div>\n );\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0\">\n <PageHeader\n title=\"Pace Board\"\n docsHash=\"#docs:dashboard.md:pace-board\"\n center={periodSelector}\n actions={\n <button\n onClick={handleRefresh}\n disabled={refreshing}\n className=\"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40\"\n >\n <RefreshCw className={`w-3.5 h-3.5 ${refreshing ? 'animate-spin' : ''}`} />\n </button>\n }\n />\n\n {isLoading ? (\n <div className=\"animate-pulse space-y-6 mt-4\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-64 bg-surface-sunken rounded w-full\" />\n <div className=\"space-y-3\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-8 bg-surface-sunken rounded w-full\" />\n ))}\n </div>\n </div>\n ) : fragments.length === 0 ? (\n <div className=\"mt-8\">\n <p className=\"text-sm text-text-secondary mb-2\">Stations appear here once roles are marked visible in Operations.</p>\n <p className=\"text-xs text-text-tertiary\">\n Go to{' '}\n <Link to=\"/admin/roles\" className=\"text-accent hover:underline\">\n Roles\n </Link>{' '}\n and enable <strong>Visible in Operations</strong> on roles that represent pipeline\n stations.\n </p>\n </div>\n ) : (\n /* Console layout: fixed header (above) → chart row (min 40vh) → table row (max 30vh) */\n <div className=\"flex flex-col flex-1 min-h-0\">\n\n {/* Top strip: segment selector (left) + scale toggle (right) */}\n <div className=\"flex items-end justify-between\">\n <div className=\"flex-1\">\n {fragments.length > 1 && (\n <SequenceMenu\n fragments={fragments}\n aggregates={fragmentAggregates}\n activeOrigin={activeFragment?.origin.role ?? null}\n onSelect={selectFragment}\n />\n )}\n </div>\n <div className=\"flex items-center gap-0.5 px-4 pt-2 pb-0.5\">\n {(['lin', 'log'] as const).map((mode) => (\n <button\n key={mode}\n onClick={() => setLogScale(mode === 'log')}\n className={`px-2 py-0.5 text-2xs font-mono rounded transition-colors ${\n (logScale ? 'log' : 'lin') === mode\n ? 'text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n title={mode === 'lin' ? 'Linear Y axis' : 'Logarithmic Y axis — reveals shape across wide value ranges'}\n >\n {mode}\n </button>\n ))}\n </div>\n </div>\n\n {/* Middle row: flexible, never below 40vh — SVG fills left, sidebar fixed-width right */}\n <div className=\"flex-1 min-h-[40vh] flex items-stretch overflow-hidden\">\n {/* SVG chart — scales to fill available space */}\n <div className=\"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden px-2 py-4\">\n <PaceChart\n stations={chartStations}\n selectedRole={selectedRole}\n onSelect={handleSelect}\n onUpstreamSelect={handleUpstreamSelect}\n onCmdClick={(role) => navigate(`/escalations/available?role=${encodeURIComponent(role)}`)}\n periodHours={PERIOD_HOURS[period]}\n logScale={logScale}\n />\n </div>\n {/* Vertical divider */}\n <div className=\"w-px bg-surface-border shrink-0 self-stretch\" />\n {/* Right sidebar — fixed width, scrolls its own content */}\n <StationDetailPanel\n role={selectedRoleDetail}\n allMetrics={fragmentMetrics}\n orderedRoles={ordered.map((o) => o.role)}\n globalPeriod={period}\n onClose={() => setSelectedRole(null)}\n />\n </div>\n\n {/* Bottom row: one real table in one scroll container — the platform\n idiom (see DataTable). `table-fixed w-full` locks columns to the\n container width (no horizontal scroll at any breakpoint); header\n cells stick inside the scrolling region. */}\n <div className=\"max-h-[30vh] flex-none overflow-y-auto border-t border-surface-border\">\n <table className=\"w-full table-fixed\">\n <TableHead />\n <tbody>\n {ordered.map(({ role }) => (\n <StationRow\n key={role.role}\n role={role}\n metric={metrics.find((m) => m.role === role.role)}\n selected={selectedRole === role.role}\n periodHours={PERIOD_HOURS[period]}\n onClick={() => handleSelect(role.role)}\n />\n ))}\n </tbody>\n </table>\n </div>\n\n </div>\n )}\n </div>\n );\n}\n"],"names":["W","H","ML","MR","MT","MB","chartW","chartH","bottom","right","EASE","SOLO_HALF","soloSegment","p","catmullPath","pts","segs","i","p0","p1","p2","p3","cp1x","cp1y","cp2x","cp2y","bandPath","upper","lower","forward","rev","back","backSegs","compact","n","last","a","logTicks","maxVal","v","linearTicks","f","ACTIVE_COLOR","QUEUED_COLOR","RESOLVED_COLOR","TARGET_COLOR","PRIORITY_TEXT_COLOR","withAlpha","color","alpha","MIN_GAP","spreadLabels","labels","maxY","sorted","b","prev","placed","l","labelY","overflow","PaceChart","stations","selectedRole","onSelect","onUpstreamSelect","onCmdClick","periodHours","logScale","fill","hoveredIdx","setHoveredIdx","useState","xOf","xMap","rows","target","expected","_a","_b","_c","_d","hasTargets","r","yScale","logMax","gridTicks","withTarget","targetPts","actualPts","activePts","pendingPts","targetLinePath","actualSplinePath","activeSplinePath","pendingSplinePath","lastActual","lastTarget","lastActive","lastPending","areaPath","hasQueue","workedBandPath","queuedBandPath","showActiveLabel","showQueuedLabel","endLabels","jsxs","gy","jsx","px","cx","tp","ap","ar","hasWaiting","pendingLabelY","row","s","x","cy","isSelected","isHovered","tooltip","tipW","tipX","tipY","e","jeopardyQueueLink","role","field","orderBy","PERIODS","isPeriod","fmt","min","MetricRow","label","value","PeriodSelector","period","onChange","RoleView","globalPeriod","onClose","setPeriod","data","useStationMetrics","metric","slaMinutes","targetPerHour","workerCount","Fragment","displayRoleTitle","X","Link","Settings","List","ExternalLink","OverviewPanel","allMetrics","orderedRoles","totalPending","m","totalResolved","totalPriority","metricByRole","stationsAtRisk","stationsWithLoad","pending","claimed","resolved","priorityCount","hasAlert","hasLoad","StationDetailPanel","SLA_COLOR","WORKERS_COLOR","TARGET_BAND","SLA_BAND","WORKERS_BAND","PENDING_BAND","ACTIVE_BAND","RESOLVED_BAND","PERIOD_HOURS","buildFragments","roles","opsRoles","roleMap","visited","fragments","root","queue","item","c","fmtMin","loadBar","ratio","pct","EditableNumber","onSave","local","setLocal","timer","useRef","commit","val","StationRow","selected","onClick","updateRole","useUpdateRole","barWidth","workers","calcWorkers","saveTarget","saveSla","GitMerge","Eye","TriangleAlert","JeopardyCount","SequenceMenu","aggregates","activeOrigin","open","setOpen","ref","useEffect","onKey","active","agg","ChevronDown","isActive","COLORED_COLS","steadyState","backlogExtra","TH_BASE","TableHead","col","OperationsPage","setSelectedRole","refreshing","setRefreshing","setLogScale","useStationMetricsEvents","roleData","rolesLoading","refetchRoles","useRoleDetails","metricsData","metricsLoading","refetchMetrics","metrics","fragmentAggregates","useMemo","out","jeopardy","searchParams","setSearchParams","useSearchParams","fragmentParam","activeFragment","selectFragment","useCallback","origin","ordered","fragmentRoleSet","fragmentMetrics","chartStations","handleUpstreamSelect","upstreamRole","navigate","useNavigate","selectedRoleDetail","isLoading","handleRefresh","handleSelect","periodSelector","PageHeader","RefreshCw","mode","o"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as E,j as e}from"./vendor-query-B2UbickB.js";import{k as q,O as Q,E as D,H,q as $,r as B,s as G,L as K,K as Y}from"./index-Dh3CVChT.js";import{f as J}from"./useEventHooks-5uI9oI5W.js";import{u as W}from"./useFilterParams-CvRyYHJN.js";import{u as X}from"./useDebouncedValue-BA5S7V54.js";import{b as Z}from"./api-path-u2Jf9QXS.js";import{D as ee}from"./DataTable-BTu2AaEl.js";import{P as te}from"./PageHeader-Bw8RNkqR.js";import{S as se}from"./StickyPagination-DvIF3kQK.js";import{R as ae,a as oe}from"./RowActions-BA9L9djf.js";import{E as re,T as ie,a as ne}from"./escalation-columns-XLEcZRCg.js";import{L as ce}from"./ListToolbar-BFHVOba6.js";import{E as le}from"./CountdownTimer-BF71ldGF.js";import{af as me,ag as ue,P as pe,aj as de}from"./vendor-icons-DN9yGSuM.js";import{a as he,u as ge,f as fe}from"./vendor-react-BpdAFEQc.js";import"./EmptyState-BcsfPq9T.js";import"./FilterBar-BOV8DWIO.js";import"./PriorityBadge-CCERr9vQ.js";import"./RolePill-TySZq7dB.js";import"./WorkflowPill-CcUUVugA.js";import"./StatusBadge-BwUvykDo.js";import"./Modal-CA2Iv02H.js";function Ue(){var C;const L=he(),{user:d}=q(),{filters:a,setFilter:R,pagination:t,sort:h,setSort:k}=W({filters:{role:"",type:"",priority:"",search:""}});J({role:a.role||null,verbs:["claimed","resolved"]});const N=X(a.search,300),z=Q(),{data:g}=D(),{data:f}=H(),i=ge(),{data:n}=$(),P=B(),V=E.useCallback(()=>{var r;const s=window.prompt("Pin label:");if(!(s!=null&&s.trim()))return;const x=((r=n==null?void 0:n.preferences)==null?void 0:r.pinnedViews)??[];P.mutate({pinnedViews:[...x,{id:G(),label:s.trim(),url:i.pathname+i.search,badge:!0}]})},[i.pathname,i.search,n,P]),[F,w]=fe(),y=F.get("view"),T=E.useCallback(s=>{w(x=>{const r=new URLSearchParams(x);return s?r.set("view",s):r.delete("view"),r},{replace:!0})},[w]),c=a.role||null,l=((C=K(c??"",void 0,!!c).data)==null?void 0:C.list_schema)??null,b=!!c&&!!l&&!!l["x-lt-layout"]&&l["x-lt-layout"]!=="table",m=b&&(y?y==="rich":!0),v={assigned_to:d==null?void 0:d.userId,status:"pending",role:a.role||void 0,type:a.type||void 0,priority:a.priority?parseInt(a.priority):void 0,search:N||void 0,sort_by:h.sort_by||"created_at",order:h.order||"desc"},{data:o,isLoading:_,error:u,refetch:A,isFetching:O}=Y({...v,limit:t.pageSize,offset:t.offset}),S=(o==null?void 0:o.escalations)??[],p=(o==null?void 0:o.total)??0,I=Z("/escalations",{...v,limit:t.pageSize,offset:t.offset}),M={key:"actions",label:"",render:s=>e.jsx(ae,{children:e.jsx(oe,{icon:de,title:"Release escalation",onClick:()=>z.mutate(s.id),alwaysVisible:!0,colorClass:"text-status-warning/40 hover:text-status-warning"})}),className:"w-16 text-right"},U=[ie,...ne,M],j=s=>L(`/escalations/detail/${s.id}`,{state:{from:"/escalations/queue"}});return e.jsxs("div",{children:[e.jsx(te,{title:"My Escalations"}),e.jsx(re,{filters:a,setFilter:R,roles:(g==null?void 0:g.roles)??[],types:(f==null?void 0:f.types)??[],actions:e.jsxs(e.Fragment,{children:[e.jsx(ce,{onRefresh:()=>A(),isFetching:O,apiPath:I}),b&&e.jsx("button",{onClick:()=>T(m?"table":"rich"),className:"ml-2 inline-flex h-7 w-7 items-center justify-center rounded text-text-tertiary hover:bg-surface-hover hover:text-text-primary transition-colors",title:m?"Table view":"Rich view",children:m?e.jsx(me,{className:"w-4 h-4"}):e.jsx(ue,{className:"w-4 h-4"})}),e.jsx("button",{onClick:V,className:"ml-2 inline-flex h-7 w-7 items-center justify-center rounded text-text-tertiary hover:bg-surface-hover hover:text-accent transition-colors",title:"Pin this view — save the current filters to your Pinned section","data-testid":"pin-current-view",children:e.jsx(pe,{className:"w-4 h-4"})})]})}),u&&e.jsx("div",{className:"mb-4 px-4 py-3 rounded-md bg-status-error/10 border border-status-error/20 text-xs text-status-error",children:u.message==="Session expired"?"Your session has expired. Please log in again.":`Failed to load escalations: ${u.message}`}),m?e.jsx(le,{role:c,listSchema:l,activeEscalations:S,onRowClick:j,forceViewAction:!0,total:p,page:t.page,totalPages:t.totalPages(p),pageSize:t.pageSize,onPageChange:t.setPage,onPageSizeChange:t.setPageSize}):e.jsxs(e.Fragment,{children:[e.jsx(ee,{columns:U,data:S,layout:"fixed",keyFn:s=>s.id,onRowClick:j,isLoading:_,emptyMessage:u?"Unable to load data":"No assigned escalations",sort:h,onSort:k}),e.jsx(se,{page:t.page,totalPages:t.totalPages(p),onPageChange:t.setPage,total:p,pageSize:t.pageSize,onPageSizeChange:t.setPageSize})]})]})}export{Ue as OperatorDashboard};
|
|
2
|
-
//# sourceMappingURL=OperatorDashboard-Dhs2eo8p.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OperatorDashboard-Dhs2eo8p.js","sources":["../../src/pages/operator/OperatorDashboard.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { useNavigate, useLocation, useSearchParams } from 'react-router-dom';\nimport { LockOpen, Pin, LayoutList, Table } from 'lucide-react';\nimport { useAuth } from '../../hooks/useAuth';\nimport { useEscalations, useEscalationTypes, useReleaseEscalation } from '../../api/escalations';\nimport { useEscalationListEvents } from '../../hooks/useEventHooks';\nimport { useRoles, useRoleListSchema } from '../../api/roles';\nimport { useFilterParams } from '../../hooks/useFilterParams';\nimport { useDebouncedValue } from '../../hooks/useDebouncedValue';\nimport { buildApiPath } from '../../lib/api-path';\nimport { DataTable, type Column } from '../../components/common/data/DataTable';\nimport { PageHeader } from '../../components/common/layout/PageHeader';\nimport { StickyPagination } from '../../components/common/data/StickyPagination';\nimport { RowAction, RowActionGroup } from '../../components/common/layout/RowActions';\nimport { ESCALATION_COLUMNS, TIME_LEFT_COLUMN, EscalationFilterBar } from './escalation-columns';\nimport { ListToolbar } from '../../components/common/data/ListToolbar';\nimport { EscalationListView } from '../../components/escalation/EscalationListView';\nimport { usePatchPreferences, usePreferences } from '../../api/preferences';\nimport { newPinId } from '../../lib/pinned-views';\nimport type { LTEscalationRecord } from '../../api/types';\n\nexport function OperatorDashboard() {\n const navigate = useNavigate();\n const { user } = useAuth();\n const { filters, setFilter, pagination, sort, setSort } = useFilterParams({\n filters: { role: '', type: '', priority: '', search: '' },\n });\n // The personal inbox moves on claims arriving and work leaving — claimed\n // and resolved. A role filter narrows the subscription to that queue's\n // subject token; nothing else in the escalation space reaches this page.\n useEscalationListEvents({ role: filters.role || null, verbs: ['claimed', 'resolved'] });\n // Debounce so server-side search fires once the user pauses, not per keystroke.\n const debouncedSearch = useDebouncedValue(filters.search, 300);\n const release = useReleaseEscalation();\n const { data: rolesData } = useRoles();\n const { data: typesData } = useEscalationTypes();\n\n // Pin current view — the same gesture as All Escalations: every filter is\n // already deep-linked in the URL, so a pin is just the labeled URL.\n const location = useLocation();\n const { data: prefsData } = usePreferences();\n const patchPrefs = usePatchPreferences();\n const pinCurrentView = useCallback(() => {\n const label = window.prompt('Pin label:');\n if (!label?.trim()) return;\n const existing = prefsData?.preferences?.pinnedViews ?? [];\n patchPrefs.mutate({\n pinnedViews: [\n ...existing,\n { id: newPinId(), label: label.trim(), url: location.pathname + location.search, badge: true },\n ],\n });\n }, [location.pathname, location.search, prefsData, patchPrefs]);\n\n // Role-owned rich view — the same resolution the queue page runs, applied to\n // the personal inbox when one role is filtered. Deep-linked (?view=rich|table)\n // so a pinned link reproduces the presentation; absent, rich wins when the\n // role owns a non-table list_schema. Row actions render in view mode: every\n // row here is already held by the viewer, so claim gestures don't apply.\n const [searchParams, setSearchParams] = useSearchParams();\n const viewParam = searchParams.get('view');\n const setViewParam = useCallback((v: 'table' | 'rich' | null) => {\n setSearchParams((prev) => {\n const p = new URLSearchParams(prev);\n if (v) p.set('view', v); else p.delete('view');\n return p;\n }, { replace: true });\n }, [setSearchParams]);\n const singleRole = filters.role || null;\n const listSchemaQuery = useRoleListSchema(singleRole ?? '', undefined, !!singleRole);\n const listSchema = (listSchemaQuery.data?.list_schema ?? null) as Record<string, any> | null;\n const hasRichView = !!singleRole && !!listSchema\n && !!listSchema['x-lt-layout'] && listSchema['x-lt-layout'] !== 'table';\n const useRichView = hasRichView && (viewParam ? viewParam === 'rich' : true);\n\n const escalationQuery = {\n assigned_to: user?.userId,\n status: 'pending',\n role: filters.role || undefined,\n type: filters.type || undefined,\n priority: filters.priority ? parseInt(filters.priority) : undefined,\n search: debouncedSearch || undefined,\n sort_by: sort.sort_by || 'created_at',\n order: sort.order || 'desc',\n };\n\n const { data, isLoading, error: queryError, refetch, isFetching } = useEscalations({\n ...escalationQuery,\n limit: pagination.pageSize,\n offset: pagination.offset,\n });\n\n // Search is server-side (full result set) — results and total come straight\n // from the query, no client-side filtering of the current page.\n const activeClaims = data?.escalations ?? [];\n const total = data?.total ?? 0;\n\n // Copy-URL/curl path built from the SAME params the query sends.\n const apiPath = buildApiPath('/escalations', {\n ...escalationQuery,\n limit: pagination.pageSize,\n offset: pagination.offset,\n });\n\n const releaseColumn: Column<LTEscalationRecord> = {\n key: 'actions',\n label: '',\n render: (row) => (\n <RowActionGroup>\n <RowAction\n icon={LockOpen}\n title=\"Release escalation\"\n onClick={() => release.mutate(row.id)}\n alwaysVisible\n colorClass=\"text-status-warning/40 hover:text-status-warning\"\n />\n </RowActionGroup>\n ),\n className: 'w-16 text-right',\n };\n\n // Time-left first (aligns with checkbox on All Escalations), then summary + base columns + actions\n const columns: Column<LTEscalationRecord>[] = [\n TIME_LEFT_COLUMN,\n ...ESCALATION_COLUMNS,\n releaseColumn,\n ];\n\n const openDetail = (row: LTEscalationRecord) =>\n navigate(`/escalations/detail/${row.id}`, { state: { from: '/escalations/queue' } });\n\n return (\n <div>\n <PageHeader title=\"My Escalations\" />\n\n <EscalationFilterBar\n filters={filters}\n setFilter={setFilter}\n roles={rolesData?.roles ?? []}\n types={typesData?.types ?? []}\n actions={\n <>\n <ListToolbar\n onRefresh={() => refetch()}\n isFetching={isFetching}\n apiPath={apiPath}\n />\n {hasRichView && (\n <button\n onClick={() => setViewParam(useRichView ? 'table' : 'rich')}\n className=\"ml-2 inline-flex h-7 w-7 items-center justify-center rounded text-text-tertiary hover:bg-surface-hover hover:text-text-primary transition-colors\"\n title={useRichView ? 'Table view' : 'Rich view'}\n >\n {useRichView\n ? <Table className=\"w-4 h-4\" />\n : <LayoutList className=\"w-4 h-4\" />}\n </button>\n )}\n <button\n onClick={pinCurrentView}\n className=\"ml-2 inline-flex h-7 w-7 items-center justify-center rounded text-text-tertiary hover:bg-surface-hover hover:text-accent transition-colors\"\n title=\"Pin this view — save the current filters to your Pinned section\"\n data-testid=\"pin-current-view\"\n >\n <Pin className=\"w-4 h-4\" />\n </button>\n </>\n }\n />\n\n {queryError && (\n <div className=\"mb-4 px-4 py-3 rounded-md bg-status-error/10 border border-status-error/20 text-xs text-status-error\">\n {(queryError as Error).message === 'Session expired'\n ? 'Your session has expired. Please log in again.'\n : `Failed to load escalations: ${(queryError as Error).message}`}\n </div>\n )}\n\n {useRichView ? (\n <EscalationListView\n role={singleRole!}\n listSchema={listSchema!}\n activeEscalations={activeClaims}\n onRowClick={openDetail}\n forceViewAction\n total={total}\n page={pagination.page}\n totalPages={pagination.totalPages(total)}\n pageSize={pagination.pageSize}\n onPageChange={pagination.setPage}\n onPageSizeChange={pagination.setPageSize}\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n data={activeClaims}\n layout=\"fixed\"\n keyFn={(row) => row.id}\n onRowClick={openDetail}\n isLoading={isLoading}\n emptyMessage={queryError ? 'Unable to load data' : 'No assigned escalations'}\n sort={sort}\n onSort={setSort}\n />\n\n <StickyPagination\n page={pagination.page}\n totalPages={pagination.totalPages(total)}\n onPageChange={pagination.setPage}\n total={total}\n pageSize={pagination.pageSize}\n onPageSizeChange={pagination.setPageSize}\n />\n </>\n )}\n </div>\n );\n}\n"],"names":["OperatorDashboard","navigate","useNavigate","user","useAuth","filters","setFilter","pagination","sort","setSort","useFilterParams","useEscalationListEvents","debouncedSearch","useDebouncedValue","release","useReleaseEscalation","rolesData","useRoles","typesData","useEscalationTypes","location","useLocation","prefsData","usePreferences","patchPrefs","usePatchPreferences","pinCurrentView","useCallback","label","existing","_a","newPinId","searchParams","setSearchParams","useSearchParams","viewParam","setViewParam","v","prev","p","singleRole","listSchema","useRoleListSchema","hasRichView","useRichView","escalationQuery","data","isLoading","queryError","refetch","isFetching","useEscalations","activeClaims","total","apiPath","buildApiPath","releaseColumn","row","jsx","RowActionGroup","RowAction","LockOpen","columns","TIME_LEFT_COLUMN","ESCALATION_COLUMNS","openDetail","PageHeader","EscalationFilterBar","jsxs","Fragment","ListToolbar","Table","LayoutList","Pin","EscalationListView","DataTable","StickyPagination"],"mappings":"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"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{j as e,a as h}from"./vendor-query-B2UbickB.js";import{G as X,a4 as O,a5 as ee,J as te,a3 as se,a6 as ae,a7 as ne}from"./index-Dh3CVChT.js";import{C as re}from"./ConfirmDeleteModal-DD7SEMeX.js";import{A as ie}from"./AutoGrowTextarea-y-oNY51_.js";import{u as le}from"./users-AO8lUyNr.js";import{I as F,B as oe,H as ce,a2 as de,T as me,aJ as xe,L as ue,N as he,m as pe,P as fe,n as I,ag as ve}from"./vendor-icons-DN9yGSuM.js";import{e as be,a as ge,L as J}from"./vendor-react-BpdAFEQc.js";import"./Modal-CA2Iv02H.js";function je({role:s}){const{data:i}=le({role:s,limit:100}),r=(i==null?void 0:i.users)??[];return r.length===0?e.jsx("p",{className:"text-2xs text-text-tertiary",children:"Assign users to this role to staff its queue. Members see and resolve escalations here according to their read/write scope; admins manage the role itself."}):e.jsx("div",{className:"divide-y divide-surface-border/40",children:r.map(l=>{const d=l.roles.find(c=>c.role===s);return d?e.jsxs("div",{className:"flex items-center justify-between py-1.5 gap-2",children:[e.jsx("span",{className:"text-xs text-text-secondary truncate",children:l.display_name||l.external_id}),e.jsx("span",{className:"text-2xs text-text-quaternary font-mono shrink-0",children:_e(d)})]},l.id):null})})}function _e(s){return s.type==="superadmin"||s.type==="admin"?s.type:`member · read ${s.read_scope} · write ${s.write_scope}`}function M(s){return s==null?"":JSON.stringify(s,null,2)}function E(s){const i=s.trim();if(!i)return{ok:!0,value:void 0};try{const r=JSON.parse(i);return typeof r!="object"||Array.isArray(r)||r===null?{ok:!1}:{ok:!0,value:r}}catch{return{ok:!1}}}function g({icon:s,label:i,annotation:r,aside:l,accent:d=!1,children:c}){const u=d?"text-accent":"text-text-tertiary";return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-5",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx(s,{className:`w-3 h-3 ${u} shrink-0`,strokeWidth:1.5}),e.jsx("span",{className:`text-2xs font-semibold uppercase tracking-widest ${u}`,children:i}),r&&e.jsxs("span",{className:"text-2xs text-text-quaternary truncate",children:["— ",r]})]}),l]}),e.jsx("div",{className:`border-l-2 pl-5 ${d?"border-accent/15":"border-surface-border/60"}`,children:c})]})}function L(){return e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full dot-ring bg-status-success"}),e.jsx("span",{className:"text-2xs font-semibold uppercase tracking-widest text-status-success",children:"Live"})]})}function Ne({role:s,allRoles:i}){const{data:r}=se(),l=ae(),d=ne(),[c,u]=h.useState(""),a=(r==null?void 0:r.chains)??[],t=h.useMemo(()=>a.filter(o=>o.source_role===s.role).map(o=>o.target_role),[a,s.role]),f=h.useMemo(()=>i.map(o=>o.role).filter(o=>o!==s.role&&o!=="superadmin"&&!t.includes(o)),[i,s.role,t]);return s.role==="superadmin"?e.jsx("p",{className:"text-2xs text-text-tertiary",children:"Superadmins can escalate to any role implicitly."}):e.jsxs("div",{className:"space-y-2",children:[f.length>0&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("select",{value:c,onChange:o=>u(o.target.value),className:"select text-xs font-mono flex-1",children:[e.jsx("option",{value:"",children:"Add target…"}),f.map(o=>e.jsx("option",{value:o,children:o},o))]}),e.jsx("button",{onClick:()=>{c&&l.mutate({source_role:s.role,target_role:c},{onSuccess:()=>u("")})},disabled:!c||l.isPending,className:"px-2.5 py-1 text-xs rounded bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0",children:l.isPending?"…":"Add"})]}),e.jsx(W,{items:t,empty:"Roles this one can escalate to. Add a target above and it lands here.",onRemove:o=>d.mutate({source_role:s.role,target_role:o})})]})}function W({items:s,empty:i,onRemove:r}){return s.length===0?e.jsx("p",{className:"text-2xs text-text-tertiary leading-relaxed",children:i}):e.jsx("div",{className:"flex flex-wrap gap-1.5",children:s.map(l=>e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-0.5 text-2xs bg-accent/10 rounded-sm font-mono text-accent",children:[l,e.jsx("button",{onClick:()=>r(l),className:"text-accent/50 hover:text-status-error transition-colors leading-none ml-0.5",children:"×"})]},l))})}function ye({role:s,allRoles:i}){const r=O(),[l,d]=h.useState(""),c=s.upstream_roles??[],u=h.useMemo(()=>i.map(t=>t.role).filter(t=>t!==s.role&&t!=="superadmin"&&!c.includes(t)),[i,s.role,c]),a=t=>r.mutate({role:s.role,upstream_roles:t},{onSuccess:()=>d("")});return e.jsxs("div",{className:"space-y-2",children:[u.length>0&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("select",{value:l,onChange:t=>d(t.target.value),className:"select text-xs font-mono flex-1",children:[e.jsx("option",{value:"",children:"Add upstream input…"}),u.map(t=>e.jsx("option",{value:t,children:t},t))]}),e.jsx("button",{onClick:()=>{l&&a([...c,l])},disabled:!l||r.isPending,className:"px-2.5 py-1 text-xs rounded bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0",children:r.isPending?"…":"Add"})]}),e.jsx(W,{items:c,empty:"Roles from other sequences this station draws input from. Add one above and it lands here.",onRemove:t=>a(c.filter(f=>f!==t))}),r.error&&e.jsx("p",{className:"text-2xs text-status-error",children:r.error.message})]})}function we({role:s}){const i=O(),[r,l]=h.useState(""),[d,c]=h.useState(""),[u,a]=h.useState(!0),t=s.default_pins??[],f=m=>i.mutate({role:s.role,default_pins:m.length?m:null},{onSuccess:()=>{l(""),c(""),a(!0)}}),o=r.trim()!==""&&d.trim().startsWith("/");return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"text",value:r,onChange:m=>l(m.target.value),placeholder:"Label — e.g. Needs harvesting",className:"input text-xs w-40 shrink-0"}),e.jsx("input",{type:"text",value:d,onChange:m=>c(m.target.value),placeholder:"/escalations/available?role=…",className:"input text-xs font-mono flex-1"}),e.jsxs("label",{className:"flex items-center gap-1 text-2xs text-text-tertiary shrink-0",title:"Show a live count beside the label",children:[e.jsx("input",{type:"checkbox",checked:u,onChange:m=>a(m.target.checked),className:"w-3 h-3 accent-accent"}),"count"]}),e.jsx("button",{onClick:()=>{o&&f([...t,{label:r.trim(),url:d.trim(),badge:u}])},disabled:!o||i.isPending,className:"px-2.5 py-1 text-xs rounded bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed shrink-0",children:i.isPending?"…":"Add"})]}),t.length===0?e.jsx("p",{className:"text-2xs text-text-tertiary leading-relaxed",children:"Pinned views every member starts with — the persona's exact queries. Add one above and it lands here."}):e.jsx("div",{className:"space-y-1",children:t.map(m=>e.jsxs("div",{className:"flex items-baseline gap-2 text-2xs",children:[e.jsx("span",{className:"font-medium text-text-secondary shrink-0",children:m.label}),m.badge&&e.jsx("span",{className:"text-2xs text-accent shrink-0",title:"Shows a live count",children:"count"}),e.jsx("span",{className:"font-mono text-text-quaternary truncate flex-1",title:m.url,children:m.url}),e.jsx("button",{onClick:()=>f(t.filter(C=>C.label!==m.label)),className:"text-text-quaternary hover:text-status-error transition-colors leading-none shrink-0","aria-label":`Remove pin ${m.label}`,children:"×"})]},m.label))}),i.error&&e.jsx("p",{className:"text-2xs text-status-error",children:i.error.message})]})}function ke(s){return{title:s.title??"",description:s.description??"",ops_visible:s.ops_visible,enforce_schema:s.enforce_schema,parent_role:s.parent_role??"",metadata_schema:M(s.metadata_schema),properties:M(s.properties)||"{}",sla_minutes:s.sla_minutes!=null?String(s.sla_minutes):"",target_per_hour:s.target_per_hour!=null?String(s.target_per_hour):"",worker_count:s.worker_count!=null?String(s.worker_count):"",priority_threshold_minutes:s.priority_threshold_minutes!=null?String(s.priority_threshold_minutes):"",priority_facet:s.priority_facet??""}}const Se=/^[a-zA-Z0-9_]+$/;function Le(){const{role:s}=be(),i=ge(),{data:r,isLoading:l}=X(),d=O(),c=ee(),u=(r==null?void 0:r.roles)??[],a=u.find(n=>n.role===s),[t,f]=h.useState({title:"",description:"",ops_visible:!1,enforce_schema:!1,parent_role:"",metadata_schema:"",properties:"{}",sla_minutes:"",target_per_hour:"",worker_count:"",priority_threshold_minutes:"",priority_facet:""}),[o,m]=h.useState(!1),[C,y]=h.useState(!1),[_,P]=h.useState({}),[V,T]=h.useState(!1),[U,A]=h.useState(new Set),G=n=>A(x=>new Set([...x,n]));h.useEffect(()=>{a&&(f(ke(a)),m(!1),y(!1),P({}),A(new Set))},[s,a==null?void 0:a.role]);const v=n=>{f(x=>({...x,...n})),m(!0),y(!1)},z=h.useMemo(()=>u.filter(n=>{if(!a||n.role===a.role)return!1;let x=n;const p=new Set;for(;x!=null&&x.parent_role;){if(x.parent_role===a.role)return!1;if(p.has(x.parent_role))break;p.add(x.parent_role),x=u.find(b=>b.role===x.parent_role)}return!0}),[u,a]),K=()=>{const n=E(t.metadata_schema),x=E(t.properties),p=t.priority_facet.trim(),b={};if(n.ok||(b.metadata_schema="Invalid JSON"),x.ok||(b.properties="Invalid JSON"),p&&!Se.test(p)&&(b.priority_facet="Letters, numbers, underscores only"),P(b),Object.keys(b).length||!a)return;const j=D=>{const N=parseFloat(D);return isNaN(N)?null:N},Q=D=>{const N=j(D);return N==null?null:Math.round(N)};d.mutate({role:a.role,title:t.title.trim()||null,description:t.description.trim()||null,ops_visible:t.ops_visible,enforce_schema:t.enforce_schema,parent_role:t.parent_role||null,metadata_schema:n.value??null,properties:x.value??{},sla_minutes:j(t.sla_minutes),target_per_hour:j(t.target_per_hour),worker_count:Q(t.worker_count),priority_threshold_minutes:j(t.priority_threshold_minutes),priority_facet:p||null},{onSuccess:()=>{m(!1),y(!0),A(new Set),window.setTimeout(()=>y(!1),2500)}})},H=()=>{a&&c.mutate(a.role,{onSuccess:()=>i("/admin/roles")})};if(l)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-48"}),e.jsx("div",{className:"h-4 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded"})]});if(!a)return e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(F,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsx("h2",{className:"heading-3 mb-2",children:"Role not found"})]});const Y=a.user_count>0||a.chain_count>0||a.workflow_count>0,Z=o&&!Object.values(_).some(Boolean),w=parseFloat(t.sla_minutes),k=parseFloat(t.target_per_hour),S=parseFloat(t.worker_count),R=!isNaN(w)&&w>0,$=!isNaN(k)&&k>0,q=!isNaN(S)&&S>0,B=R&&q&&!$?`→ target ≈ ${(S/(w/60)).toFixed(1)}/h`:R&&$&&!q?`→ workers ≈ ${(k*(w/60)).toFixed(1)}`:$&&q&&!R?`→ SLA ≈ ${(S/k*60).toFixed(0)}m`:null;return e.jsxs("div",{children:[e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-center mb-12",children:[e.jsxs("div",{className:"flex items-center justify-between gap-4 min-w-0",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-3 mb-1",children:[e.jsx(F,{className:"w-5 h-5 text-accent",strokeWidth:1.5}),e.jsx("h1",{className:"text-lg font-mono font-medium text-text-primary",children:a.role}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:dashboard.md:role-detail"},className:"text-text-quaternary hover:text-accent transition-colors",title:"Open docs for this page",children:e.jsx(oe,{className:"w-4 h-4",strokeWidth:1.5})}),a.parent_role&&e.jsxs("span",{className:"flex items-center gap-1 text-2xs text-text-quaternary font-mono",children:[e.jsx(ce,{className:"w-3 h-3"})," ",a.parent_role]})]}),a.title&&e.jsx("p",{className:"text-sm text-text-secondary pl-8",children:a.title})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",title:"Show as a station on the Pace Board",children:[e.jsx("span",{className:"text-2xs font-semibold uppercase tracking-widest text-text-tertiary",children:"Pace Board"}),e.jsx("button",{onClick:()=>v({ops_visible:!t.ops_visible}),className:`w-9 h-5 rounded-full transition-colors relative shrink-0 ${t.ops_visible?"bg-accent":"bg-surface-border"}`,children:e.jsx("span",{className:`absolute top-[3px] left-0 w-3.5 h-3.5 rounded-full bg-text-inverse transition-transform shadow ${t.ops_visible?"translate-x-[18px]":"translate-x-[3px]"}`})})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",title:"Validate every resolve payload against this role's form schema — API, MCP, and CLI submissions are rejected with field-level errors when they violate it",children:[e.jsx("span",{className:"text-2xs font-semibold uppercase tracking-widest text-text-tertiary",children:"Enforce Schema"}),e.jsx("button",{onClick:()=>v({enforce_schema:!t.enforce_schema}),className:`w-9 h-5 rounded-full transition-colors relative shrink-0 ${t.enforce_schema?"bg-accent":"bg-surface-border"}`,children:e.jsx("span",{className:`absolute top-[3px] left-0 w-3.5 h-3.5 rounded-full bg-text-inverse transition-transform shadow ${t.enforce_schema?"translate-x-[18px]":"translate-x-[3px]"}`})})]})]}),e.jsxs("div",{className:"flex items-end justify-center gap-4 bg-surface-sunken rounded-lg px-4 py-2.5 w-full",title:"throughput = workers / (sla / 60)",children:[[{key:"sla_minutes",label:"SLA",unit:"min",placeholder:"30"},{key:"target_per_hour",label:"Target",unit:"/h",placeholder:"20"},{key:"worker_count",label:"Workers",unit:"",placeholder:"4"}].map(({key:n,label:x,unit:p,placeholder:b})=>e.jsxs("div",{children:[e.jsxs("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-quaternary mb-1",children:[x,p&&e.jsx("span",{className:"normal-case font-normal ml-1",children:p})]}),e.jsx("input",{type:"number",min:"0",step:"any",value:t[n],onChange:j=>v({[n]:j.target.value}),placeholder:b,className:"input text-xs w-16 font-mono"})]},n)),B&&e.jsx("span",{className:"text-2xs text-accent font-mono whitespace-nowrap pb-2",children:B})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4 min-w-0",children:[e.jsxs("div",{className:"flex items-end gap-4 bg-surface-sunken rounded-lg px-4 py-2.5 shrink-0",title:"Pending unclaimed items older than the threshold count as priority on the Pace Board. Age is measured from the metadata facet (an ISO 8601 UTC timestamp, e.g. authorized_at) or from created_at when the facet is blank; the threshold falls back to SLA when blank.",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-quaternary mb-1",children:["Priority",e.jsx("span",{className:"normal-case font-normal ml-1",children:"min"})]}),e.jsx("input",{type:"number",min:"0",step:"any",value:t.priority_threshold_minutes,onChange:n=>v({priority_threshold_minutes:n.target.value}),placeholder:t.sla_minutes||"60",className:"input text-xs w-16 font-mono"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-quaternary mb-1",children:"Facet"}),e.jsx("input",{type:"text",value:t.priority_facet,onChange:n=>v({priority_facet:n.target.value}),placeholder:"created_at",className:"input text-xs w-28 font-mono"}),_.priority_facet&&e.jsx("p",{className:"text-2xs text-status-error mt-0.5",children:_.priority_facet})]})]}),e.jsxs("div",{className:"flex items-center gap-3 shrink-0",children:[C&&e.jsxs("span",{className:"flex items-center gap-1 text-xs text-status-success animate-page-enter",children:[e.jsx(de,{className:"w-3 h-3"})," Saved"]}),d.error&&e.jsx("span",{className:"text-xs text-status-error max-w-[180px] truncate",children:d.error.message}),e.jsx("button",{onClick:K,disabled:!Z||d.isPending,className:"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:d.isPending?"Saving…":"Save"}),!Y&&e.jsxs("button",{onClick:()=>T(!0),className:"px-3 py-1.5 text-xs rounded-md text-status-error/60 hover:text-status-error hover:bg-status-error/10 transition-colors flex items-center gap-1",children:[e.jsx(me,{className:"w-3 h-3"})," Delete"]})]})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-start",children:[e.jsxs("div",{className:"space-y-14",children:[e.jsx(g,{icon:xe,label:"Identity",annotation:"name and description",accent:!0,children:e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Display Name"}),e.jsx("input",{type:"text",value:t.title,onChange:n=>v({title:n.target.value}),placeholder:`e.g., ${a.role.charAt(0).toUpperCase()+a.role.slice(1)}`,className:"input text-sm w-full"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Description"}),e.jsx(ie,{value:t.description,onChange:n=>v({description:n}),placeholder:"A short description shown on role cards and in the operations view.",rows:2})]})]})}),e.jsx("div",{className:`overflow-hidden transition-all duration-300 ease-out ${t.ops_visible?"opacity-100 max-h-[900px]":"opacity-0 max-h-0 pointer-events-none"}`,"aria-hidden":!t.ops_visible,children:e.jsx(g,{icon:ue,label:"Pace Board",annotation:"where this station sits on the floor",accent:!0,children:e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Prior Step"}),e.jsxs("select",{value:t.parent_role,onChange:n=>v({parent_role:n.target.value}),className:"select text-sm w-full font-mono",children:[e.jsx("option",{value:"",children:"None — root process"}),z.map(n=>e.jsx("option",{value:n.role,children:n.title?`${n.role} — ${n.title}`:n.role},n.role))]}),e.jsx("p",{className:"text-2xs text-text-tertiary leading-relaxed mt-1.5",children:"Places this role in one Pace Board sequence. A role with no prior step starts its own sequence."})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary",children:"Upstream Inputs"}),e.jsx(L,{})]}),e.jsx(ye,{role:a,allRoles:u})]})]})})})]}),e.jsxs("div",{className:"space-y-14",children:[e.jsx(g,{icon:he,label:"Escalation Targets",annotation:"hand-off routing",aside:e.jsx(L,{}),children:e.jsx(Ne,{role:a,allRoles:u})}),e.jsx(g,{icon:pe,label:"Members",annotation:"read = what appears · write = what they can act on",children:e.jsx(je,{role:a.role})}),e.jsx(g,{icon:fe,label:"Default Pins",annotation:"pinned views every member starts with",aside:e.jsx(L,{}),children:e.jsx(we,{role:a})})]}),e.jsxs("div",{className:"space-y-14",children:[e.jsxs(g,{icon:I,label:"Escalation Schema",annotation:"the resolve form",accent:!0,aside:a.current_schema_version!=null?e.jsxs("span",{className:"text-2xs font-mono text-text-quaternary",children:["v",a.current_schema_version," in use"]}):void 0,children:[e.jsxs("p",{className:"text-2xs text-text-tertiary mb-3 leading-relaxed",children:["The form a person completes to resolve this role's escalations. Versioned — each save adds one; workflows pin any version via"," ",e.jsx("code",{className:"font-mono",children:"schemaVersion"}),"."]}),e.jsx(J,{to:`/admin/roles/${encodeURIComponent(a.role)}/schema`,className:"text-xs text-accent hover:underline",children:a.form_schema?`Open schema editor — v${a.current_schema_version??1} in use →`:"Define the escalation form →"})]}),e.jsxs(g,{icon:ve,label:"Escalations List Schema",annotation:"rich list view",aside:a.current_list_schema_version!=null?e.jsxs("span",{className:"text-2xs font-mono text-text-quaternary",children:["v",a.current_list_schema_version," in use"]}):void 0,children:[e.jsx("p",{className:"text-2xs text-text-tertiary mb-3 leading-relaxed",children:"Richly formats the escalation list when it's scoped to just this role. Versioned on its own timeline — separate from the resolve form."}),e.jsx(J,{to:`/admin/roles/${encodeURIComponent(a.role)}/list-schema`,className:"text-xs text-accent hover:underline",children:a.list_schema?`Open list schema editor — v${a.current_list_schema_version??1} in use →`:"Define the list view →"})]}),e.jsxs(g,{icon:I,label:"Metadata Schema",annotation:"validates metadata at creation",aside:!U.has("metadata_schema")&&a.metadata_schema?e.jsx("button",{onClick:()=>G("metadata_schema"),className:"text-2xs text-accent hover:underline",children:"Edit"}):void 0,children:[e.jsxs("p",{className:"text-2xs text-text-tertiary mb-3 leading-relaxed",children:["Validates ",e.jsx("code",{className:"font-mono",children:"metadata"})," at creation time. Keys appear in faceted search autocomplete."]}),!U.has("metadata_schema")&&a.metadata_schema?e.jsx(te,{data:a.metadata_schema}):e.jsxs(e.Fragment,{children:[e.jsx("textarea",{value:t.metadata_schema,onChange:n=>{const x=n.target.value;v({metadata_schema:x}),P(p=>({...p,metadata_schema:E(x).ok?void 0:"Invalid JSON"}))},rows:10,spellCheck:!1,className:"input text-xs font-mono w-full resize-y",placeholder:`{
|
|
2
|
-
"type": "object",
|
|
3
|
-
"properties": {
|
|
4
|
-
"order_id": { "type": "string" }
|
|
5
|
-
},
|
|
6
|
-
"required": ["order_id"]
|
|
7
|
-
}`}),_.metadata_schema&&e.jsx("p",{className:"text-2xs text-status-error mt-1",children:_.metadata_schema})]})]})]})]}),e.jsx(re,{open:V,onClose:()=>T(!1),onConfirm:H,title:"Delete Role",description:e.jsxs(e.Fragment,{children:["Delete role ",e.jsx("span",{className:"font-medium font-mono text-text-primary",children:a.role}),"? This cannot be undone."]}),isPending:c.isPending,error:c.error})]})}export{Le as RoleDetailPage};
|
|
8
|
-
//# sourceMappingURL=RoleDetailPage-D70jTLZm.js.map
|