@hotmeshio/long-tail 0.8.4 → 0.8.5
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/index.d.ts +1 -1
- package/build/api/escalations/index.js +2 -1
- package/build/api/escalations/resolve.d.ts +28 -0
- package/build/api/escalations/resolve.js +98 -0
- package/build/lib/db/index.js +12 -1
- package/build/lib/db/migrate.js +27 -0
- package/build/modules/defaults.d.ts +2 -0
- package/build/modules/defaults.js +3 -1
- package/build/routes/escalations/resolve.js +18 -0
- package/build/sdk/index.d.ts +8 -0
- package/build/sdk/index.js +1 -0
- package/build/services/escalation/crud.d.ts +36 -0
- package/build/services/escalation/crud.js +53 -0
- package/build/services/maintenance/index.js +22 -0
- package/build/types/maintenance.d.ts +6 -1
- package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js → AdminDashboard-RrSvb8d-.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js.map → AdminDashboard-RrSvb8d-.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js → AgentConfigPage-5RMdI0ku.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js.map → AgentConfigPage-5RMdI0ku.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js → AgentDetailPage-C_-sIBcr.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js.map → AgentDetailPage-C_-sIBcr.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js → AgentsPage-CxNNtSsf.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js.map → AgentsPage-CxNNtSsf.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-DDwAgklw.js → AvailableEscalationsPage-BcbhA6NE.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-DDwAgklw.js.map → AvailableEscalationsPage-BcbhA6NE.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js → BotPicker-BQVz6bye.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js.map → BotPicker-BQVz6bye.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js → CapabilitiesPage-DlZ8SgO3.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js.map → CapabilitiesPage-DlZ8SgO3.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js → CollapsibleSection-lo8Qx4zb.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js.map → CollapsibleSection-lo8Qx4zb.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js → CredentialsPage-DJ9DyXsO.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js.map → CredentialsPage-DJ9DyXsO.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js → CronLabel-o0dbisw1.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js.map → CronLabel-o0dbisw1.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-OKlwH5a_.js → CustomDurationPicker-CGiwKSVJ.js} +2 -2
- package/dashboard/dist/assets/{CustomDurationPicker-OKlwH5a_.js.map → CustomDurationPicker-CGiwKSVJ.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js → ElapsedCell-N22STQcg.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js.map → ElapsedCell-N22STQcg.js.map} +1 -1
- package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js +9 -0
- package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js.map +1 -0
- package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js → EscalationsOverview-CE2p70Ct.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js.map → EscalationsOverview-CE2p70Ct.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-BARJeiS2.js → EventTable-B3Gj7Ayg.js} +2 -2
- package/dashboard/dist/assets/{EventTable-BARJeiS2.js.map → EventTable-B3Gj7Ayg.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js → GraphInvokePage-DywWVSdj.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js.map → GraphInvokePage-DywWVSdj.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-BKFpOaHX.js → HomePage-CxtBiULd.js} +2 -2
- package/dashboard/dist/assets/{HomePage-BKFpOaHX.js.map → HomePage-CxtBiULd.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js → ListToolbar-Ok2O0wmC.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js.map → ListToolbar-Ok2O0wmC.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js → McpOverview-CTCYZXIp.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js.map → McpOverview-CTCYZXIp.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js → McpQueryDetailPage-BOaXf0bZ.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js.map → McpQueryDetailPage-BOaXf0bZ.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js → McpQueryPage-BbYojoHu.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js.map → McpQueryPage-BbYojoHu.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-BzzZIXIk.js → McpRunDetailPage-CXGCVmPq.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-BzzZIXIk.js.map → McpRunDetailPage-CXGCVmPq.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js → McpRunsPage-Df1GqxUg.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js.map → McpRunsPage-Df1GqxUg.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js +2 -0
- package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-BqJn8wEZ.js → OperatorDashboard-DIR7XDeD.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BqJn8wEZ.js.map → OperatorDashboard-DIR7XDeD.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js → ProcessDetailPage-BuSi654J.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js.map → ProcessDetailPage-BuSi654J.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js → ProcessesListPage-Dhik7XpW.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js.map → ProcessesListPage-Dhik7XpW.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js.map +1 -0
- package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js → RolesPage-BRicD5mx.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js.map → RolesPage-BRicD5mx.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js → RunAsSelector-DipyBf_T.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js.map → RunAsSelector-DipyBf_T.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js → StickyPagination-CD4W450h.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js.map → StickyPagination-CD4W450h.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js → StreamMessageDetail-BbDdWDUY.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js.map → StreamMessageDetail-BbDdWDUY.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js → SwimlaneTimeline-Bktv2vi5.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js.map → SwimlaneTimeline-Bktv2vi5.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js → TaskDetailPage-pSU3xuG9.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js.map → TaskDetailPage-pSU3xuG9.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js → TasksListPage-M6ok-Nfd.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js.map → TasksListPage-M6ok-Nfd.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-C606LzT3.js → TimeAgo-DVWhRNbi.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-C606LzT3.js.map → TimeAgo-DVWhRNbi.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js → TimestampCell-rG7pmIkW.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js.map → TimestampCell-rG7pmIkW.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js → ToolTestPanel-CLcMA8OE.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js.map → ToolTestPanel-CLcMA8OE.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js → TopicDetailPage-C9gvoWYu.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js.map → TopicDetailPage-C9gvoWYu.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-B6fn7fFh.js → TopicsPage-CIGi3TdT.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-B6fn7fFh.js.map → TopicsPage-CIGi3TdT.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-Cc-5QH5q.js → UserName-CifbOahk.js} +2 -2
- package/dashboard/dist/assets/{UserName-Cc-5QH5q.js.map → UserName-CifbOahk.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-BFq7UdhA.js → WorkflowExecutionPage-BR0oVx_p.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-BFq7UdhA.js.map → WorkflowExecutionPage-BR0oVx_p.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js → WorkflowsDashboard-CSYrYQsO.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js.map → WorkflowsDashboard-CSYrYQsO.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js → WorkflowsOverview-CaXWYZuw.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js.map → WorkflowsOverview-CaXWYZuw.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js → YamlWorkflowDetailPage-CRBXahFJ.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js.map → YamlWorkflowDetailPage-CRBXahFJ.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js → YamlWorkflowsPage-D5zc8Lta.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js.map → YamlWorkflowsPage-D5zc8Lta.js.map} +1 -1
- package/dashboard/dist/assets/{agents-DlzvjNxE.js → agents-D7vYXeLj.js} +2 -2
- package/dashboard/dist/assets/{agents-DlzvjNxE.js.map → agents-D7vYXeLj.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CWMN-KUI.js → bots-CKaYN2fL.js} +2 -2
- package/dashboard/dist/assets/{bots-CWMN-KUI.js.map → bots-CKaYN2fL.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js → capabilities-BknM6oFm.js} +2 -2
- package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js.map → capabilities-BknM6oFm.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-_k9nWZy3.js → controlplane-BodNGCr-.js} +2 -2
- package/dashboard/dist/assets/{controlplane-_k9nWZy3.js.map → controlplane-BodNGCr-.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-CK8ElYzD.js → escalation-CH_XK2XM.js} +2 -2
- package/dashboard/dist/assets/{escalation-CK8ElYzD.js.map → escalation-CH_XK2XM.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-Dyrf6Tdp.js → escalation-columns-CcbLKRI4.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-Dyrf6Tdp.js.map → escalation-columns-CcbLKRI4.js.map} +1 -1
- package/dashboard/dist/assets/index--xNp5rx6.css +1 -0
- package/dashboard/dist/assets/{index-uUYXmvFf.js → index-B-LQ8fsJ.js} +2 -2
- package/dashboard/dist/assets/{index-uUYXmvFf.js.map → index-B-LQ8fsJ.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dd4Si1Rl.js → index-BH67e1Ah.js} +2 -2
- package/dashboard/dist/assets/{index-Dd4Si1Rl.js.map → index-BH67e1Ah.js.map} +1 -1
- package/dashboard/dist/assets/{index-DwWP-Z_c.js → index-B_aFcGv9.js} +4 -4
- package/dashboard/dist/assets/{index-DwWP-Z_c.js.map → index-B_aFcGv9.js.map} +1 -1
- package/dashboard/dist/assets/{index-BiApvBEz.js → index-Bwofyfi8.js} +2 -2
- package/dashboard/dist/assets/{index-BiApvBEz.js.map → index-Bwofyfi8.js.map} +1 -1
- package/dashboard/dist/assets/{index-C3h0qGzG.js → index-C3QKGY5C.js} +2 -2
- package/dashboard/dist/assets/{index-C3h0qGzG.js.map → index-C3QKGY5C.js.map} +1 -1
- package/dashboard/dist/assets/{index-ZBj6Xgim.js → index-CJpywoA8.js} +2 -2
- package/dashboard/dist/assets/{index-ZBj6Xgim.js.map → index-CJpywoA8.js.map} +1 -1
- package/dashboard/dist/assets/{index-Ex5tEte8.js → index-CV8soXIM.js} +2 -2
- package/dashboard/dist/assets/{index-Ex5tEte8.js.map → index-CV8soXIM.js.map} +1 -1
- package/dashboard/dist/assets/{index-C5Yc3TT7.js → index-CdN-Z8-r.js} +2 -2
- package/dashboard/dist/assets/{index-C5Yc3TT7.js.map → index-CdN-Z8-r.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dc-Ye2wk.js → index-D7NHjvA4.js} +2 -2
- package/dashboard/dist/assets/{index-Dc-Ye2wk.js.map → index-D7NHjvA4.js.map} +1 -1
- package/dashboard/dist/assets/{index-D8GcHxNJ.js → index-DHhz71yv.js} +2 -2
- package/dashboard/dist/assets/{index-D8GcHxNJ.js.map → index-DHhz71yv.js.map} +1 -1
- package/dashboard/dist/assets/{index-kKufOPLo.js → index-Gt2Rebs9.js} +2 -2
- package/dashboard/dist/assets/{index-kKufOPLo.js.map → index-Gt2Rebs9.js.map} +1 -1
- package/dashboard/dist/assets/index-IIq5Tkqs.js +6 -0
- package/dashboard/dist/assets/index-IIq5Tkqs.js.map +1 -0
- package/dashboard/dist/assets/{index-C7VXLKJs.js → index-_gh2K9DU.js} +2 -2
- package/dashboard/dist/assets/{index-C7VXLKJs.js.map → index-_gh2K9DU.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js → knowledge-CE6gjN3G.js} +2 -2
- package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js.map → knowledge-CE6gjN3G.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-1lzlgtS8.js → mcp-B4F-T-7R.js} +2 -2
- package/dashboard/dist/assets/{mcp-1lzlgtS8.js.map → mcp-B4F-T-7R.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js → mcp-query-CYOarL1K.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js.map → mcp-query-CYOarL1K.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js → pipelines-B4gB9qMx.js} +2 -2
- package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js.map → pipelines-B4gB9qMx.js.map} +1 -1
- package/dashboard/dist/assets/{roles-D4Wn7kE9.js → roles-LZf98ePg.js} +2 -2
- package/dashboard/dist/assets/{roles-D4Wn7kE9.js.map → roles-LZf98ePg.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-Crk9Y3C4.js → tasks-GG8c_U9w.js} +2 -2
- package/dashboard/dist/assets/{tasks-Crk9Y3C4.js.map → tasks-GG8c_U9w.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CtfcCyw9.js → topics-C7-6oOgA.js} +2 -2
- package/dashboard/dist/assets/{topics-CtfcCyw9.js.map → topics-C7-6oOgA.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js → useEventHooks-82IiMmOc.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js.map → useEventHooks-82IiMmOc.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js → useNamespace-DQEqD2LA.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js.map → useNamespace-DQEqD2LA.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js → useYamlActivityEvents-Bm0-ewLx.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js.map → useYamlActivityEvents-Bm0-ewLx.js.map} +1 -1
- package/dashboard/dist/assets/{users-DngGIiC_.js → users-DHx2ium7.js} +2 -2
- package/dashboard/dist/assets/{users-DngGIiC_.js.map → users-DHx2ium7.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-DuUspMAl.js → workflows-qIbkeQ_5.js} +2 -2
- package/dashboard/dist/assets/{workflows-DuUspMAl.js.map → workflows-qIbkeQ_5.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js → yaml-workflows-FibyeHpo.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js.map → yaml-workflows-FibyeHpo.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/api/http/escalations.md +20 -0
- package/docs/api/sdk/escalations.md +37 -2
- package/docs/dashboard.md +1 -1
- package/docs/data.md +1 -1
- package/docs/hitl-guide.md +112 -3
- package/docs/maintenance.md +6 -3
- package/docs/oauth-and-delegation.md +1 -1
- package/package.json +2 -2
- package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js +0 -9
- package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js +0 -2
- package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js +0 -11
- package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js.map +0 -1
- package/dashboard/dist/assets/index-BdqKh0UK.css +0 -1
- package/dashboard/dist/assets/index-OV1JoB_P.js +0 -6
- package/dashboard/dist/assets/index-OV1JoB_P.js.map +0 -1
package/docs/hitl-guide.md
CHANGED
|
@@ -83,9 +83,24 @@ export async function approvalWorkflow(envelope: LTEnvelope) {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
The `timeout` field
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
The `timeout` field makes the wait SLA-gated in the same single Leg1 write:
|
|
87
|
+
one `conditionLT` call yields the worklist row AND the resume timer. Omit it
|
|
88
|
+
for an open-ended wait.
|
|
89
|
+
|
|
90
|
+
Two engine contracts worth building on:
|
|
91
|
+
|
|
92
|
+
- **The row is complete from its first visible moment.** Every field of the
|
|
93
|
+
config — including `metadata` facets — commits inside the Leg1 checkpoint.
|
|
94
|
+
A claim-by-metadata router or a version-pinned facet (e.g. a
|
|
95
|
+
`schema_version` the resolver UI renders) can trust every row it reads;
|
|
96
|
+
there is no window where a row is visible but its metadata is still en route.
|
|
97
|
+
- **Early signals are buffered.** A resolve that races ahead of the
|
|
98
|
+
`condition()` registration (a fast webhook, a payload deposited before the
|
|
99
|
+
workflow starts) is held as a pending signal and delivered when the wait
|
|
100
|
+
registers — 10 minutes by default; pass `expire` to `signal()` (e.g. `'1h'`)
|
|
101
|
+
when signaling early on purpose. Fan-out (`Promise.all` over many waits)
|
|
102
|
+
scales the same way: buffering covers every signal that outruns its
|
|
103
|
+
registration.
|
|
89
104
|
|
|
90
105
|
#### Two-step form
|
|
91
106
|
|
|
@@ -209,12 +224,47 @@ await lt.escalations.resolve({
|
|
|
209
224
|
|
|
210
225
|
The patch is distinct from the resolver payload: the payload resumes the paused workflow and is not indexed; the metadata patch is the durable, queryable record on the row. Use it for the audit trail and analytics — disposition, reviewer, time-to-resolve — so the escalation table answers *what was asked, what was decided, and how long it took* without a parallel log.
|
|
211
226
|
|
|
227
|
+
### Resolving a set atomically
|
|
228
|
+
|
|
229
|
+
When one decision settles a SET of waits — each with its own payload — use
|
|
230
|
+
`lt.escalations.resolveAllOrNone({ items })` (`POST /api/escalations/resolve-all-or-none`).
|
|
231
|
+
Every listed row resolves with its own `resolverPayload` in one SQL statement,
|
|
232
|
+
waking each parked workflow with its own value, or nothing resolves and the
|
|
233
|
+
409 body names exactly the rows that blocked (`failedIds` + reasons). Pass
|
|
234
|
+
`requireClaimed: true` in claim-then-resolve flows to assert, inside the same
|
|
235
|
+
statement, that every row is still assigned to the caller. See the
|
|
236
|
+
[SDK reference](./api/sdk/escalations.md#resolveallornone) for the full contract.
|
|
237
|
+
|
|
212
238
|
---
|
|
213
239
|
|
|
214
240
|
## JSON Schema Form Authoring
|
|
215
241
|
|
|
216
242
|
The dashboard renders forms automatically from JSON Schema. No frontend code needed.
|
|
217
243
|
|
|
244
|
+
The full custom vocabulary at a glance — every `x-lt-*` keyword and extension key the renderer honors:
|
|
245
|
+
|
|
246
|
+
| Keyword | Level | Purpose |
|
|
247
|
+
|---------|-------|---------|
|
|
248
|
+
| `x-lt-widget` | field | Rich control: `file-upload`, `code-editor`, `signature`, `rich-text`, `markdown` |
|
|
249
|
+
| `x-lt-language` | field | Syntax hint shown by the `code-editor` widget |
|
|
250
|
+
| `accept` | field | File-type filter for the `file-upload` widget (e.g. `".pdf,.png"`) |
|
|
251
|
+
| `x-lt-bind` | field | Path this field's value occupies in the resolver payload (e.g. `"customer.email"`) |
|
|
252
|
+
| `x-lt-span` | field | Column span in a `two-column` layout (`2` = full width) |
|
|
253
|
+
| `x-lt-order` | schema | Field render sequence |
|
|
254
|
+
| `x-lt-layout` | schema | `"two-column"` grid layout |
|
|
255
|
+
| `x-lt-context` | schema | Context panel text shown alongside the form in user mode |
|
|
256
|
+
| `x-lt-viewport` | schema | Replace the generated form with a custom iframe UI |
|
|
257
|
+
| `format` | field | Input specialization: `password`, `date`, `date-time`, `email`, `uri`, `textarea` |
|
|
258
|
+
| `readOnly` | field | Static display (or a rendered content block with the `markdown` widget) |
|
|
259
|
+
| `required` | schema | Fields that must be filled before submit |
|
|
260
|
+
| `title` / `description` | both | Section header / helper text |
|
|
261
|
+
|
|
262
|
+
The working reference is `examples/workflows/rich-form/` — the `intake-reviewer` role's
|
|
263
|
+
versioned `form_schema` (seeded by `examples/seed-rich-form.ts`) exercises the whole
|
|
264
|
+
vocabulary in one form: a markdown content block, two-column layout, ordering, date and
|
|
265
|
+
email formats, enum, file upload, spans, required fields, and `x-lt-bind` mapping into a
|
|
266
|
+
nested payload.
|
|
267
|
+
|
|
218
268
|
### Supported Field Types
|
|
219
269
|
|
|
220
270
|
| JSON Type | Renders As |
|
|
@@ -260,6 +310,7 @@ For rich inputs beyond standard HTML types:
|
|
|
260
310
|
| `"code-editor"` | Monospace textarea with tab-key support. Use `x-lt-language` for syntax hint. |
|
|
261
311
|
| `"signature"` | HTML5 Canvas drawing pad. Outputs PNG data URL. |
|
|
262
312
|
| `"rich-text"` | Tall textarea for formatted text input. |
|
|
313
|
+
| `"markdown"` | Markdown source, rendered with the same engine as the docs drawer (headings, tables, lists, code blocks, callouts). Editable fields get a Write/Preview toggle; with `readOnly: true` the field is a pure content block — see below. |
|
|
263
314
|
|
|
264
315
|
```json
|
|
265
316
|
{
|
|
@@ -285,6 +336,64 @@ For rich inputs beyond standard HTML types:
|
|
|
285
336
|
}
|
|
286
337
|
```
|
|
287
338
|
|
|
339
|
+
#### Markdown content blocks
|
|
340
|
+
|
|
341
|
+
`readOnly: true` + `x-lt-widget: "markdown"` turns a field into a rendered content
|
|
342
|
+
block: the markdown in its `default` displays as HTML inside the form — headings,
|
|
343
|
+
tables, checklists, callouts. The versioned schema carries the page source itself,
|
|
344
|
+
so review instructions and SOPs version with the form they belong to, and the
|
|
345
|
+
source rides along in the resolver payload like any read-only field.
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"properties": {
|
|
350
|
+
"review_guide": {
|
|
351
|
+
"type": "string",
|
|
352
|
+
"readOnly": true,
|
|
353
|
+
"x-lt-widget": "markdown",
|
|
354
|
+
"x-lt-span": 2,
|
|
355
|
+
"default": "### Review checklist\n\n1. Confirm the **legal name** matches.\n2. Send a test message before approving.\n\n> Escalate non-standard contract language to legal."
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Without `readOnly`, the field is a markdown *editor* — the resolver writes source in
|
|
362
|
+
a Write/Preview toggle and the submitted value is the markdown text.
|
|
363
|
+
|
|
364
|
+
### Payload Binding (`x-lt-bind`)
|
|
365
|
+
|
|
366
|
+
The form is flat; the payload the workflow consumes rarely is. A field may declare
|
|
367
|
+
`x-lt-bind` — the path its value occupies in the resolver payload (dot keys, optional
|
|
368
|
+
`[n]` indices). The dashboard maps the flat form through the binds on submit, and
|
|
369
|
+
reverse-maps workflow-seeded `envelope.formDefaults` through them to prefill. A field
|
|
370
|
+
with no bind lands at its own name at the payload root (1:1).
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
{
|
|
374
|
+
"properties": {
|
|
375
|
+
"customer_name": { "type": "string", "x-lt-bind": "customer.name" },
|
|
376
|
+
"contact_email": { "type": "string", "format": "email", "x-lt-bind": "customer.email" },
|
|
377
|
+
"tier": { "type": "string", "enum": ["starter", "professional"], "x-lt-bind": "contract.tier" },
|
|
378
|
+
"notes": { "type": "string", "format": "textarea" }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Submitting `{ customer_name, contact_email, tier, notes }` stores:
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"customer": { "name": "…", "email": "…" },
|
|
388
|
+
"contract": { "tier": "…" },
|
|
389
|
+
"notes": "…"
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Only the FORM is versioned on the role — the payload shape is the workflow's own
|
|
394
|
+
contract, produced by the binds. Evolve the form and its binds together, and the
|
|
395
|
+
workflow's resolver type in the same commit.
|
|
396
|
+
|
|
288
397
|
### Layout Options (`x-lt-layout`)
|
|
289
398
|
|
|
290
399
|
Control how fields are arranged:
|
package/docs/maintenance.md
CHANGED
|
@@ -28,6 +28,7 @@ await start({
|
|
|
28
28
|
{ target: 'jobs', olderThan: '14 days', action: 'delete', hasEntity: false },
|
|
29
29
|
{ target: 'jobs', olderThan: '14 days', action: 'prune', hasEntity: true },
|
|
30
30
|
{ target: 'jobs', olderThan: '180 days', action: 'delete', pruned: true },
|
|
31
|
+
{ target: 'escalations', olderThan: '180 days', action: 'delete' },
|
|
31
32
|
],
|
|
32
33
|
},
|
|
33
34
|
});
|
|
@@ -42,14 +43,15 @@ await start({
|
|
|
42
43
|
|
|
43
44
|
## What gets cleaned
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
Five categories of data are subject to maintenance:
|
|
46
47
|
|
|
47
48
|
| Category | Description |
|
|
48
49
|
|---|---|
|
|
49
|
-
| **Streams** |
|
|
50
|
+
| **Streams** | Stream messages used internally by HotMesh to coordinate activities. These are pure infrastructure; no user-facing data resides here. |
|
|
50
51
|
| **Transient jobs** | Job rows where the `entity` column is `NULL`. These represent activity executions, signal deliveries, and other bookkeeping that is not tied to a named workflow entity. They serve no purpose after execution completes. |
|
|
51
52
|
| **Entity jobs** | Job rows where `entity` is set. These represent actual workflow instances -- the rows that back `Durable.Client.workflow.search()` calls and execution exports. Deleting them removes the workflow record entirely. |
|
|
52
53
|
| **Pruned jobs** | Entity jobs that have already had their execution artifacts stripped (the `pruned_at` column is not `NULL`). These retain core data but no longer carry execution scaffolding. They can be hard-deleted after a longer retention window. |
|
|
54
|
+
| **Terminal escalations** | Escalation rows in a terminal status (`resolved`, `cancelled`, `expired`). Terminal rows are inert audit records -- every engine state transition guards on `status = 'pending'`, so aging them out is safe for live waiters, claims, and signal delivery. The engine-owned prune deletes in atomic batches until the retention horizon is drained. Pending rows are never touched. |
|
|
53
55
|
|
|
54
56
|
## Prune vs. delete
|
|
55
57
|
|
|
@@ -93,11 +95,12 @@ Defined in `types/maintenance.ts`. Each rule describes a single cleanup operatio
|
|
|
93
95
|
|
|
94
96
|
| Field | Type | Required | Description |
|
|
95
97
|
|---|---|---|---|
|
|
96
|
-
| `target` | `'streams' \| 'jobs'` | Yes | The resource type to act on. |
|
|
98
|
+
| `target` | `'streams' \| 'jobs' \| 'escalations'` | Yes | The resource type to act on. |
|
|
97
99
|
| `action` | `'delete' \| 'prune'` | Yes | Whether to remove entirely or strip execution artifacts. |
|
|
98
100
|
| `olderThan` | `string` | Yes | A PostgreSQL interval expression: `'7 days'`, `'24 hours'`, `'90 days'`. |
|
|
99
101
|
| `hasEntity` | `boolean` | No | When `target` is `'jobs'`: `true` selects entity jobs, `false` selects transient jobs (where `entity IS NULL`). |
|
|
100
102
|
| `pruned` | `boolean` | No | When `true`, only targets jobs where `pruned_at IS NOT NULL` -- jobs that have already been pruned. |
|
|
103
|
+
| `statuses` | `Array<'resolved' \| 'cancelled' \| 'expired'>` | No | When `target` is `'escalations'`: which terminal statuses to age out. Defaults to all three. |
|
|
101
104
|
|
|
102
105
|
### `LTMaintenanceConfig`
|
|
103
106
|
|
|
@@ -143,7 +143,7 @@ if (ctx) {
|
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context
|
|
146
|
+
The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context — YAML workflow workers get auth propagation this way without any explicit wiring.
|
|
147
147
|
|
|
148
148
|
## Delegation Tokens
|
|
149
149
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/long-tail",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@anthropic-ai/sdk": "^0.92.0",
|
|
73
73
|
"@aws-sdk/client-s3": "^3.1017.0",
|
|
74
74
|
"@aws-sdk/s3-request-presigner": "^3.1045.0",
|
|
75
|
-
"@hotmeshio/hotmesh": "^0.
|
|
75
|
+
"@hotmeshio/hotmesh": "^0.26.1",
|
|
76
76
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
77
77
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.215.0",
|
|
78
78
|
"@opentelemetry/resources": "^2.5.1",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import{a as l,j as e}from"./vendor-query-B2UbickB.js";import{c as S,e as w,i as _,j as E}from"./roles-D4Wn7kE9.js";import{J as C}from"./index-DwWP-Z_c.js";import{g as j,B as O,V as R,ah as q}from"./vendor-icons-DOIDKgo6.js";import{e as V}from"./vendor-react-CXumBFUA.js";const J=`{
|
|
2
|
-
"type": "object",
|
|
3
|
-
"properties": {
|
|
4
|
-
"approved": { "type": "boolean", "title": "Approve?" },
|
|
5
|
-
"notes": { "type": "string", "title": "Notes" }
|
|
6
|
-
},
|
|
7
|
-
"required": ["approved"]
|
|
8
|
-
}`;function v(o){const t=o.trim();if(!t)return{ok:!0,value:null};try{const r=JSON.parse(t);return typeof r!="object"||Array.isArray(r)||r===null?{ok:!1}:{ok:!0,value:r}}catch{return{ok:!1}}}function F(){const{role:o}=V(),{data:t,isLoading:r}=S(),n=w(),s=((t==null?void 0:t.roles)??[]).find(a=>a.role===o),[c,i]=l.useState(""),[h,u]=l.useState(""),[N,d]=l.useState(!1),[g,x]=l.useState(!1),[p,m]=l.useState(!1),f=l.useRef(null);l.useEffect(()=>{!s||f.current===s.role||(f.current=s.role,i(s.form_schema?JSON.stringify(s.form_schema,null,2):""),u(""),d(!1),m(!1))},[s]);const y=a=>{i(a),d(!0),x(!1),m(!v(a).ok)},b=()=>{const a=v(c);if(!a.ok||!s){m(!a.ok);return}n.mutate({role:s.role,form_schema:a.value??null,change_summary:h.trim()||void 0},{onSuccess:()=>{d(!1),x(!0),u(""),window.setTimeout(()=>x(!1),2500)}})},k=a=>{i(a?JSON.stringify(a,null,2):""),d(!0),x(!1),m(!1)};return r?e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-96 bg-surface-sunken rounded"})]}):s?e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-10",children:[e.jsx("div",{children:e.jsxs("div",{className:"flex items-center gap-3 mb-1",children:[e.jsx(j,{className:"w-5 h-5 text-accent",strokeWidth:1.5}),e.jsx("h1",{className:"text-lg font-medium text-text-primary",children:"Escalation Schema"}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:dashboard.md:escalation-schema"},className:"text-text-quaternary hover:text-accent transition-colors",title:"Open docs for this page",children:e.jsx(O,{className:"w-4 h-4",strokeWidth:1.5})}),e.jsx("span",{className:"font-mono text-sm text-text-tertiary",children:s.role}),s.current_schema_version!=null&&e.jsxs("span",{className:"text-[10px] font-mono text-text-quaternary",children:["v",s.current_schema_version," in use"]})]})}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[g&&e.jsxs("span",{className:"flex items-center gap-1 text-xs text-status-success animate-page-enter",children:[e.jsx(R,{className:"w-3 h-3"})," Saved"]}),n.error&&e.jsx("span",{className:"text-xs text-status-error max-w-[220px] truncate",children:n.error.message}),e.jsx("button",{onClick:b,disabled:!N||p||n.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:n.isPending?"Saving…":"Save Version"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-start",children:[e.jsxs("div",{className:"col-span-2 space-y-6",children:[e.jsx("textarea",{value:c,onChange:a=>y(a.target.value),rows:28,spellCheck:!1,className:"input text-xs font-mono w-full resize-y leading-relaxed",placeholder:J}),p&&e.jsx("p",{className:"text-[10px] text-status-error -mt-4",children:"Invalid JSON"}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Change Summary"}),e.jsx("input",{type:"text",value:h,onChange:a=>u(a.target.value),placeholder:"e.g., Added lotNumber field for shoe orders",className:"input text-sm w-full"}),e.jsx("p",{className:"text-[10px] text-text-quaternary mt-1.5",children:"Recorded on the version this save creates."})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border",children:[e.jsx(q,{className:"w-4 h-4 text-text-quaternary",strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:"Versions"})]}),e.jsx(L,{role:s.role,onLoad:k})]})]})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(j,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsx("h2",{className:"text-lg font-medium text-text-primary mb-2",children:"Role not found"})]})}function L({role:o,onLoad:t}){const{data:r}=_(o),n=(r==null?void 0:r.versions)??[];return n.length===0?e.jsx("p",{className:"text-[11px] text-text-tertiary leading-relaxed",children:"Versions appear here after the first save. Each schema change adds one; earlier versions stay viewable and loadable as an editing base."}):e.jsx("div",{className:"divide-y divide-surface-border/40",children:n.map(s=>e.jsx(P,{role:o,summary:s,onLoad:t},s.version))})}function P({role:o,summary:t,onLoad:r}){const[n,s]=l.useState(!1),c=E(o,t.version,n);return e.jsxs("div",{className:"py-2",children:[e.jsxs("button",{onClick:()=>s(i=>!i),className:"w-full text-left group",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:["v",t.version]}),t.is_current&&e.jsx("span",{className:"text-[9px] font-bold uppercase tracking-wider text-accent",children:"current"}),e.jsx("span",{className:"flex-1"}),e.jsx("span",{className:"text-[10px] text-text-quaternary shrink-0",children:new Date(t.created_at).toLocaleDateString()})]}),t.change_summary&&e.jsx("p",{className:"mt-0.5 text-[10px] text-text-tertiary truncate",children:t.change_summary})]}),n&&e.jsx("div",{className:"mt-2 space-y-2",children:c.data?e.jsxs(e.Fragment,{children:[c.data.form_schema?e.jsx(C,{data:c.data.form_schema,defaultCollapsed:!0}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"This version carries no form schema."}),e.jsx("button",{onClick:()=>r(c.data.form_schema??null),className:"text-[10px] text-accent hover:underline",children:"Load into editor"})]}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"Loading…"})})]})}export{F as EscalationSchemaPage};
|
|
9
|
-
//# sourceMappingURL=EscalationSchemaPage-Br5sD8-N.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EscalationSchemaPage-Br5sD8-N.js","sources":["../../src/pages/admin/roles/EscalationSchemaPage.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Braces, Check, History, BookOpen } from 'lucide-react';\nimport {\n useRoleDetails,\n useRoleSchema,\n useRoleSchemaVersions,\n useUpdateRole,\n type RoleSchemaVersionSummary,\n} from '../../../api/roles';\nimport { JsonViewer } from '../../../components/common/data/JsonViewer';\n\n/**\n * The escalation form schema, on its own page (/admin/roles/:role/schema).\n * This is the form a person completes to resolve the role's escalations —\n * versioned, pinnable by workflows (conditionLT schemaVersion), and worth a\n * full editing surface. Saving here writes ONLY the schema: one PATCH with\n * form_schema (+ optional change summary); every actual change appends the\n * next immutable version.\n */\n\nconst PLACEHOLDER = `{\n \"type\": \"object\",\n \"properties\": {\n \"approved\": { \"type\": \"boolean\", \"title\": \"Approve?\" },\n \"notes\": { \"type\": \"string\", \"title\": \"Notes\" }\n },\n \"required\": [\"approved\"]\n}`;\n\nfunction safeParseObject(text: string): { ok: boolean; value?: Record<string, unknown> | null } {\n const trimmed = text.trim();\n if (!trimmed) return { ok: true, value: null };\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) return { ok: false };\n return { ok: true, value: parsed };\n } catch {\n return { ok: false };\n }\n}\n\nexport function EscalationSchemaPage() {\n const { role: roleKey } = useParams<{ role: string }>();\n const { data, isLoading } = useRoleDetails();\n const updateRole = useUpdateRole();\n\n const role = (data?.roles ?? []).find((r) => r.role === roleKey);\n\n const [text, setText] = useState('');\n const [summary, setSummary] = useState('');\n const [dirty, setDirty] = useState(false);\n const [savedOk, setSavedOk] = useState(false);\n const [jsonError, setJsonError] = useState(false);\n\n // Seed the editor from the live schema exactly once per role — refetches\n // (cache invalidations, saves) must not clobber in-progress edits.\n const seededFor = useRef<string | null>(null);\n useEffect(() => {\n if (!role || seededFor.current === role.role) return;\n seededFor.current = role.role;\n setText(role.form_schema ? JSON.stringify(role.form_schema, null, 2) : '');\n setSummary('');\n setDirty(false);\n setJsonError(false);\n }, [role]);\n\n const handleEdit = (value: string) => {\n setText(value);\n setDirty(true);\n setSavedOk(false);\n setJsonError(!safeParseObject(value).ok);\n };\n\n const handleSave = () => {\n const parsed = safeParseObject(text);\n if (!parsed.ok || !role) { setJsonError(!parsed.ok); return; }\n // Saving just the schema — nothing else rides this PATCH.\n updateRole.mutate(\n { role: role.role, form_schema: parsed.value ?? null, change_summary: summary.trim() || undefined },\n {\n onSuccess: () => {\n setDirty(false);\n setSavedOk(true);\n setSummary('');\n window.setTimeout(() => setSavedOk(false), 2500);\n },\n },\n );\n };\n\n const loadIntoEditor = (schema: Record<string, unknown> | null) => {\n setText(schema ? JSON.stringify(schema, null, 2) : '');\n setDirty(true);\n setSavedOk(false);\n setJsonError(false);\n };\n\n if (isLoading) {\n return (\n <div className=\"animate-pulse space-y-4\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-96 bg-surface-sunken rounded\" />\n </div>\n );\n }\n\n if (!role) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center\">\n <Braces className=\"w-12 h-12 text-text-quaternary mb-4\" strokeWidth={1} />\n <h2 className=\"text-lg font-medium text-text-primary mb-2\">Role not found</h2>\n </div>\n );\n }\n\n return (\n <div>\n {/* ── Header ── */}\n <div className=\"flex items-start justify-between mb-10\">\n <div>\n <div className=\"flex items-center gap-3 mb-1\">\n <Braces className=\"w-5 h-5 text-accent\" strokeWidth={1.5} />\n <h1 className=\"text-lg font-medium text-text-primary\">Escalation Schema</h1>\n <button\n onClick={() => { window.location.hash = '#docs:dashboard.md:escalation-schema'; }}\n className=\"text-text-quaternary hover:text-accent transition-colors\"\n title=\"Open docs for this page\"\n >\n <BookOpen className=\"w-4 h-4\" strokeWidth={1.5} />\n </button>\n <span className=\"font-mono text-sm text-text-tertiary\">{role.role}</span>\n {role.current_schema_version != null && (\n <span className=\"text-[10px] font-mono text-text-quaternary\">\n v{role.current_schema_version} in use\n </span>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 shrink-0\">\n {savedOk && (\n <span className=\"flex items-center gap-1 text-xs text-status-success animate-page-enter\">\n <Check className=\"w-3 h-3\" /> Saved\n </span>\n )}\n {updateRole.error && (\n <span className=\"text-xs text-status-error max-w-[220px] truncate\">\n {(updateRole.error as Error).message}\n </span>\n )}\n <button\n onClick={handleSave}\n disabled={!dirty || jsonError || updateRole.isPending}\n 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\"\n >\n {updateRole.isPending ? 'Saving…' : 'Save Version'}\n </button>\n </div>\n </div>\n\n {/* ── Editor + version rail ── */}\n <div className=\"grid grid-cols-3 gap-16 items-start\">\n <div className=\"col-span-2 space-y-6\">\n <textarea\n value={text}\n onChange={(e) => handleEdit(e.target.value)}\n rows={28}\n spellCheck={false}\n className=\"input text-xs font-mono w-full resize-y leading-relaxed\"\n placeholder={PLACEHOLDER}\n />\n {jsonError && <p className=\"text-[10px] text-status-error -mt-4\">Invalid JSON</p>}\n\n <div>\n <label className=\"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5\">\n Change Summary\n </label>\n <input\n type=\"text\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"e.g., Added lotNumber field for shoe orders\"\n className=\"input text-sm w-full\"\n />\n <p className=\"text-[10px] text-text-quaternary mt-1.5\">\n Recorded on the version this save creates.\n </p>\n </div>\n </div>\n\n {/* Version history — view any snapshot, load one as the editing base */}\n <div>\n <div className=\"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border\">\n <History className=\"w-4 h-4 text-text-quaternary\" strokeWidth={1.5} />\n <h2 className=\"section-h2\">Versions</h2>\n </div>\n <VersionRail role={role.role} onLoad={loadIntoEditor} />\n </div>\n </div>\n </div>\n );\n}\n\nfunction VersionRail({\n role,\n onLoad,\n}: {\n role: string;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const { data } = useRoleSchemaVersions(role);\n const versions = data?.versions ?? [];\n\n if (versions.length === 0) {\n return (\n <p className=\"text-[11px] text-text-tertiary leading-relaxed\">\n Versions appear here after the first save. Each schema change adds one;\n earlier versions stay viewable and loadable as an editing base.\n </p>\n );\n }\n\n return (\n <div className=\"divide-y divide-surface-border/40\">\n {versions.map((v) => (\n <VersionEntry key={v.version} role={role} summary={v} onLoad={onLoad} />\n ))}\n </div>\n );\n}\n\nfunction VersionEntry({\n role,\n summary,\n onLoad,\n}: {\n role: string;\n summary: RoleSchemaVersionSummary;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const [open, setOpen] = useState(false);\n // Snapshot fetch is lazy — only when the entry is expanded.\n const snapshot = useRoleSchema(role, summary.version, open);\n\n return (\n <div className=\"py-2\">\n <button onClick={() => setOpen((o) => !o)} className=\"w-full text-left group\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-mono text-text-secondary\">v{summary.version}</span>\n {summary.is_current && (\n <span className=\"text-[9px] font-bold uppercase tracking-wider text-accent\">current</span>\n )}\n <span className=\"flex-1\" />\n <span className=\"text-[10px] text-text-quaternary shrink-0\">\n {new Date(summary.created_at).toLocaleDateString()}\n </span>\n </div>\n {summary.change_summary && (\n <p className=\"mt-0.5 text-[10px] text-text-tertiary truncate\">{summary.change_summary}</p>\n )}\n </button>\n {open && (\n <div className=\"mt-2 space-y-2\">\n {snapshot.data ? (\n <>\n {snapshot.data.form_schema\n ? <JsonViewer data={snapshot.data.form_schema} defaultCollapsed />\n : <p className=\"text-[10px] text-text-quaternary\">This version carries no form schema.</p>}\n <button\n onClick={() => onLoad((snapshot.data!.form_schema ?? null) as Record<string, unknown> | null)}\n className=\"text-[10px] text-accent hover:underline\"\n >\n Load into editor\n </button>\n </>\n ) : (\n <p className=\"text-[10px] text-text-quaternary\">Loading…</p>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["PLACEHOLDER","safeParseObject","text","trimmed","parsed","EscalationSchemaPage","roleKey","useParams","data","isLoading","useRoleDetails","updateRole","useUpdateRole","role","r","setText","useState","summary","setSummary","dirty","setDirty","savedOk","setSavedOk","jsonError","setJsonError","seededFor","useRef","useEffect","handleEdit","value","handleSave","loadIntoEditor","schema","jsxs","jsx","Braces","BookOpen","Check","e","History","VersionRail","onLoad","useRoleSchemaVersions","versions","v","VersionEntry","open","setOpen","snapshot","useRoleSchema","o","Fragment","JsonViewer"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as w,j as e}from"./vendor-query-B2UbickB.js";import{c as Pe}from"./roles-D4Wn7kE9.js";import{O as ye}from"./index-DwWP-Z_c.js";import{i as Se}from"./useEventHooks-Dn_vN2xE.js";import{P as Ce}from"./PageHeader-DJo7yMUR.js";import{X as Me,ae as ie,av as Le,ar as qe}from"./vendor-icons-DOIDKgo6.js";import{L as Q,f as Fe,c as Oe}from"./vendor-react-CXumBFUA.js";const ge=800,je=270,I=40,Ae=76,ce=24,Ee=72,oe=ge-I-Ae,be=je-ce-Ee,$=ce+be,he=I+oe,j="0.5s cubic-bezier(0.4, 0, 0.2, 1)",me=9;function We(t){return`M ${(t.x-me).toFixed(1)} ${t.y.toFixed(1)} L ${(t.x+me).toFixed(1)} ${t.y.toFixed(1)}`}function T(t){if(t.length===0)return"";if(t.length===1)return We(t[0]);const n=[];for(let l=0;l<t.length-1;l++){const i=t[Math.max(0,l-1)],c=t[l],p=t[l+1],a=t[Math.min(t.length-1,l+2)],d=(c.x+(p.x-i.x)/6).toFixed(1),u=(c.y+(p.y-i.y)/6).toFixed(1),o=(p.x-(a.x-c.x)/6).toFixed(1),x=(p.y-(a.y-c.y)/6).toFixed(1);l===0&&n.push(`M ${c.x.toFixed(1)} ${c.y.toFixed(1)}`),n.push(`C ${d} ${u}, ${o} ${x}, ${p.x.toFixed(1)} ${p.y.toFixed(1)}`)}return n.join(" ")}function Ie(t,n){if(t.length<2||n.length<2)return"";const l=T(t),i=[...n].reverse(),c=T(i),p=c.slice(c.indexOf("C"));return`${l} L ${i[0].x.toFixed(1)} ${i[0].y.toFixed(1)} ${p} Z`}function Te(t){return t>=1e4?`${Math.round(t/1e3)}k`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)}function ne(t){return t.length>0?t[t.length-1]:void 0}const O="#6366f1",D="#0ea5e9",q="#1E3A8A",X="#059669",ve="#b0e0e6",A="#5b93ad",De=13;function Be(t,n){const l=[...t].sort((a,d)=>a.y-d.y);let i=-1/0;const c=l.map(a=>{const d=Math.max(a.y+3,i+De);return i=d,{...a,labelY:d}}),p=c.length>0?c[c.length-1].labelY-n:0;if(p>0)for(const a of c)a.labelY-=p;return c}function ze({stations:t,selectedRole:n,onSelect:l,onUpstreamSelect:i,onPrioritySelect:c,periodHours:p}){const[a,d]=w.useState(null),u=t.length;if(u===0)return null;const o=s=>u===1?I+oe/2:I+s/(u-1)*oe,x=new Map(t.map((s,h)=>[s.role,o(h)])),y=t.map((s,h)=>{var P,V,G,U;const f=s.target_per_hour??null,C=f!=null?f*p:null;return{idx:h,x:o(h),expected:C,actual:((P=s.metric)==null?void 0:P.resolved)??0,active:((V=s.metric)==null?void 0:V.claimed)??0,pending:((G=s.metric)==null?void 0:G.pending)??0,priorityCount:((U=s.metric)==null?void 0:U.priority_count)??0}}),b=y.some(s=>s.expected!=null),v=Math.max(1,...y.map(s=>s.expected??0),...y.map(s=>s.actual),...y.map(s=>s.expected!=null?s.pending:0))*1.15,S=s=>ce+be*(1-Math.max(0,Math.min(s,v))/v),N=y.filter(s=>s.expected!=null),M=N.map(s=>({...s,y:S(s.expected)})),_=N.map(s=>({...s,y:S(s.actual)})),R=N.map(s=>({...s,y:S(s.active)})),k=N.map(s=>({...s,y:S(s.pending)})),B=T(M),z=T(_),H=T(R),Z=T(k),F=ne(_),K=ne(M),E=ne(R),J=ne(k),ee=_.length>=2&&F?`${z} L ${F.x.toFixed(1)} ${$} L ${_[0].x.toFixed(1)} ${$} Z`:"",W=N.some(s=>s.pending>0||s.active>0),te=W&&R.length>=2&&E?`${H} L ${E.x.toFixed(1)} ${$} L ${R[0].x.toFixed(1)} ${$} Z`:"",Y=W?Ie(k,R):"",r=N.some(s=>s.active>0),m=N.some(s=>s.pending>s.active),L=Be([K?{key:"target",text:"Target",color:X,y:K.y}:null,F?{key:"actual",text:"Resolved",color:q,y:F.y}:null,r&&E?{key:"active",text:"Claimed",color:O,y:E.y}:null,m&&J?{key:"queued",text:"Pending",color:D,y:J.y}:null].filter(Boolean),$+4);return e.jsxs("svg",{viewBox:`0 0 ${ge} ${je}`,className:"w-full h-full",preserveAspectRatio:"xMidYMid meet",style:{display:"block"},children:[t.map(s=>{if(!s.parent_role)return null;const h=x.get(s.parent_role);if(h==null)return null;const f=o(t.findIndex(C=>C.role===s.role));return e.jsx("line",{x1:h,y1:$,x2:f,y2:$,stroke:"#e2e8f0",strokeWidth:.75},`dep-${s.role}`)}),ee&&e.jsx("path",{d:ee,fill:q,opacity:.05,style:{transition:`d ${j}`}}),te&&e.jsx("path",{d:te,fill:O,opacity:.12,style:{transition:`d ${j}`}}),Y&&e.jsx("path",{d:Y,fill:D,opacity:.12,style:{transition:`d ${j}`}}),B&&e.jsx("path",{d:B,fill:"none",stroke:X,strokeWidth:.5,strokeLinejoin:"round",opacity:.9,style:{transition:`d ${j}`}}),M.map(s=>e.jsx("text",{x:s.x,y:s.y-3.5,textAnchor:"middle",fontSize:6.5,fill:X,fontFamily:"ui-monospace, monospace",fontWeight:"500",opacity:.7,style:{transition:`y ${j}`},children:Math.round(s.expected)},`tval-${s.idx}`)),W&&Z&&e.jsx("path",{d:Z,fill:"none",stroke:D,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.55,style:{transition:`d ${j}`}}),H&&e.jsx("path",{d:H,fill:"none",stroke:O,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.5,style:{transition:`d ${j}`}}),z&&e.jsx("path",{d:z,fill:"none",stroke:q,strokeWidth:.6,strokeLinejoin:"round",strokeLinecap:"round",opacity:.65,style:{transition:`d ${j}`}}),R.map((s,h)=>{if(s.pending<=0&&s.active<=0)return null;const f=Math.max(2.5,Math.min(5,2.5+s.active/10)),C=s.pending>s.active,P=Math.min(k[h].y-4,s.y-f-4);return e.jsxs("g",{children:[s.pending>0&&e.jsx("text",{x:s.x,y:P,textAnchor:"middle",fontSize:7.5,fill:C?D:"#64748b",fontFamily:"ui-monospace, monospace",fontWeight:"600",style:{transition:`y ${j}`},children:s.pending}),e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:f,fill:O,opacity:.85}),s.active>0&&e.jsx("text",{y:f+9,textAnchor:"middle",fontSize:7.5,fill:O,fontFamily:"ui-monospace, monospace",fontWeight:"600",children:s.active})]})]},`active-${s.idx}`)}),y.map(s=>{var ue;const h=t[s.idx],f=s.x,C=s.expected!=null?S(s.actual):$,P=Math.max(2.25,Math.min(4,2.25+s.actual/160)),V=h.role===n,G=a===s.idx,U=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} priority`:""),se=U.length*5.5+20,de=Math.max(I+se/2+4,Math.min(he-se/2-4,f)),pe=C-P-10;return e.jsxs("g",{onClick:()=>l(h.role),onMouseEnter:()=>d(s.idx),onMouseLeave:()=>d(null),style:{cursor:"pointer"},children:[e.jsxs("g",{transform:`translate(${f} ${C})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:22,fill:"transparent"}),e.jsx("circle",{r:P,fill:q,opacity:.9,style:{transition:`r ${j}`}}),s.expected!=null&&e.jsx("text",{y:-P-5,textAnchor:"middle",fontSize:9.5,fill:q,fontFamily:"ui-monospace, monospace",fontWeight:"500",children:Te(s.actual)}),G&&!V&&e.jsx("circle",{r:P+3,fill:"none",stroke:q,strokeWidth:1,opacity:.4}),V&&e.jsx("circle",{r:P+4,fill:"none",stroke:"#6366f1",strokeWidth:2})]}),s.priorityCount>0&&e.jsxs("g",{transform:`translate(${f+15} ${Math.max(C-15,12)})`,onClick:le=>{le.stopPropagation(),c==null||c(h.role)},style:{cursor:"pointer",transition:`transform ${j}`},children:[e.jsx("title",{children:`${s.priorityCount} past the priority threshold — click to pull oldest first`}),e.jsx("circle",{r:6.5+(String(s.priorityCount).length-1)*2,fill:ve,opacity:.9}),e.jsx("text",{y:2.5,textAnchor:"middle",fontSize:7,fill:A,fontFamily:"ui-monospace, monospace",fontWeight:"700",children:s.priorityCount})]}),(((ue=h.upstream_roles)==null?void 0:ue.length)??0)>0&&e.jsxs("g",{transform:`translate(${f-20} ${$})`,onClick:le=>{le.stopPropagation(),i==null||i(h.upstream_roles[0])},style:{cursor:"pointer"},children:[e.jsx("title",{children:`Fed by ${h.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",stroke:"#94a3b8",strokeWidth:1,strokeDasharray:"2 2",strokeLinecap:"round"}),e.jsx("path",{d:"M 0 -2 L 8 -2",stroke:"#94a3b8",strokeWidth:1,strokeLinecap:"round"}),e.jsx("path",{d:"M 8 -2 l -3.5 -2 v 4 z",fill:"#94a3b8"})]}),e.jsx("text",{x:f,y:$+14,textAnchor:"middle",fontSize:9.5,fill:"#475569",fontFamily:"ui-sans-serif, sans-serif",fontWeight:"500",children:h.title??h.role}),e.jsx("text",{x:f,y:$+26,textAnchor:"middle",fontSize:8,fill:"#94a3b8",fontFamily:"ui-monospace, monospace",children:h.role}),G&&e.jsxs("g",{children:[e.jsx("rect",{x:de-se/2,y:pe-16,width:se,height:19,rx:3,fill:"#0f172a",opacity:.88}),e.jsx("text",{x:de,y:pe-3,textAnchor:"middle",fontSize:8.5,fill:"white",fontFamily:"ui-monospace, monospace",children:U})]})]},h.role)}),!b&&e.jsx("text",{x:I+oe/2,y:$+48,textAnchor:"middle",fontSize:8.5,fill:"#94a3b8",fontFamily:"ui-sans-serif, sans-serif",children:"Set target_per_hour on each role in Admin → Roles to enable the pace chart"}),L.map(s=>e.jsx("text",{x:he+6,y:s.labelY,fontSize:8,fill:s.color,fontFamily:"ui-sans-serif, sans-serif",fontWeight:s.key==="actual"?700:500,opacity:s.key==="active"?.85:1,style:{transition:`y ${j}`},children:s.text},s.key))]})}function xe(t){const n=`/escalations/available?role=${encodeURIComponent(t.role)}`;if(t.priority_facet){const l=JSON.stringify([{field:`metadata.${t.priority_facet}`,direction:"asc"}]);return`${n}&orderBy=${encodeURIComponent(l)}`}return`${n}&sort_by=created_at&order=asc`}const Ne=["15m","1h","24h","7d","30d"],He=t=>Ne.includes(t);function ae(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function re({label:t,value:n}){return e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary uppercase tracking-wider",children:t}),e.jsx("span",{className:"text-xs font-mono font-medium text-text-primary tabular-nums",children:n})]})}function Ye({period:t,onChange:n}){return e.jsx("div",{className:"flex items-center gap-1 mb-4",children:Ne.map(l=>e.jsx("button",{onClick:()=>n(l),className:`px-2 py-0.5 text-[10px] font-mono rounded transition-colors ${t===l?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:l},l))})}function Ve({role:t,globalPeriod:n,onClose:l}){const[i,c]=w.useState(He(n)?n:"24h"),{data:p}=ye(i),a=p==null?void 0:p.stations.find(x=>x.role===t.role),d=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-1",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-mono font-bold text-text-primary",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-secondary",children:t.title})]}),t.description&&e.jsx("p",{className:"text-[10px] text-text-tertiary mt-0.5 line-clamp-2 leading-relaxed",children:t.description})]}),e.jsx("button",{onClick:l,className:"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2",children:e.jsx(Me,{className:"w-4 h-4"})})]}),e.jsxs(Q,{to:`/admin/roles/${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4",children:[e.jsx(ie,{className:"w-2.5 h-2.5"}),"Edit in Roles"]}),e.jsx(Ye,{period:i,onChange:c}),(u||d||o)&&e.jsxs("div",{className:"flex items-center gap-4 mb-4 text-[10px]",children:[u&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[u,"/h"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"target"})]}),d&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[d,"m"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"SLA"})]}),o&&e.jsxs("div",{children:[e.jsx("div",{className:"font-mono font-semibold text-text-primary",children:o}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"workers"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Wait (queue)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.wait.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.wait.avg)??null)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Work (proc.)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.work.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.work.avg)??null)})]})]}),e.jsxs("div",{className:"border-t border-surface-border/40 pt-2 mt-3 space-y-1.5",children:[d&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary",children:"SLA target"}),e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:[d,"m"]})]}),a&&a.priority_count>0&&e.jsxs(Q,{to:xe(t),className:"flex items-center justify-between group",children:[e.jsxs("span",{className:"text-[10px]",style:{color:A},children:[a.priority_count," priority — pull oldest first"]}),e.jsx(ie,{className:"w-3 h-3 transition-colors",style:{color:A}})]})]}),e.jsx("div",{className:"mt-4 border-t border-surface-border/40 pt-3",children:e.jsxs(Q,{to:`/escalations/available?role=${encodeURIComponent(t.role)}`,className:"flex items-center gap-1.5 text-[10px] text-accent hover:underline",children:["View full queue",e.jsx(ie,{className:"w-2.5 h-2.5"})]})})]})}function Ge({allMetrics:t,orderedRoles:n,period:l}){const i=t.reduce((o,x)=>o+x.pending,0),c=t.reduce((o,x)=>o+x.resolved,0),p=t.reduce((o,x)=>o+x.priority_count,0),a=new Map(t.map(o=>[o.role,o])),d=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-[9px] text-text-quaternary uppercase tracking-wider mb-3",children:["Pipeline · ",l]}),e.jsx("div",{className:"mb-4",children:d>0?e.jsxs("p",{className:"text-[11px]",style:{color:A},children:[d," station",d>1?"s":""," with priority items"]}):u>0?e.jsxs("p",{className:"text-[11px] text-status-warning",children:[u," station",u>1?"s":""," with backlog"]}):c>0?e.jsx("p",{className:"text-[11px] text-status-success",children:"Flowing — queue clear"}):e.jsx("p",{className:"text-[11px] 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-[9px] text-text-quaternary uppercase tracking-wider",children:"Pending"}),e.jsx("span",{className:`text-xs font-mono tabular-nums ${i>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:i})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:["Resolved · ",l]}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:c})]}),p>0&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Priority"}),e.jsx("span",{className:"text-xs font-mono tabular-nums font-semibold",style:{color:A},children:p})]})]}),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-[8px] text-text-quaternary uppercase tracking-wider flex-1",children:"Station"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"pend"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"clmd"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"res"})]})}),e.jsx("div",{className:"space-y-0.5",children:n.map(o=>{const x=a.get(o.role),y=(x==null?void 0:x.pending)??0,b=(x==null?void 0:x.claimed)??0,g=(x==null?void 0:x.resolved)??0,v=(x==null?void 0:x.priority_count)??0,S=o.title||o.role,N=v>0,M=y>0;return e.jsxs("div",{className:"flex items-center gap-2 py-0.5",children:[e.jsxs("span",{className:`text-[10px] font-mono flex-1 truncate ${M?"text-text-primary":"text-text-quaternary"}`,children:[S,N&&e.jsxs("span",{className:"ml-1 text-[8px] font-semibold",style:{color:A},children:[v," PRIORITY"]})]}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${M?"text-text-primary font-semibold":"text-text-quaternary"}`,children:y>0?y:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${b>0?"text-accent":"text-text-quaternary"}`,children:b>0?b:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${g>0?"text-text-secondary":"text-text-quaternary"}`,children:g>0?g:"—"})]},o.role)})}),e.jsx("p",{className:"text-[9px] text-text-quaternary mt-5",children:"Select a station for queue detail."})]})}function Ue({role:t,allMetrics:n,orderedRoles:l,globalPeriod:i,onClose:c}){return e.jsx("div",{className:"w-[280px] shrink-0 px-6 py-8 flex flex-col overflow-y-auto min-h-0",children:t?e.jsx(Ve,{role:t,globalPeriod:i,onClose:c}):e.jsx(Ge,{allMetrics:n,orderedRoles:l,period:i})})}const ke=`${X}14`,$e=`${D}14`,we=`${O}14`,_e=`${q}14`,Qe=["15m","1h","24h","7d","30d"],Xe={"15m":.25,"1h":1,"24h":24,"7d":168,"30d":720};function Re(t){const n=t.filter(a=>a.ops_visible),l=new Map(n.map(a=>[a.role,a])),i=new Set,p=n.filter(a=>!a.parent_role||!l.has(a.parent_role)).map(a=>{const d=[],u=[{role:a,depth:0}];for(;u.length>0;){const o=u.shift();i.has(o.role.role)||(i.add(o.role.role),d.push(o),n.filter(x=>x.parent_role===o.role.role&&!i.has(x.role)).forEach(x=>u.push({role:x,depth:o.depth+1})))}return{origin:a,stations:d}});return n.filter(a=>!i.has(a.role)).forEach(a=>p.push({origin:a,stations:[{role:a,depth:0}]})),p.sort((a,d)=>d.stations.length-a.stations.length||a.origin.role.localeCompare(d.origin.role))}function fe(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function Ze(t,n){if(!n||t===0)return{pct:null,color:"bg-surface-border"};const l=t/n,i=Math.round(l*100),c=l>1?"bg-status-warning":l<.2?"bg-status-draft":"bg-status-success";return{pct:i,color:c}}function Ke({role:t,metric:n,selected:l,onClick:i}){var b;const c=(n==null?void 0:n.pending)??0,p=(n==null?void 0:n.claimed)??0,a=(n==null?void 0:n.resolved)??0,d=(n==null?void 0:n.priority_count)??0,u=t.target_per_hour??null,{pct:o,color:x}=Ze(c,u),y=o!=null?Math.min(100,o):0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{role:"button",tabIndex:0,onClick:i,onKeyDown:g=>{g.key==="Enter"&&i()},className:`grid items-center gap-4 pr-3 cursor-pointer transition-colors ${l?"border-l-2 !border-l-accent pl-2.5":"pl-3"}`,style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0 py-1.5",children:[e.jsx("span",{className:"font-mono font-bold text-text-primary truncate text-[11px]",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-tertiary truncate",children:t.title}),(((b=t.upstream_roles)==null?void 0:b.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("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:ke},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${u!=null?"text-text-secondary":"text-text-quaternary"}`,children:u??"—"})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:$e},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${c>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:c})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:we},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${p>0?"text-accent font-semibold":"text-text-quaternary"}`,children:p})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:_e},children:e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-quaternary",children:a})}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.wait.p99)??null)}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.work.p99)??null)}),e.jsxs("div",{className:"flex items-center gap-2 py-1.5",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 ${x}`,style:{width:`${y}%`}})}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums ${o!=null&&o>100?"text-status-warning font-semibold":"text-text-quaternary"}`,children:o!=null?`${o}%`:"—"})]})]}),d>0&&e.jsxs("div",{className:"flex items-center gap-1.5 pb-2 pl-3.5",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full dot-ring shrink-0",style:{backgroundColor:ve}}),e.jsxs(Q,{to:xe(t),className:"text-[10px] hover:underline",style:{color:A},onClick:g=>g.stopPropagation(),children:[d," priority — pull oldest first →"]})]})]})}function Je(){const t=[{label:"ROLE"},{label:"TARGET/H",band:ke,hue:X},{label:"PENDING",band:$e,hue:D},{label:"CLAIMED",band:we,hue:O},{label:"RESOLVED",band:_e,hue:q},{label:"P99 WAIT"},{label:"P99 WORK"},{label:"TREND"}];return e.jsx("div",{className:"grid items-center gap-4 px-3 border-b border-surface-border mb-0.5",style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:t.map((n,l)=>n.band?e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:n.band},children:e.jsx("span",{className:"text-[9px] font-semibold uppercase tracking-wider",style:{color:n.hue},children:n.label})},n.label):e.jsx("span",{className:`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary py-1.5 ${l>0&&l<7?"text-right":""}`,children:n.label},n.label))})}function et(){var Y;const[t,n]=w.useState("1h"),[l,i]=w.useState(null),[c,p]=w.useState(!1);Se();const{data:a,isLoading:d,refetch:u}=Pe(),{data:o,isLoading:x,refetch:y}=ye(t),b=(a==null?void 0:a.roles)??[],g=(o==null?void 0:o.stations)??[],v=w.useMemo(()=>Re(b),[b]),[S,N]=Fe(),M=S.get("fragment"),_=v.find(r=>r.origin.role===M)??v[0]??null,R=w.useCallback(r=>{i(null),N(m=>{const L=new URLSearchParams(m);return L.set("fragment",r),L})},[N]),k=(_==null?void 0:_.stations)??[],B=w.useMemo(()=>new Set(k.map(({role:r})=>r.role)),[k]),z=w.useMemo(()=>g.filter(r=>B.has(r.role)),[g,B]),H=w.useMemo(()=>k.map(({role:r})=>({role:r.role,title:r.title,parent_role:r.parent_role,target_per_hour:r.target_per_hour??null,upstream_roles:r.upstream_roles??[],metric:g.find(m=>m.role===r.role)})),[k,g]),Z=w.useCallback(r=>{const m=v.find(L=>L.stations.some(({role:s})=>s.role===r));m&&R(m.origin.role)},[v,R]),F=Oe(),K=w.useCallback(r=>{const m=b.find(L=>L.role===r);m&&F(xe(m))},[b,F]),E=((Y=k.find(({role:r})=>r.role===l))==null?void 0:Y.role)??null,J=d||x,ee=async()=>{p(!0),await Promise.all([u(),y()]),p(!1)},W=r=>i(m=>m===r?null:r),te=e.jsx("div",{className:"flex items-center gap-0.5",children:Qe.map(r=>e.jsx("button",{onClick:()=>n(r),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${t===r?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:r},r))});return e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsx(Ce,{title:"Pace Board",docsHash:"#docs:dashboard.md:pace-board",center:te,actions:e.jsx("button",{onClick:ee,disabled:c,className:"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40",children:e.jsx(Le,{className:`w-3.5 h-3.5 ${c?"animate-spin":""}`})})}),J?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(r=>e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-full"},r))})]}):v.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(Q,{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:[v.length>1&&e.jsx("div",{className:"flex items-center gap-0.5 px-4 pt-2",children:v.map(r=>{const m=r.origin.role===(_==null?void 0:_.origin.role);return e.jsxs("button",{onClick:()=>R(r.origin.role),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${m?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:[r.origin.title??r.origin.role,e.jsx("span",{className:`ml-1.5 tabular-nums ${m?"text-accent/60":"text-text-quaternary/60"}`,children:r.stations.length})]},r.origin.role)})}),e.jsxs("div",{className:"flex-1 min-h-0 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 p-4",children:e.jsx(ze,{stations:H,selectedRole:l,onSelect:W,onUpstreamSelect:Z,onPrioritySelect:K,periodHours:Xe[t]})}),e.jsx("div",{className:"w-px bg-surface-border shrink-0 self-stretch"}),e.jsx(Ue,{role:E,allMetrics:z,orderedRoles:k.map(r=>r.role),globalPeriod:t,onClose:()=>i(null)})]}),e.jsxs("div",{className:"max-h-[45vh] flex-none flex flex-col border-t border-surface-border overflow-hidden",children:[e.jsx(Je,{}),e.jsx("div",{className:"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30",children:k.map(({role:r})=>e.jsx(Ke,{role:r,metric:g.find(m=>m.role===r.role),selected:l===r.role,onClick:()=>W(r.role)},r.role))})]})]})]})}const it=Object.freeze(Object.defineProperty({__proto__:null,OperationsPage:et,buildFragments:Re},Symbol.toStringTag,{value:"Module"}));export{it as O,ze as P,Re as b,xe as p};
|
|
2
|
-
//# sourceMappingURL=OperationsPage-BaM-QDhy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OperationsPage-BaM-QDhy.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';\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 /** Priority-badge click — open the station's queue oldest-first by its age facet. */\n onPrioritySelect?: (role: string) => void;\n /** Selected window length in hours — target count = target_per_hour × this. */\n periodHours: number;\n}\n\n// ── SVG layout ────────────────────────────────────────────────────────────────\n\nconst W = 800;\nconst H = 270;\nconst ML = 40;\nconst MR = 76;\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// ── 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.\nexport const ACTIVE_COLOR = '#6366f1'; // claimed, being worked right now — indigo\nexport const QUEUED_COLOR = '#0ea5e9'; // pending and unclaimed, waiting in the queue — sky\nexport const RESOLVED_COLOR = '#1E3A8A'; // done — navy, matches the default theme accent\nexport const TARGET_COLOR = '#059669'; // target pace line — emerald\n\n// Priority — unclaimed items past the role's age threshold, the count the\n// floor pulls to the front of the rack. Powder blue for the badge circle;\n// the count text uses the same hue deepened enough to read at chart sizes.\nexport const PRIORITY_COLOR = '#b0e0e6';\nexport const PRIORITY_TEXT_COLOR = '#5b93ad';\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, onPrioritySelect, periodHours }: 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 const yScale = (v: number) => MT + chartH * (1 - Math.max(0, Math.min(v, maxVal)) / 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 viewBox={`0 0 ${W} ${H}`} className=\"w-full h-full\" preserveAspectRatio=\"xMidYMid meet\" style={{ display: 'block' }}>\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} stroke=\"#e2e8f0\" strokeWidth={0.75} />;\n })}\n\n {/* Area under the actual curve */}\n {areaPath && <path d={areaPath} fill={RESOLVED_COLOR} opacity={0.05} style={{ 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} fill={ACTIVE_COLOR} opacity={0.12} style={{ transition: `d ${EASE}` }} />\n )}\n {queuedBandPath && (\n <path d={queuedBandPath} fill={QUEUED_COLOR} opacity={0.12} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Target — thin emerald reference curve at the window's expected count */}\n {targetLinePath && (\n <path d={targetLinePath} fill=\"none\" stroke={TARGET_COLOR} strokeWidth={0.5} strokeLinejoin=\"round\" opacity={0.9} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Per-role target for the window (target_per_hour × duration), just above the red line */}\n {targetPts.map((tp) => (\n <text\n key={`tval-${tp.idx}`}\n x={tp.x}\n y={tp.y - 3.5}\n textAnchor=\"middle\"\n fontSize={6.5}\n fill={TARGET_COLOR}\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"500\"\n opacity={0.7}\n style={{ transition: `y ${EASE}` }}\n >\n {Math.round(tp.expected as number)}\n </text>\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\" stroke={QUEUED_COLOR} strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.55} style={{ 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\" stroke={ACTIVE_COLOR} strokeWidth={0.75} strokeDasharray=\"2 3\" strokeLinecap=\"round\" opacity={0.5} style={{ transition: `d ${EASE}` }} />\n )}\n\n {/* Actual — thin solid, primary line */}\n {actualSplinePath && (\n <path d={actualSplinePath} fill=\"none\" stroke={RESOLVED_COLOR} strokeWidth={0.6} strokeLinejoin=\"round\" strokeLinecap=\"round\" opacity={0.65} style={{ 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 fill={hasWaiting ? QUEUED_COLOR : '#64748b'}\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"600\"\n style={{ 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} fill={ACTIVE_COLOR} opacity={0.85} />\n {ap.active > 0 && (\n <text y={ar + 9} textAnchor=\"middle\" fontSize={7.5} fill={ACTIVE_COLOR} fontFamily=\"ui-monospace, monospace\" fontWeight=\"600\">\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} priority` : '');\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={() => onSelect(s.role)}\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} fill={RESOLVED_COLOR} opacity={0.9} style={{ transition: `r ${EASE}` }} />\n {row.expected != null && (\n <text y={-r - 5} textAnchor=\"middle\" fontSize={9.5} fill={RESOLVED_COLOR} fontFamily=\"ui-monospace, monospace\" fontWeight=\"500\">\n {compact(row.actual)}\n </text>\n )}\n {isHovered && !isSelected && <circle r={r + 3} fill=\"none\" stroke={RESOLVED_COLOR} strokeWidth={1} opacity={0.4} />}\n {isSelected && <circle r={r + 4} fill=\"none\" stroke=\"#6366f1\" strokeWidth={2} />}\n </g>\n\n {/* Priority badge — powder blue circle carrying the count of\n unclaimed items past the role's age threshold. Clicking opens\n the station's queue oldest-first by the priority facet so the\n runner grabs the top few and moves them to the front. */}\n {row.priorityCount > 0 && (\n <g\n transform={`translate(${x + 15} ${Math.max(cy - 15, 12)})`}\n onClick={(e) => {\n e.stopPropagation();\n onPrioritySelect?.(s.role);\n }}\n style={{ cursor: 'pointer', transition: `transform ${EASE}` }}\n >\n <title>{`${row.priorityCount} past the priority threshold — click to pull oldest first`}</title>\n <circle\n r={6.5 + (String(row.priorityCount).length - 1) * 2}\n fill={PRIORITY_COLOR}\n opacity={0.9}\n />\n <text\n y={2.5}\n textAnchor=\"middle\"\n fontSize={7}\n fill={PRIORITY_TEXT_COLOR}\n fontFamily=\"ui-monospace, monospace\"\n fontWeight=\"700\"\n >\n {row.priorityCount}\n </text>\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\" stroke=\"#94a3b8\" strokeWidth={1} strokeDasharray=\"2 2\" strokeLinecap=\"round\" />\n <path d=\"M 0 -2 L 8 -2\" stroke=\"#94a3b8\" strokeWidth={1} strokeLinecap=\"round\" />\n <path d=\"M 8 -2 l -3.5 -2 v 4 z\" fill=\"#94a3b8\" />\n </g>\n )}\n\n {/* Station labels (fixed at the floor) */}\n <text x={x} y={bottom + 14} textAnchor=\"middle\" fontSize={9.5} fill=\"#475569\" fontFamily=\"ui-sans-serif, sans-serif\" fontWeight=\"500\">\n {s.title ?? s.role}\n </text>\n <text x={x} y={bottom + 26} textAnchor=\"middle\" fontSize={8} fill=\"#94a3b8\" fontFamily=\"ui-monospace, monospace\">\n {s.role}\n </text>\n\n {/* Tooltip */}\n {isHovered && (\n <g>\n <rect x={tipX - tipW / 2} y={tipY - 16} width={tipW} height={19} rx={3} fill=\"#0f172a\" opacity={0.88} />\n <text x={tipX} y={tipY - 3} textAnchor=\"middle\" fontSize={8.5} fill=\"white\" 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} fill=\"#94a3b8\" 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 fill={l.color}\n fontFamily=\"ui-sans-serif, sans-serif\"\n fontWeight={l.key === 'actual' ? 700 : 500}\n opacity={l.key === 'active' ? 0.85 : 1}\n style={{ transition: `y ${EASE}` }}\n >\n {l.text}\n </text>\n ))}\n </svg>\n );\n}\n","import type { RoleDetail } from '../../api/roles';\n\n/**\n * Deep link to the available-escalations queue ordered oldest-first by the\n * role's priority age origin: the metadata facet when configured (faceted\n * orderBy, nulls last), else created_at. Mirrors the priority_count SQL so\n * the counted items sit at the top of the list the runner opens.\n */\nexport function priorityQueueLink(role: Pick<RoleDetail, 'role' | 'priority_facet'>): string {\n const base = `/escalations/available?role=${encodeURIComponent(role.role)}`;\n if (role.priority_facet) {\n const orderBy = JSON.stringify([{ field: `metadata.${role.priority_facet}`, direction: 'asc' }]);\n return `${base}&orderBy=${encodeURIComponent(orderBy)}`;\n }\n return `${base}&sort_by=created_at&order=asc`;\n}\n","import { useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { X, ExternalLink } 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 { priorityQueueLink } from './priority-link';\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\">\n <span className=\"text-[10px] 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-[10px] 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-1\">\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-mono font-bold text-text-primary\">{role.role}</span>\n {role.title && (\n <span className=\"text-[10px] text-text-secondary\">{role.title}</span>\n )}\n </div>\n {role.description && (\n <p className=\"text-[10px] text-text-tertiary mt-0.5 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 <Link\n to={`/admin/roles/${encodeURIComponent(role.role)}`}\n className=\"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4\"\n >\n <ExternalLink className=\"w-2.5 h-2.5\" />\n Edit in Roles\n </Link>\n\n <PeriodSelector period={period} onChange={setPeriod} />\n\n {/* Capacity settings */}\n {(targetPerHour || slaMinutes || workerCount) && (\n <div className=\"flex items-center gap-4 mb-4 text-[10px]\">\n {targetPerHour && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{targetPerHour}/h</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">target</div>\n </div>\n )}\n {slaMinutes && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{slaMinutes}m</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">SLA</div>\n </div>\n )}\n {workerCount && (\n <div>\n <div className=\"font-mono font-semibold text-text-primary\">{workerCount}</div>\n <div className=\"text-text-quaternary uppercase tracking-wider text-[8.5px]\">workers</div>\n </div>\n )}\n </div>\n )}\n\n {/* Two-column wait / work grid */}\n <div className=\"grid grid-cols-2 gap-x-3\">\n <div>\n <p className=\"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5\">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-[9px] text-text-quaternary uppercase tracking-wider mb-1.5\">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-2 mt-3 space-y-1.5\">\n {slaMinutes && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-[10px] 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={priorityQueueLink(role)}\n className=\"flex items-center justify-between group\"\n >\n <span className=\"text-[10px]\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {metric.priority_count} priority — 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 {/* View queue — `role=` drives the visible filter bar on the escalations\n page (the JSON `roles=` facet param filters silently and leaves the\n bar reading \"all roles\"). */}\n <div className=\"mt-4 border-t border-surface-border/40 pt-3\">\n <Link\n to={`/escalations/available?role=${encodeURIComponent(role.role)}`}\n className=\"flex items-center gap-1.5 text-[10px] text-accent hover:underline\"\n >\n View full queue\n <ExternalLink className=\"w-2.5 h-2.5\" />\n </Link>\n </div>\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-[9px] 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-[11px]\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {stationsAtRisk} station{stationsAtRisk > 1 ? 's' : ''} with priority items\n </p>\n ) : stationsWithLoad > 0 ? (\n <p className=\"text-[11px] text-status-warning\">\n {stationsWithLoad} station{stationsWithLoad > 1 ? 's' : ''} with backlog\n </p>\n ) : totalResolved > 0 ? (\n <p className=\"text-[11px] text-status-success\">Flowing — queue clear</p>\n ) : (\n <p className=\"text-[11px] 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-[9px] 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-[9px] 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-[9px] 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-[8px] text-text-quaternary uppercase tracking-wider flex-1\">Station</span>\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">pend</span>\n <span className=\"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0\">clmd</span>\n <span className=\"text-[8px] 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-[10px] 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-[8px] font-semibold\" style={{ color: PRIORITY_TEXT_COLOR }}>\n {priorityCount} PRIORITY\n </span>\n )}\n </span>\n <span\n className={`text-[10px] 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-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${claimed > 0 ? 'text-accent' : 'text-text-quaternary'}`}>\n {claimed > 0 ? claimed : '—'}\n </span>\n <span className={`text-[10px] 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-[9px] 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-6 py-8 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, useMemo, useCallback } from 'react';\nimport { Link, useNavigate, useSearchParams } from 'react-router-dom';\nimport { GitMerge, RefreshCw } from 'lucide-react';\nimport { useRoleDetails, 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_COLOR,\n PRIORITY_TEXT_COLOR,\n type ChartStation,\n} from './PaceChart';\nimport { StationDetailPanel } from './StationDetailPanel';\nimport { priorityQueueLink } from './priority-link';\n\n// Column band tints — the same hues as the chart's queue-composition bands\n// (~8% alpha), so PENDING/ACTIVE/RESOLVED in the table visually continue the\n// sky/indigo/sage story told above.\nconst TARGET_BAND = `${TARGET_COLOR}14`;\nconst PENDING_BAND = `${QUEUED_COLOR}14`;\nconst ACTIVE_BAND = `${ACTIVE_COLOR}14`;\nconst RESOLVED_BAND = `${RESOLVED_COLOR}14`;\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// ── Station table row ─────────────────────────────────────────────────────────\n\nfunction StationRow({\n role,\n metric,\n selected,\n onClick,\n}: {\n role: RoleDetail;\n metric: StationMetric | undefined;\n selected: boolean;\n onClick: () => void;\n}) {\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 return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => { if (e.key === 'Enter') onClick(); }}\n className={`grid items-center gap-4 pr-3 cursor-pointer transition-colors ${\n selected ? 'border-l-2 !border-l-accent pl-2.5' : 'pl-3'\n }`}\n style={{ gridTemplateColumns: '1fr 64px 56px 56px 60px 72px 72px 104px' }}\n >\n {/* Role name */}\n <div className=\"flex items-center gap-1.5 min-w-0 py-1.5\">\n <span\n className=\"font-mono font-bold text-text-primary truncate text-[11px]\"\n >\n {role.role}\n </span>\n {role.title && (\n <span className=\"text-[10px] text-text-tertiary truncate\">{role.title}</span>\n )}\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\n {/* Target / hour — emerald band, same hue as the chart's target line */}\n <div\n className=\"self-stretch flex items-center justify-end px-1.5\"\n style={{ backgroundColor: TARGET_BAND }}\n >\n <span\n className={`text-xs font-mono tabular-nums ${\n target != null ? 'text-text-secondary' : 'text-text-quaternary'\n }`}\n >\n {target != null ? target : '—'}\n </span>\n </div>\n\n {/* Pending — sky band, same hue as the chart's waiting band */}\n <div\n className=\"self-stretch flex items-center justify-end px-1.5\"\n style={{ backgroundColor: PENDING_BAND }}\n >\n <span\n className={`text-xs font-mono tabular-nums ${\n pending > 0 ? 'text-text-primary font-semibold' : 'text-text-quaternary'\n }`}\n >\n {pending}\n </span>\n </div>\n\n {/* Active — indigo band, same hue as the chart's worked band */}\n <div\n className=\"self-stretch flex items-center justify-end px-1.5\"\n style={{ backgroundColor: ACTIVE_BAND }}\n >\n <span\n className={`text-xs font-mono tabular-nums ${\n claimed > 0 ? 'text-accent font-semibold' : 'text-text-quaternary'\n }`}\n >\n {claimed}\n </span>\n </div>\n\n {/* Resolved — sage band, same hue as the chart's actual curve */}\n <div\n className=\"self-stretch flex items-center justify-end px-1.5\"\n style={{ backgroundColor: RESOLVED_BAND }}\n >\n <span className=\"text-xs font-mono tabular-nums text-text-quaternary\">\n {resolved}\n </span>\n </div>\n\n {/* P99 wait */}\n <span className=\"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary\">\n {fmtMin(metric?.wait.p99 ?? null)}\n </span>\n\n {/* P99 work */}\n <span className=\"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary\">\n {fmtMin(metric?.work.p99 ?? null)}\n </span>\n\n {/* Load mini-bar */}\n <div className=\"flex items-center gap-2 py-1.5\">\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-[10px] 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 </div>\n\n {/* Priority sub-row — unclaimed items past the role's age threshold */}\n {priorityCount > 0 && (\n <div className=\"flex items-center gap-1.5 pb-2 pl-3.5\">\n <span\n className=\"w-2.5 h-2.5 rounded-full dot-ring shrink-0\"\n style={{ backgroundColor: PRIORITY_COLOR }}\n />\n <Link\n to={priorityQueueLink(role)}\n className=\"text-[10px] hover:underline\"\n style={{ color: PRIORITY_TEXT_COLOR }}\n onClick={(e) => e.stopPropagation()}\n >\n {priorityCount} priority — pull oldest first →\n </Link>\n </div>\n )}\n </>\n );\n}\n\n// ── Table header ──────────────────────────────────────────────────────────────\n\nfunction TableHead() {\n // Queue-state columns carry the chart's band hue into the table.\n const cols: { label: string; band?: string; hue?: string }[] = [\n { label: 'ROLE' },\n { label: 'TARGET/H', band: TARGET_BAND, hue: TARGET_COLOR },\n { label: 'PENDING', band: PENDING_BAND, hue: QUEUED_COLOR },\n { label: 'CLAIMED', band: ACTIVE_BAND, hue: ACTIVE_COLOR },\n { label: 'RESOLVED', band: RESOLVED_BAND, hue: RESOLVED_COLOR },\n { label: 'P99 WAIT' },\n { label: 'P99 WORK' },\n { label: 'TREND' },\n ];\n return (\n <div\n className=\"grid items-center gap-4 px-3 border-b border-surface-border mb-0.5\"\n style={{ gridTemplateColumns: '1fr 64px 56px 56px 60px 72px 72px 104px' }}\n >\n {cols.map((col, i) =>\n col.band ? (\n <div\n key={col.label}\n className=\"self-stretch flex items-center justify-end px-1.5\"\n style={{ backgroundColor: col.band }}\n >\n <span\n className=\"text-[9px] font-semibold uppercase tracking-wider\"\n style={{ color: col.hue }}\n >\n {col.label}\n </span>\n </div>\n ) : (\n <span\n key={col.label}\n className={`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary py-1.5 ${\n i > 0 && i < 7 ? 'text-right' : ''\n }`}\n >\n {col.label}\n </span>\n ),\n )}\n </div>\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\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 // 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 // Priority-badge click — open the station's queue oldest-first by its facet.\n const navigate = useNavigate();\n const handlePrioritySelect = useCallback(\n (roleName: string) => {\n const detail = roles.find((r) => r.role === roleName);\n if (detail) navigate(priorityQueueLink(detail));\n },\n [roles, navigate],\n );\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-[10px] 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) → flexible middle → fixed table (30vh) */\n <div className=\"flex flex-col flex-1 min-h-0\">\n\n {/* Sequence picker — one tab per fragment, named by its origin role.\n Only rendered when there is more than one story to tell. */}\n {fragments.length > 1 && (\n <div className=\"flex items-center gap-0.5 px-4 pt-2\">\n {fragments.map((f) => {\n const isActive = f.origin.role === activeFragment?.origin.role;\n return (\n <button\n key={f.origin.role}\n onClick={() => selectFragment(f.origin.role)}\n className={`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${\n isActive\n ? 'bg-accent/10 text-accent font-semibold'\n : 'text-text-quaternary hover:text-text-secondary'\n }`}\n >\n {f.origin.title ?? f.origin.role}\n <span className={`ml-1.5 tabular-nums ${isActive ? 'text-accent/60' : 'text-text-quaternary/60'}`}>\n {f.stations.length}\n </span>\n </button>\n );\n })}\n </div>\n )}\n\n {/* Middle row: flexible height — SVG fills left, sidebar fixed-width right */}\n <div className=\"flex-1 min-h-0 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 p-4\">\n <PaceChart\n stations={chartStations}\n selectedRole={selectedRole}\n onSelect={handleSelect}\n onUpstreamSelect={handleUpstreamSelect}\n onPrioritySelect={handlePrioritySelect}\n periodHours={PERIOD_HOURS[period]}\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: sized to its rows up to 45vh — header sticky, rows scroll */}\n <div className=\"max-h-[45vh] flex-none flex flex-col border-t border-surface-border overflow-hidden\">\n <TableHead />\n <div className=\"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30\">\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 onClick={() => handleSelect(role.role)}\n />\n ))}\n </div>\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","ACTIVE_COLOR","QUEUED_COLOR","RESOLVED_COLOR","TARGET_COLOR","PRIORITY_COLOR","PRIORITY_TEXT_COLOR","MIN_GAP","spreadLabels","labels","maxY","sorted","b","prev","placed","l","labelY","overflow","PaceChart","stations","selectedRole","onSelect","onUpstreamSelect","onPrioritySelect","periodHours","hoveredIdx","setHoveredIdx","useState","xOf","xMap","rows","target","expected","_a","_b","_c","_d","hasTargets","r","maxVal","yScale","v","withTarget","targetPts","actualPts","activePts","pendingPts","targetLinePath","actualSplinePath","activeSplinePath","pendingSplinePath","lastActual","lastTarget","lastActive","lastPending","areaPath","hasQueue","workedBandPath","queuedBandPath","showActiveLabel","showQueuedLabel","endLabels","px","cx","jsx","tp","ap","ar","hasWaiting","pendingLabelY","row","s","x","cy","isSelected","isHovered","tooltip","tipW","tipX","tipY","jsxs","e","priorityQueueLink","role","base","orderBy","PERIODS","isPeriod","fmt","min","MetricRow","label","value","PeriodSelector","period","onChange","RoleView","globalPeriod","onClose","setPeriod","data","useStationMetrics","metric","slaMinutes","targetPerHour","workerCount","Fragment","X","Link","ExternalLink","OverviewPanel","allMetrics","orderedRoles","totalPending","m","totalResolved","totalPriority","metricByRole","stationsAtRisk","stationsWithLoad","pending","claimed","resolved","priorityCount","hasAlert","hasLoad","StationDetailPanel","TARGET_BAND","PENDING_BAND","ACTIVE_BAND","RESOLVED_BAND","PERIOD_HOURS","buildFragments","roles","opsRoles","roleMap","visited","fragments","root","queue","item","c","fmtMin","loadBar","ratio","pct","color","StationRow","selected","onClick","barWidth","GitMerge","TableHead","cols","col","OperationsPage","setSelectedRole","refreshing","setRefreshing","useStationMetricsEvents","roleData","rolesLoading","refetchRoles","useRoleDetails","metricsData","metricsLoading","refetchMetrics","metrics","useMemo","searchParams","setSearchParams","useSearchParams","fragmentParam","activeFragment","f","selectFragment","useCallback","origin","ordered","fragmentRoleSet","fragmentMetrics","chartStations","handleUpstreamSelect","upstreamRole","navigate","useNavigate","handlePrioritySelect","roleName","detail","selectedRoleDetail","isLoading","handleRefresh","handleSelect","periodSelector","PageHeader","RefreshCw","isActive","o"],"mappings":"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"}
|