@hotmeshio/long-tail 0.25.1 → 0.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/build/api/escalations/batch-flag.d.ts +19 -0
  2. package/build/api/escalations/batch-flag.js +138 -0
  3. package/build/api/escalations/index.d.ts +2 -0
  4. package/build/api/escalations/index.js +8 -1
  5. package/build/api/escalations/roster-action.d.ts +37 -0
  6. package/build/api/escalations/roster-action.js +179 -0
  7. package/build/api/scan-codes/choice-roles.d.ts +3 -0
  8. package/build/api/scan-codes/choice-roles.js +48 -0
  9. package/build/api/scan-codes/choice.js +8 -0
  10. package/build/api/scan-codes/fill-labels.d.ts +15 -0
  11. package/build/api/scan-codes/fill-labels.js +99 -0
  12. package/build/api/scan-codes/hold.js +1 -0
  13. package/build/api/scan-codes/present.js +7 -2
  14. package/build/api/scan-codes/verb-fill.js +22 -5
  15. package/build/routes/escalations/resolve.js +45 -0
  16. package/build/sdk/index.d.ts +11 -0
  17. package/build/sdk/index.js +3 -0
  18. package/build/services/escalation/batch-flag.d.ts +25 -0
  19. package/build/services/escalation/batch-flag.js +51 -0
  20. package/build/services/escalation/index.d.ts +1 -0
  21. package/build/services/escalation/index.js +1 -0
  22. package/build/services/escalation/roster.d.ts +3 -1
  23. package/build/services/escalation/roster.js +6 -1
  24. package/build/services/scan-code/validate-subject.js +16 -0
  25. package/build/services/scan-code/validate.js +7 -0
  26. package/build/shared/form-validation/index.d.ts +1 -0
  27. package/build/shared/form-validation/index.js +1 -0
  28. package/build/shared/form-validation/x-lt-actions.d.ts +80 -0
  29. package/build/shared/form-validation/x-lt-actions.js +133 -0
  30. package/build/shared/form-validation/x-lt-roster.d.ts +2 -0
  31. package/build/shared/form-validation/x-lt-roster.js +28 -3
  32. package/build/tsconfig.tsbuildinfo +1 -1
  33. package/build/types/escalation.d.ts +19 -0
  34. package/build/types/escalation.js +4 -0
  35. package/build/types/index.d.ts +1 -1
  36. package/build/types/scan-choice.d.ts +9 -0
  37. package/build/types/scan-code/response.d.ts +2 -0
  38. package/build/types/scan-code/rule.d.ts +17 -0
  39. package/dashboard/dist/assets/{AdminDashboard-C_0ShYrm.js → AdminDashboard-B8MqNLiB.js} +2 -2
  40. package/dashboard/dist/assets/{AdminDashboard-C_0ShYrm.js.map → AdminDashboard-B8MqNLiB.js.map} +1 -1
  41. package/dashboard/dist/assets/{AgentConfigPage-Cc9dI8lO.js → AgentConfigPage-Yg81Eiuo.js} +2 -2
  42. package/dashboard/dist/assets/{AgentConfigPage-Cc9dI8lO.js.map → AgentConfigPage-Yg81Eiuo.js.map} +1 -1
  43. package/dashboard/dist/assets/{AgentDetailPage-Sdmt5j-k.js → AgentDetailPage-ZUGYEONO.js} +3 -3
  44. package/dashboard/dist/assets/{AgentDetailPage-Sdmt5j-k.js.map → AgentDetailPage-ZUGYEONO.js.map} +1 -1
  45. package/dashboard/dist/assets/{AgentsPage-BuzXc1mg.js → AgentsPage-CZIWMwak.js} +2 -2
  46. package/dashboard/dist/assets/{AgentsPage-BuzXc1mg.js.map → AgentsPage-CZIWMwak.js.map} +1 -1
  47. package/dashboard/dist/assets/AvailableEscalationsPage-BVtpc4xO.js +2 -0
  48. package/dashboard/dist/assets/{AvailableEscalationsPage-DU84iaBN.js.map → AvailableEscalationsPage-BVtpc4xO.js.map} +1 -1
  49. package/dashboard/dist/assets/{BotPicker-Ci5PUrkl.js → BotPicker-8bf0yOCT.js} +2 -2
  50. package/dashboard/dist/assets/{BotPicker-Ci5PUrkl.js.map → BotPicker-8bf0yOCT.js.map} +1 -1
  51. package/dashboard/dist/assets/{BulkAssignModal-PLx7SLvF.js → BulkAssignModal-B2lr1J6P.js} +2 -2
  52. package/dashboard/dist/assets/{BulkAssignModal-PLx7SLvF.js.map → BulkAssignModal-B2lr1J6P.js.map} +1 -1
  53. package/dashboard/dist/assets/{CapabilitiesPage-DuSfgr6i.js → CapabilitiesPage-BQQBR2sj.js} +2 -2
  54. package/dashboard/dist/assets/{CapabilitiesPage-DuSfgr6i.js.map → CapabilitiesPage-BQQBR2sj.js.map} +1 -1
  55. package/dashboard/dist/assets/{CollapsibleSection-KOO37qs6.js → CollapsibleSection-DFW8TYyU.js} +2 -2
  56. package/dashboard/dist/assets/{CollapsibleSection-KOO37qs6.js.map → CollapsibleSection-DFW8TYyU.js.map} +1 -1
  57. package/dashboard/dist/assets/{ConfirmDeleteModal-CI1eE-Nr.js → ConfirmDeleteModal-DGlOlQAo.js} +2 -2
  58. package/dashboard/dist/assets/{ConfirmDeleteModal-CI1eE-Nr.js.map → ConfirmDeleteModal-DGlOlQAo.js.map} +1 -1
  59. package/dashboard/dist/assets/{ConnectedAppsPage-DZgbCC0G.js → ConnectedAppsPage-CIM9gzHj.js} +2 -2
  60. package/dashboard/dist/assets/{ConnectedAppsPage-DZgbCC0G.js.map → ConnectedAppsPage-CIM9gzHj.js.map} +1 -1
  61. package/dashboard/dist/assets/{CountdownTimer-C-Ngk8oq.js → CountdownTimer-BD_iWm6S.js} +2 -2
  62. package/dashboard/dist/assets/{CountdownTimer-C-Ngk8oq.js.map → CountdownTimer-BD_iWm6S.js.map} +1 -1
  63. package/dashboard/dist/assets/{CredentialsPage-BR7s6eVu.js → CredentialsPage-CTtUkvoD.js} +2 -2
  64. package/dashboard/dist/assets/{CredentialsPage-BR7s6eVu.js.map → CredentialsPage-CTtUkvoD.js.map} +1 -1
  65. package/dashboard/dist/assets/{CronLabel-BQ7Y1Nfd.js → CronLabel-rZMptkYQ.js} +2 -2
  66. package/dashboard/dist/assets/{CronLabel-BQ7Y1Nfd.js.map → CronLabel-rZMptkYQ.js.map} +1 -1
  67. package/dashboard/dist/assets/{DropZone-DCZpORx-.js → DropZone-u1JGfR9G.js} +2 -2
  68. package/dashboard/dist/assets/{DropZone-DCZpORx-.js.map → DropZone-u1JGfR9G.js.map} +1 -1
  69. package/dashboard/dist/assets/{ElapsedCell-BT49mUIc.js → ElapsedCell-CmK9gHC5.js} +2 -2
  70. package/dashboard/dist/assets/{ElapsedCell-BT49mUIc.js.map → ElapsedCell-CmK9gHC5.js.map} +1 -1
  71. package/dashboard/dist/assets/{EntityTimelinePanel-BtARWy3R.js → EntityTimelinePanel-Cfg0EMZs.js} +2 -2
  72. package/dashboard/dist/assets/{EntityTimelinePanel-BtARWy3R.js.map → EntityTimelinePanel-Cfg0EMZs.js.map} +1 -1
  73. package/dashboard/dist/assets/{EscalationListSchemaPage-EEBsNpTe.js → EscalationListSchemaPage-CPcbC1-9.js} +2 -2
  74. package/dashboard/dist/assets/{EscalationListSchemaPage-EEBsNpTe.js.map → EscalationListSchemaPage-CPcbC1-9.js.map} +1 -1
  75. package/dashboard/dist/assets/{EscalationSchemaPage-BHSC46Ow.js → EscalationSchemaPage-BsYdNVN7.js} +3 -3
  76. package/dashboard/dist/assets/{EscalationSchemaPage-BHSC46Ow.js.map → EscalationSchemaPage-BsYdNVN7.js.map} +1 -1
  77. package/dashboard/dist/assets/EscalationTimeline-D1enxZDs.js +2 -0
  78. package/dashboard/dist/assets/{EscalationTimeline-BWTUBxUf.js.map → EscalationTimeline-D1enxZDs.js.map} +1 -1
  79. package/dashboard/dist/assets/{EscalationTitleSelect-DOsfY4u8.js → EscalationTitleSelect-DwqhnPKX.js} +2 -2
  80. package/dashboard/dist/assets/{EscalationTitleSelect-DOsfY4u8.js.map → EscalationTitleSelect-DwqhnPKX.js.map} +1 -1
  81. package/dashboard/dist/assets/{EscalationsOverview-Bl8g53x-.js → EscalationsOverview-ChuKh02E.js} +2 -2
  82. package/dashboard/dist/assets/{EscalationsOverview-Bl8g53x-.js.map → EscalationsOverview-ChuKh02E.js.map} +1 -1
  83. package/dashboard/dist/assets/{EventTable-Bjx17W_i.js → EventTable-GqZIKPce.js} +2 -2
  84. package/dashboard/dist/assets/{EventTable-Bjx17W_i.js.map → EventTable-GqZIKPce.js.map} +1 -1
  85. package/dashboard/dist/assets/{FilterBar-Dnse0soD.js → FilterBar-CqPFZkVU.js} +2 -2
  86. package/dashboard/dist/assets/{FilterBar-Dnse0soD.js.map → FilterBar-CqPFZkVU.js.map} +1 -1
  87. package/dashboard/dist/assets/{GraphInvokePage-HQEvbHTd.js → GraphInvokePage-_fB7mtBU.js} +2 -2
  88. package/dashboard/dist/assets/{GraphInvokePage-HQEvbHTd.js.map → GraphInvokePage-_fB7mtBU.js.map} +1 -1
  89. package/dashboard/dist/assets/{HomePage-DizTklVg.js → HomePage-BXA0Cz0F.js} +2 -2
  90. package/dashboard/dist/assets/{HomePage-DizTklVg.js.map → HomePage-BXA0Cz0F.js.map} +1 -1
  91. package/dashboard/dist/assets/{ListToolbar-B0qe2QZ5.js → ListToolbar-Bf4DFITI.js} +2 -2
  92. package/dashboard/dist/assets/{ListToolbar-B0qe2QZ5.js.map → ListToolbar-Bf4DFITI.js.map} +1 -1
  93. package/dashboard/dist/assets/{McpOverview-DmH-qtX9.js → McpOverview-H61XQRji.js} +2 -2
  94. package/dashboard/dist/assets/{McpOverview-DmH-qtX9.js.map → McpOverview-H61XQRji.js.map} +1 -1
  95. package/dashboard/dist/assets/{McpQueryDetailPage-B-qtmwLd.js → McpQueryDetailPage-wbwK8WWq.js} +2 -2
  96. package/dashboard/dist/assets/{McpQueryDetailPage-B-qtmwLd.js.map → McpQueryDetailPage-wbwK8WWq.js.map} +1 -1
  97. package/dashboard/dist/assets/{McpQueryPage-DX1T-Ugq.js → McpQueryPage-CYvVEk3G.js} +2 -2
  98. package/dashboard/dist/assets/{McpQueryPage-DX1T-Ugq.js.map → McpQueryPage-CYvVEk3G.js.map} +1 -1
  99. package/dashboard/dist/assets/{McpRunDetailPage-DNdl10nB.js → McpRunDetailPage-TZBX68i4.js} +2 -2
  100. package/dashboard/dist/assets/{McpRunDetailPage-DNdl10nB.js.map → McpRunDetailPage-TZBX68i4.js.map} +1 -1
  101. package/dashboard/dist/assets/{McpRunsPage-DVXVB4AJ.js → McpRunsPage-Cu91Mh52.js} +2 -2
  102. package/dashboard/dist/assets/{McpRunsPage-DVXVB4AJ.js.map → McpRunsPage-Cu91Mh52.js.map} +1 -1
  103. package/dashboard/dist/assets/{NamespacePill-CSZT2fA4.js → NamespacePill-C5epAgq6.js} +2 -2
  104. package/dashboard/dist/assets/{NamespacePill-CSZT2fA4.js.map → NamespacePill-C5epAgq6.js.map} +1 -1
  105. package/dashboard/dist/assets/{OperationsPage-BxgWQq8v.js → OperationsPage-D-6sKw6y.js} +2 -2
  106. package/dashboard/dist/assets/{OperationsPage-BxgWQq8v.js.map → OperationsPage-D-6sKw6y.js.map} +1 -1
  107. package/dashboard/dist/assets/OperatorDashboard-DD8JUU3L.js +2 -0
  108. package/dashboard/dist/assets/{OperatorDashboard-Cw86vOPg.js.map → OperatorDashboard-DD8JUU3L.js.map} +1 -1
  109. package/dashboard/dist/assets/{PageHeader-C4z1ExF7.js → PageHeader-BSWIspNB.js} +2 -2
  110. package/dashboard/dist/assets/{PageHeader-C4z1ExF7.js.map → PageHeader-BSWIspNB.js.map} +1 -1
  111. package/dashboard/dist/assets/{PageHeaderWithStats-B_f_Sz_Q.js → PageHeaderWithStats-DDjopQ-k.js} +2 -2
  112. package/dashboard/dist/assets/{PageHeaderWithStats-B_f_Sz_Q.js.map → PageHeaderWithStats-DDjopQ-k.js.map} +1 -1
  113. package/dashboard/dist/assets/{PersonaDetailPage-BTIJ4A6p.js → PersonaDetailPage-CGri6okV.js} +2 -2
  114. package/dashboard/dist/assets/{PersonaDetailPage-BTIJ4A6p.js.map → PersonaDetailPage-CGri6okV.js.map} +1 -1
  115. package/dashboard/dist/assets/{PersonasPage-D11PokPb.js → PersonasPage-DtHW6-Qx.js} +2 -2
  116. package/dashboard/dist/assets/{PersonasPage-D11PokPb.js.map → PersonasPage-DtHW6-Qx.js.map} +1 -1
  117. package/dashboard/dist/assets/PortalPage-Dgbk_mzr.js +2 -0
  118. package/dashboard/dist/assets/{PortalPage-1rXm0cNK.js.map → PortalPage-Dgbk_mzr.js.map} +1 -1
  119. package/dashboard/dist/assets/{ProcessDetailPage-Cp5-969g.js → ProcessDetailPage-BdkacBrD.js} +2 -2
  120. package/dashboard/dist/assets/{ProcessDetailPage-Cp5-969g.js.map → ProcessDetailPage-BdkacBrD.js.map} +1 -1
  121. package/dashboard/dist/assets/{ProcessesListPage-assXQe-x.js → ProcessesListPage-DO5A-OfZ.js} +2 -2
  122. package/dashboard/dist/assets/{ProcessesListPage-assXQe-x.js.map → ProcessesListPage-DO5A-OfZ.js.map} +1 -1
  123. package/dashboard/dist/assets/ResolverForm-CqGE5lf-.js +8 -0
  124. package/dashboard/dist/assets/ResolverForm-CqGE5lf-.js.map +1 -0
  125. package/dashboard/dist/assets/{RoleDetailPage-c_Qij0Tf.js → RoleDetailPage-DgVp0Hih.js} +2 -2
  126. package/dashboard/dist/assets/{RoleDetailPage-c_Qij0Tf.js.map → RoleDetailPage-DgVp0Hih.js.map} +1 -1
  127. package/dashboard/dist/assets/{RolePill-Bw_n7EpR.js → RolePill-CoETDpjF.js} +2 -2
  128. package/dashboard/dist/assets/{RolePill-Bw_n7EpR.js.map → RolePill-CoETDpjF.js.map} +1 -1
  129. package/dashboard/dist/assets/{RolesPage-D4oaYgTg.js → RolesPage-BmsWq83e.js} +2 -2
  130. package/dashboard/dist/assets/{RolesPage-D4oaYgTg.js.map → RolesPage-BmsWq83e.js.map} +1 -1
  131. package/dashboard/dist/assets/RosterWidget-DR-T4Izu.js +2 -0
  132. package/dashboard/dist/assets/RosterWidget-DR-T4Izu.js.map +1 -0
  133. package/dashboard/dist/assets/{RunAsSelector-Fptil_sB.js → RunAsSelector-CyUWGp5P.js} +2 -2
  134. package/dashboard/dist/assets/{RunAsSelector-Fptil_sB.js.map → RunAsSelector-CyUWGp5P.js.map} +1 -1
  135. package/dashboard/dist/assets/{ScanCodesPage-BeZA_YJW.js → ScanCodesPage-Cy05JOt4.js} +2 -2
  136. package/dashboard/dist/assets/{ScanCodesPage-BeZA_YJW.js.map → ScanCodesPage-Cy05JOt4.js.map} +1 -1
  137. package/dashboard/dist/assets/{ScanSchemeDetailPage-Bxi15xNr.js → ScanSchemeDetailPage-BX1afzOg.js} +2 -2
  138. package/dashboard/dist/assets/{ScanSchemeDetailPage-Bxi15xNr.js.map → ScanSchemeDetailPage-BX1afzOg.js.map} +1 -1
  139. package/dashboard/dist/assets/ScanStationPage-DLAB41tb.js +2 -0
  140. package/dashboard/dist/assets/ScanStationPage-DLAB41tb.js.map +1 -0
  141. package/dashboard/dist/assets/{ScopeBadge-CKgfwTy3.js → ScopeBadge-CGsTtqFr.js} +2 -2
  142. package/dashboard/dist/assets/{ScopeBadge-CKgfwTy3.js.map → ScopeBadge-CGsTtqFr.js.map} +1 -1
  143. package/dashboard/dist/assets/{StickyPagination-C2drZk7w.js → StickyPagination-BzUTnYYE.js} +2 -2
  144. package/dashboard/dist/assets/{StickyPagination-C2drZk7w.js.map → StickyPagination-BzUTnYYE.js.map} +1 -1
  145. package/dashboard/dist/assets/{StreamMessageDetail-x-l5653y.js → StreamMessageDetail-D0W1ekgG.js} +2 -2
  146. package/dashboard/dist/assets/{StreamMessageDetail-x-l5653y.js.map → StreamMessageDetail-D0W1ekgG.js.map} +1 -1
  147. package/dashboard/dist/assets/{SwimlaneTimeline-Ds_CqLXL.js → SwimlaneTimeline-BPBwmwuO.js} +2 -2
  148. package/dashboard/dist/assets/{SwimlaneTimeline-Ds_CqLXL.js.map → SwimlaneTimeline-BPBwmwuO.js.map} +1 -1
  149. package/dashboard/dist/assets/{TagInput-B6fMAqxd.js → TagInput-S7UuVT_M.js} +2 -2
  150. package/dashboard/dist/assets/{TagInput-B6fMAqxd.js.map → TagInput-S7UuVT_M.js.map} +1 -1
  151. package/dashboard/dist/assets/{TaskDetailPage-BJGKEnIo.js → TaskDetailPage-VuaIXISy.js} +2 -2
  152. package/dashboard/dist/assets/{TaskDetailPage-BJGKEnIo.js.map → TaskDetailPage-VuaIXISy.js.map} +1 -1
  153. package/dashboard/dist/assets/{TaskQueuePill-BnDp_DE6.js → TaskQueuePill-BlHNpYiZ.js} +2 -2
  154. package/dashboard/dist/assets/{TaskQueuePill-BnDp_DE6.js.map → TaskQueuePill-BlHNpYiZ.js.map} +1 -1
  155. package/dashboard/dist/assets/{TasksListPage-DyLaZvTT.js → TasksListPage-KaUSoqos.js} +2 -2
  156. package/dashboard/dist/assets/{TasksListPage-DyLaZvTT.js.map → TasksListPage-KaUSoqos.js.map} +1 -1
  157. package/dashboard/dist/assets/{TimeAgo-B_Ta97uy.js → TimeAgo-C_E6u0fr.js} +2 -2
  158. package/dashboard/dist/assets/{TimeAgo-B_Ta97uy.js.map → TimeAgo-C_E6u0fr.js.map} +1 -1
  159. package/dashboard/dist/assets/{TimestampCell-CiTYBQw-.js → TimestampCell-CUJiAmax.js} +2 -2
  160. package/dashboard/dist/assets/{TimestampCell-CiTYBQw-.js.map → TimestampCell-CUJiAmax.js.map} +1 -1
  161. package/dashboard/dist/assets/{ToolPill-BRZWo4l8.js → ToolPill-D40uLM_v.js} +2 -2
  162. package/dashboard/dist/assets/{ToolPill-BRZWo4l8.js.map → ToolPill-D40uLM_v.js.map} +1 -1
  163. package/dashboard/dist/assets/{ToolTestPanel-Dw8Zu6xr.js → ToolTestPanel-Ch5nQANh.js} +2 -2
  164. package/dashboard/dist/assets/{ToolTestPanel-Dw8Zu6xr.js.map → ToolTestPanel-Ch5nQANh.js.map} +1 -1
  165. package/dashboard/dist/assets/{TopicDetailPage-DPMAWKTN.js → TopicDetailPage-BprToO7w.js} +2 -2
  166. package/dashboard/dist/assets/{TopicDetailPage-DPMAWKTN.js.map → TopicDetailPage-BprToO7w.js.map} +1 -1
  167. package/dashboard/dist/assets/{TopicsPage-0Z2lAE0d.js → TopicsPage-8SmCnQ-E.js} +2 -2
  168. package/dashboard/dist/assets/{TopicsPage-0Z2lAE0d.js.map → TopicsPage-8SmCnQ-E.js.map} +1 -1
  169. package/dashboard/dist/assets/{UserName-DrAbCpdY.js → UserName-BkJryunF.js} +2 -2
  170. package/dashboard/dist/assets/{UserName-DrAbCpdY.js.map → UserName-BkJryunF.js.map} +1 -1
  171. package/dashboard/dist/assets/{WorkflowExecutionPage-DQITqX-6.js → WorkflowExecutionPage-B2S5ODie.js} +2 -2
  172. package/dashboard/dist/assets/{WorkflowExecutionPage-DQITqX-6.js.map → WorkflowExecutionPage-B2S5ODie.js.map} +1 -1
  173. package/dashboard/dist/assets/{WorkflowPill-Cly9Ln_3.js → WorkflowPill-DJLcK_CM.js} +2 -2
  174. package/dashboard/dist/assets/{WorkflowPill-Cly9Ln_3.js.map → WorkflowPill-DJLcK_CM.js.map} +1 -1
  175. package/dashboard/dist/assets/{WorkflowsDashboard-CMJPiR6G.js → WorkflowsDashboard-CABuRCXp.js} +2 -2
  176. package/dashboard/dist/assets/{WorkflowsDashboard-CMJPiR6G.js.map → WorkflowsDashboard-CABuRCXp.js.map} +1 -1
  177. package/dashboard/dist/assets/{WorkflowsOverview-DwVMLe1y.js → WorkflowsOverview-BPUHAj8T.js} +2 -2
  178. package/dashboard/dist/assets/{WorkflowsOverview-DwVMLe1y.js.map → WorkflowsOverview-BPUHAj8T.js.map} +1 -1
  179. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Do6s_wmW.js → YamlWorkflowDetailPage-DZGjT-Tl.js} +2 -2
  180. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Do6s_wmW.js.map → YamlWorkflowDetailPage-DZGjT-Tl.js.map} +1 -1
  181. package/dashboard/dist/assets/{YamlWorkflowsPage-8Sr6rZMZ.js → YamlWorkflowsPage-DIxHURlM.js} +2 -2
  182. package/dashboard/dist/assets/{YamlWorkflowsPage-8Sr6rZMZ.js.map → YamlWorkflowsPage-DIxHURlM.js.map} +1 -1
  183. package/dashboard/dist/assets/{agents-Maom-dbf.js → agents-BnDp2rcb.js} +2 -2
  184. package/dashboard/dist/assets/{agents-Maom-dbf.js.map → agents-BnDp2rcb.js.map} +1 -1
  185. package/dashboard/dist/assets/{bots-BESWftTA.js → bots-maURYoyr.js} +2 -2
  186. package/dashboard/dist/assets/{bots-BESWftTA.js.map → bots-maURYoyr.js.map} +1 -1
  187. package/dashboard/dist/assets/{capabilities-BnvJAmFQ.js → capabilities-5-TakMUh.js} +2 -2
  188. package/dashboard/dist/assets/{capabilities-BnvJAmFQ.js.map → capabilities-5-TakMUh.js.map} +1 -1
  189. package/dashboard/dist/assets/{controlplane-Cu7TD1RX.js → controlplane-BXVVTWoh.js} +2 -2
  190. package/dashboard/dist/assets/{controlplane-Cu7TD1RX.js.map → controlplane-BXVVTWoh.js.map} +1 -1
  191. package/dashboard/dist/assets/escalation-columns-v8gx8wFb.js +2 -0
  192. package/dashboard/dist/assets/{escalation-columns-BPSH8qMx.js.map → escalation-columns-v8gx8wFb.js.map} +1 -1
  193. package/dashboard/dist/assets/escalation-items-OWntjDaH.js +2 -0
  194. package/dashboard/dist/assets/escalation-items-OWntjDaH.js.map +1 -0
  195. package/dashboard/dist/assets/{index-BkVarz6E.js → index-BIfY4mR2.js} +2 -2
  196. package/dashboard/dist/assets/{index-BkVarz6E.js.map → index-BIfY4mR2.js.map} +1 -1
  197. package/dashboard/dist/assets/{index-WA8py2Yc.js → index-BTm3-qwq.js} +2 -2
  198. package/dashboard/dist/assets/{index-WA8py2Yc.js.map → index-BTm3-qwq.js.map} +1 -1
  199. package/dashboard/dist/assets/{index-B4SNhwgt.js → index-CR9rhAvN.js} +2 -2
  200. package/dashboard/dist/assets/{index-B4SNhwgt.js.map → index-CR9rhAvN.js.map} +1 -1
  201. package/dashboard/dist/assets/index-CWKs10VQ.js +13 -0
  202. package/dashboard/dist/assets/{index-qY2KDGkM.js.map → index-CWKs10VQ.js.map} +1 -1
  203. package/dashboard/dist/assets/index-CvKegIVe.js +3 -0
  204. package/dashboard/dist/assets/{index-kGSUZCOb.js.map → index-CvKegIVe.js.map} +1 -1
  205. package/dashboard/dist/assets/{index-BLbZsMRG.js → index-D7peGNN0.js} +10 -10
  206. package/dashboard/dist/assets/index-D7peGNN0.js.map +1 -0
  207. package/dashboard/dist/assets/{index-BvI2_dSt.js → index-D_ojXTgO.js} +2 -2
  208. package/dashboard/dist/assets/{index-BvI2_dSt.js.map → index-D_ojXTgO.js.map} +1 -1
  209. package/dashboard/dist/assets/index-Dafrbwk_.js +2 -0
  210. package/dashboard/dist/assets/{index-AJNPDsTI.js.map → index-Dafrbwk_.js.map} +1 -1
  211. package/dashboard/dist/assets/{index-BpWutGxS.js → index-Dk7nwVpm.js} +2 -2
  212. package/dashboard/dist/assets/{index-BpWutGxS.js.map → index-Dk7nwVpm.js.map} +1 -1
  213. package/dashboard/dist/assets/{index-_yHsJJTS.js → index-DvkcyTE2.js} +2 -2
  214. package/dashboard/dist/assets/{index-_yHsJJTS.js.map → index-DvkcyTE2.js.map} +1 -1
  215. package/dashboard/dist/assets/{index-BLlg1aVG.js → index-WPVpyoD3.js} +2 -2
  216. package/dashboard/dist/assets/{index-BLlg1aVG.js.map → index-WPVpyoD3.js.map} +1 -1
  217. package/dashboard/dist/assets/{index-D5JQweUm.js → index-YGTGxcTF.js} +2 -2
  218. package/dashboard/dist/assets/{index-D5JQweUm.js.map → index-YGTGxcTF.js.map} +1 -1
  219. package/dashboard/dist/assets/{index-BG7OpNg7.js → index-vgHzaGmW.js} +2 -2
  220. package/dashboard/dist/assets/{index-BG7OpNg7.js.map → index-vgHzaGmW.js.map} +1 -1
  221. package/dashboard/dist/assets/{knowledge-DCFRNG8R.js → knowledge-L_zY1zmy.js} +2 -2
  222. package/dashboard/dist/assets/{knowledge-DCFRNG8R.js.map → knowledge-L_zY1zmy.js.map} +1 -1
  223. package/dashboard/dist/assets/{mcp-query-Wt8z6yAL.js → mcp-query-CFbGudkb.js} +2 -2
  224. package/dashboard/dist/assets/{mcp-query-Wt8z6yAL.js.map → mcp-query-CFbGudkb.js.map} +1 -1
  225. package/dashboard/dist/assets/{mcp-CZhOocmN.js → mcp-wCWFRA0T.js} +2 -2
  226. package/dashboard/dist/assets/{mcp-CZhOocmN.js.map → mcp-wCWFRA0T.js.map} +1 -1
  227. package/dashboard/dist/assets/{personas-D2_foqJm.js → personas-B_VIacZJ.js} +2 -2
  228. package/dashboard/dist/assets/{personas-D2_foqJm.js.map → personas-B_VIacZJ.js.map} +1 -1
  229. package/dashboard/dist/assets/{pipelines-Bb159fNz.js → pipelines-Bv0Z8xQA.js} +2 -2
  230. package/dashboard/dist/assets/{pipelines-Bb159fNz.js.map → pipelines-Bv0Z8xQA.js.map} +1 -1
  231. package/dashboard/dist/assets/{tasks-xAU6C6tb.js → tasks-DP6yaNZs.js} +2 -2
  232. package/dashboard/dist/assets/{tasks-xAU6C6tb.js.map → tasks-DP6yaNZs.js.map} +1 -1
  233. package/dashboard/dist/assets/{topics-DFdqb2Te.js → topics-UBSIcpSR.js} +2 -2
  234. package/dashboard/dist/assets/{topics-DFdqb2Te.js.map → topics-UBSIcpSR.js.map} +1 -1
  235. package/dashboard/dist/assets/{useDebouncedValue-CbHmpS4w.js → useDebouncedValue-R62q3Xpx.js} +2 -2
  236. package/dashboard/dist/assets/{useDebouncedValue-CbHmpS4w.js.map → useDebouncedValue-R62q3Xpx.js.map} +1 -1
  237. package/dashboard/dist/assets/{useNamespace-B_00X1yb.js → useNamespace-CAaszJDE.js} +2 -2
  238. package/dashboard/dist/assets/{useNamespace-B_00X1yb.js.map → useNamespace-CAaszJDE.js.map} +1 -1
  239. package/dashboard/dist/assets/{useYamlActivityEvents-B4F-s9g0.js → useYamlActivityEvents-emCp9U69.js} +2 -2
  240. package/dashboard/dist/assets/{useYamlActivityEvents-B4F-s9g0.js.map → useYamlActivityEvents-emCp9U69.js.map} +1 -1
  241. package/dashboard/dist/assets/validate-C7yskdEn.js +2 -0
  242. package/dashboard/dist/assets/validate-C7yskdEn.js.map +1 -0
  243. package/dashboard/dist/assets/{vendor-icons-C_DQEeBy.js → vendor-icons-DUYcKTjP.js} +2 -2
  244. package/dashboard/dist/assets/{vendor-icons-C_DQEeBy.js.map → vendor-icons-DUYcKTjP.js.map} +1 -1
  245. package/dashboard/dist/assets/{yaml-workflows-DyjbPFVp.js → yaml-workflows-C2sOwz1p.js} +2 -2
  246. package/dashboard/dist/assets/{yaml-workflows-DyjbPFVp.js.map → yaml-workflows-C2sOwz1p.js.map} +1 -1
  247. package/dashboard/dist/index.html +2 -2
  248. package/docs/accumulator.md +1 -1
  249. package/docs/api/http/escalations.md +33 -0
  250. package/docs/api/sdk/escalations.md +26 -1
  251. package/docs/hitl/escalation.md +9 -0
  252. package/docs/hitl/x-lt-embed.md +2 -0
  253. package/docs/hitl/x-lt-roster.md +31 -7
  254. package/docs/scan-codes.md +45 -3
  255. package/package.json +2 -2
  256. package/dashboard/dist/assets/AvailableEscalationsPage-DU84iaBN.js +0 -2
  257. package/dashboard/dist/assets/EscalationTimeline-BWTUBxUf.js +0 -2
  258. package/dashboard/dist/assets/FormattedValue-DeVrRJy4.js +0 -2
  259. package/dashboard/dist/assets/FormattedValue-DeVrRJy4.js.map +0 -1
  260. package/dashboard/dist/assets/OperatorDashboard-Cw86vOPg.js +0 -2
  261. package/dashboard/dist/assets/PortalPage-1rXm0cNK.js +0 -2
  262. package/dashboard/dist/assets/ResolverForm-CMzNjmGG.js +0 -8
  263. package/dashboard/dist/assets/ResolverForm-CMzNjmGG.js.map +0 -1
  264. package/dashboard/dist/assets/ScanStationPage-t5Bz957t.js +0 -2
  265. package/dashboard/dist/assets/ScanStationPage-t5Bz957t.js.map +0 -1
  266. package/dashboard/dist/assets/escalation-columns-BPSH8qMx.js +0 -2
  267. package/dashboard/dist/assets/escalation-items-uDQTphVW.js +0 -2
  268. package/dashboard/dist/assets/escalation-items-uDQTphVW.js.map +0 -1
  269. package/dashboard/dist/assets/index-AJNPDsTI.js +0 -2
  270. package/dashboard/dist/assets/index-BLbZsMRG.js.map +0 -1
  271. package/dashboard/dist/assets/index-kGSUZCOb.js +0 -3
  272. package/dashboard/dist/assets/index-qY2KDGkM.js +0 -13
@@ -1,2 +1,2 @@
1
- import{j as s}from"./vendor-query-DvWWuNIB.js";import{I as x}from"./vendor-icons-C_DQEeBy.js";function o({role:t,size:e="sm",tone:n="default"}){const a=e==="md"?"py-0.5 text-xs gap-1.5":"py-0.5 text-2xs gap-1",i=e==="md"?"w-3 h-3":"w-2.5 h-2.5",l=n==="inherit"?"text-inherit":"text-text-secondary";return s.jsxs("span",{className:`inline-flex items-center max-w-full min-w-0 ${a} ${l}`,title:t,children:[s.jsx(x,{className:`${i} shrink-0 text-accent/75`}),s.jsx("span",{className:"truncate",children:t})]})}export{o as R};
2
- //# sourceMappingURL=RolePill-Bw_n7EpR.js.map
1
+ import{j as s}from"./vendor-query-DvWWuNIB.js";import{I as x}from"./vendor-icons-DUYcKTjP.js";function o({role:t,size:e="sm",tone:n="default"}){const a=e==="md"?"py-0.5 text-xs gap-1.5":"py-0.5 text-2xs gap-1",i=e==="md"?"w-3 h-3":"w-2.5 h-2.5",l=n==="inherit"?"text-inherit":"text-text-secondary";return s.jsxs("span",{className:`inline-flex items-center max-w-full min-w-0 ${a} ${l}`,title:t,children:[s.jsx(x,{className:`${i} shrink-0 text-accent/75`}),s.jsx("span",{className:"truncate",children:t})]})}export{o as R};
2
+ //# sourceMappingURL=RolePill-CoETDpjF.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RolePill-Bw_n7EpR.js","sources":["../../src/components/common/display/RolePill.tsx"],"sourcesContent":["import { Inbox } from 'lucide-react';\n\ninterface RolePillProps {\n role: string;\n size?: 'sm' | 'md';\n /** `default` uses secondary text; `inherit` takes the surrounding text colour. */\n tone?: 'default' | 'inherit';\n}\n\n/**\n * The universal role symbol. A role is a QUEUE — a tray work arrives into and\n * is claimed from — so the inbox glyph carries it everywhere a role appears:\n * this pill, the admin sidebar's Roles entry, and the role detail page.\n */\nexport function RolePill({ role, size = 'sm', tone = 'default' }: RolePillProps) {\n const sizeClass = size === 'md'\n ? 'py-0.5 text-xs gap-1.5'\n : 'py-0.5 text-2xs gap-1';\n const iconClass = size === 'md' ? 'w-3 h-3' : 'w-2.5 h-2.5';\n const colorClass = tone === 'inherit' ? 'text-inherit' : 'text-text-secondary';\n\n return (\n <span className={`inline-flex items-center max-w-full min-w-0 ${sizeClass} ${colorClass}`} title={role}>\n <Inbox className={`${iconClass} shrink-0 text-accent/75`} />\n <span className=\"truncate\">{role}</span>\n </span>\n );\n}\n"],"names":["RolePill","role","size","tone","sizeClass","iconClass","colorClass","jsxs","jsx","Inbox"],"mappings":"8FAcO,SAASA,EAAS,CAAE,KAAAC,EAAM,KAAAC,EAAO,KAAM,KAAAC,EAAO,WAA4B,CAC/E,MAAMC,EAAYF,IAAS,KACvB,yBACA,wBACEG,EAAYH,IAAS,KAAO,UAAY,cACxCI,EAAaH,IAAS,UAAY,eAAiB,sBAEzD,OACEI,EAAAA,KAAC,QAAK,UAAW,+CAA+CH,CAAS,IAAIE,CAAU,GAAI,MAAOL,EAChG,SAAA,CAAAO,EAAAA,IAACC,EAAA,CAAM,UAAW,GAAGJ,CAAS,2BAA4B,EAC1DG,EAAAA,IAAC,OAAA,CAAK,UAAU,WAAY,SAAAP,CAAA,CAAK,CAAA,EACnC,CAEJ"}
1
+ {"version":3,"file":"RolePill-CoETDpjF.js","sources":["../../src/components/common/display/RolePill.tsx"],"sourcesContent":["import { Inbox } from 'lucide-react';\n\ninterface RolePillProps {\n role: string;\n size?: 'sm' | 'md';\n /** `default` uses secondary text; `inherit` takes the surrounding text colour. */\n tone?: 'default' | 'inherit';\n}\n\n/**\n * The universal role symbol. A role is a QUEUE — a tray work arrives into and\n * is claimed from — so the inbox glyph carries it everywhere a role appears:\n * this pill, the admin sidebar's Roles entry, and the role detail page.\n */\nexport function RolePill({ role, size = 'sm', tone = 'default' }: RolePillProps) {\n const sizeClass = size === 'md'\n ? 'py-0.5 text-xs gap-1.5'\n : 'py-0.5 text-2xs gap-1';\n const iconClass = size === 'md' ? 'w-3 h-3' : 'w-2.5 h-2.5';\n const colorClass = tone === 'inherit' ? 'text-inherit' : 'text-text-secondary';\n\n return (\n <span className={`inline-flex items-center max-w-full min-w-0 ${sizeClass} ${colorClass}`} title={role}>\n <Inbox className={`${iconClass} shrink-0 text-accent/75`} />\n <span className=\"truncate\">{role}</span>\n </span>\n );\n}\n"],"names":["RolePill","role","size","tone","sizeClass","iconClass","colorClass","jsxs","jsx","Inbox"],"mappings":"8FAcO,SAASA,EAAS,CAAE,KAAAC,EAAM,KAAAC,EAAO,KAAM,KAAAC,EAAO,WAA4B,CAC/E,MAAMC,EAAYF,IAAS,KACvB,yBACA,wBACEG,EAAYH,IAAS,KAAO,UAAY,cACxCI,EAAaH,IAAS,UAAY,eAAiB,sBAEzD,OACEI,EAAAA,KAAC,QAAK,UAAW,+CAA+CH,CAAS,IAAIE,CAAU,GAAI,MAAOL,EAChG,SAAA,CAAAO,EAAAA,IAACC,EAAA,CAAM,UAAW,GAAGJ,CAAS,2BAA4B,EAC1DG,EAAAA,IAAC,OAAA,CAAK,UAAU,WAAY,SAAAP,CAAA,CAAK,CAAA,EACnC,CAEJ"}
@@ -1,2 +1,2 @@
1
- import{a as h,j as e}from"./vendor-query-DvWWuNIB.js";import{aS as v,M as w,Z as $,a0 as g}from"./index-BLbZsMRG.js";import{F as R,a as L}from"./FilterBar-Dnse0soD.js";import{P as k}from"./PageHeader-C4z1ExF7.js";import{R as _}from"./RolePill-Bw_n7EpR.js";import{u as P,L as S}from"./vendor-react-_9U4mKFr.js";import"./vendor-icons-C_DQEeBy.js";function E({open:s,onClose:a}){const t=v(),[c,x]=h.useState(""),[i,j]=h.useState(s);s!==i&&(j(s),s&&(x(""),t.reset()));const n=()=>{const r=c.trim().toLowerCase();r&&t.mutate(r,{onSuccess:a})};return e.jsx(w,{open:s,onClose:a,title:"Create Role",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Role Name (required)"}),e.jsx("input",{type:"text",value:c,onChange:r=>x(r.target.value),placeholder:"e.g., reviewer",className:"input text-xs w-full",onKeyDown:r=>{r.key==="Enter"&&n()}}),e.jsx("p",{className:"text-2xs text-text-tertiary mt-1",children:"Lowercase letters, numbers, hyphens, and underscores only."})]}),t.error&&e.jsx("p",{className:"text-xs text-status-error",children:t.error.message}),e.jsxs("div",{className:"flex justify-end gap-3 pt-2",children:[e.jsx("button",{onClick:a,className:"btn-secondary text-xs",children:"Cancel"}),e.jsx("button",{onClick:n,disabled:!c.trim()||t.isPending,className:"btn-primary text-xs",children:t.isPending?"Creating...":"Create"})]})]})})}const C="200px 160px 1fr 130px 88px 204px",m="text-text-secondary transition-colors group-hover/row:text-text-primary",o="text-2xs font-semibold uppercase tracking-widest text-text-quaternary",b=`${o} text-right flex-1 min-w-0`,p="py-2",u="py-2.5";function T(){return e.jsxs("div",{className:"grid gap-4 px-3 border-b border-surface-border bg-surface",style:{gridTemplateColumns:C},children:[e.jsx("span",{className:`${o} ${p} flex items-center pl-[18px]`,children:"Role"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Key"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Description"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Preceded By"}),e.jsx("span",{className:`${o} ${p} flex items-center justify-end`,children:"Members"}),e.jsxs("div",{className:"flex gap-3 items-center bg-surface-sunken px-3",children:[e.jsx("span",{className:b,children:"SLA/M"}),e.jsx("span",{className:b,children:"Target/h"}),e.jsx("span",{className:b,children:"Staff"})]})]})}const y="text-xs tabular-nums text-right flex-1 min-w-0 transition-colors";function A({role:s,onClick:a}){return e.jsxs("div",{role:"button",tabIndex:0,onClick:a,onKeyDown:t=>{(t.key==="Enter"||t.key===" ")&&a()},className:"grid gap-4 px-3 cursor-pointer group/row",style:{gridTemplateColumns:C},children:[e.jsxs("div",{className:`flex items-center gap-2.5 min-w-0 ${u}`,children:[e.jsx("span",{className:`w-2.5 h-2.5 rounded-full dot-ring shrink-0 ${s.ops_visible?"bg-status-success":"bg-surface-border"}`,title:s.ops_visible?"Visible in Operations":"Enable in role settings to show in Operations"}),e.jsx("span",{className:`text-sm truncate ${m}`,children:g(s)})]}),e.jsx("span",{className:`flex items-center text-xs font-mono truncate ${u} text-text-tertiary transition-colors group-hover/row:text-text-secondary`,children:s.role}),e.jsx("span",{className:`flex items-center min-w-0 text-xs text-text-tertiary truncate ${u} transition-colors group-hover/row:text-text-secondary`,children:s.description??""}),e.jsx("div",{className:`flex items-center min-w-0 ${u}`,onClick:t=>t.stopPropagation(),children:s.parent_role&&e.jsx(S,{to:`/admin/roles/${encodeURIComponent(s.parent_role)}`,className:"min-w-0 truncate text-text-secondary transition-colors hover:text-accent",children:e.jsx(_,{role:s.parent_role,tone:"inherit"})})}),e.jsx("span",{className:`flex items-center justify-end text-sm tabular-nums ${u} transition-colors ${m}`,children:s.user_count>0?s.user_count:""}),e.jsxs("div",{className:"flex gap-3 items-center bg-surface-sunken px-3",children:[e.jsx("span",{className:`${y} ${m}`,children:s.sla_minutes??""}),e.jsx("span",{className:`${y} ${m}`,children:s.target_per_hour??""}),e.jsx("span",{className:`${y} ${m}`,children:s.worker_count??""})]})]})}function Y(){const s=P(),{data:a,isLoading:t}=$(),[c,x]=h.useState(!1),[i,j]=h.useState(""),n=(a==null?void 0:a.roles)??[],r=h.useMemo(()=>{const l=[...n].sort((d,f)=>g(d).localeCompare(g(f)));if(!i.trim())return l;const N=i.toLowerCase();return l.filter(d=>{var f;return d.role.toLowerCase().includes(N)||g(d).toLowerCase().includes(N)||((f=d.description)==null?void 0:f.toLowerCase().includes(N))})},[n,i]);return e.jsxs("div",{children:[e.jsx(k,{title:"Roles",docsHash:"#docs:dashboard.md:roles",actions:e.jsx("button",{onClick:()=>x(!0),className:"btn-primary text-xs flex items-center gap-1.5",children:"+ Add Role"})}),!t&&n.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs(R,{children:[e.jsx(L,{label:"Search",value:i,onChange:j,placeholder:`${n.length} roles…`}),i&&r.length!==n.length&&e.jsxs("span",{className:"text-2xs text-text-quaternary tabular-nums shrink-0",children:[r.length," of ",n.length]})]}),r.length>0&&e.jsx("div",{className:"sticky top-[60px] z-10 bg-surface",children:e.jsx(T,{})})]}),t?e.jsx("div",{className:"animate-pulse space-y-3 mt-6",children:[1,2,3,4,5].map(l=>e.jsx("div",{className:"h-10 bg-surface-sunken rounded w-full"},l))}):r.length===0?e.jsx("p",{className:"text-sm text-text-tertiary mt-8",children:i?"Clear the search to see all roles.":"Create a role to get started."}):e.jsx(e.Fragment,{children:e.jsx("div",{className:"divide-y divide-surface-border/30",children:r.map(l=>e.jsx(A,{role:l,onClick:()=>s(`/admin/roles/${encodeURIComponent(l.role)}`)},l.role))})}),e.jsx(E,{open:c,onClose:()=>x(!1)})]})}export{Y as RolesPage};
2
- //# sourceMappingURL=RolesPage-D4oaYgTg.js.map
1
+ import{a as h,j as e}from"./vendor-query-DvWWuNIB.js";import{aS as v,M as w,Z as $,a0 as g}from"./index-D7peGNN0.js";import{F as R,a as L}from"./FilterBar-CqPFZkVU.js";import{P as k}from"./PageHeader-BSWIspNB.js";import{R as _}from"./RolePill-CoETDpjF.js";import{u as P,L as S}from"./vendor-react-_9U4mKFr.js";import"./vendor-icons-DUYcKTjP.js";function E({open:s,onClose:a}){const t=v(),[c,x]=h.useState(""),[i,j]=h.useState(s);s!==i&&(j(s),s&&(x(""),t.reset()));const n=()=>{const r=c.trim().toLowerCase();r&&t.mutate(r,{onSuccess:a})};return e.jsx(w,{open:s,onClose:a,title:"Create Role",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:"Role Name (required)"}),e.jsx("input",{type:"text",value:c,onChange:r=>x(r.target.value),placeholder:"e.g., reviewer",className:"input text-xs w-full",onKeyDown:r=>{r.key==="Enter"&&n()}}),e.jsx("p",{className:"text-2xs text-text-tertiary mt-1",children:"Lowercase letters, numbers, hyphens, and underscores only."})]}),t.error&&e.jsx("p",{className:"text-xs text-status-error",children:t.error.message}),e.jsxs("div",{className:"flex justify-end gap-3 pt-2",children:[e.jsx("button",{onClick:a,className:"btn-secondary text-xs",children:"Cancel"}),e.jsx("button",{onClick:n,disabled:!c.trim()||t.isPending,className:"btn-primary text-xs",children:t.isPending?"Creating...":"Create"})]})]})})}const C="200px 160px 1fr 130px 88px 204px",m="text-text-secondary transition-colors group-hover/row:text-text-primary",o="text-2xs font-semibold uppercase tracking-widest text-text-quaternary",b=`${o} text-right flex-1 min-w-0`,p="py-2",u="py-2.5";function T(){return e.jsxs("div",{className:"grid gap-4 px-3 border-b border-surface-border bg-surface",style:{gridTemplateColumns:C},children:[e.jsx("span",{className:`${o} ${p} flex items-center pl-[18px]`,children:"Role"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Key"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Description"}),e.jsx("span",{className:`${o} ${p} flex items-center`,children:"Preceded By"}),e.jsx("span",{className:`${o} ${p} flex items-center justify-end`,children:"Members"}),e.jsxs("div",{className:"flex gap-3 items-center bg-surface-sunken px-3",children:[e.jsx("span",{className:b,children:"SLA/M"}),e.jsx("span",{className:b,children:"Target/h"}),e.jsx("span",{className:b,children:"Staff"})]})]})}const y="text-xs tabular-nums text-right flex-1 min-w-0 transition-colors";function A({role:s,onClick:a}){return e.jsxs("div",{role:"button",tabIndex:0,onClick:a,onKeyDown:t=>{(t.key==="Enter"||t.key===" ")&&a()},className:"grid gap-4 px-3 cursor-pointer group/row",style:{gridTemplateColumns:C},children:[e.jsxs("div",{className:`flex items-center gap-2.5 min-w-0 ${u}`,children:[e.jsx("span",{className:`w-2.5 h-2.5 rounded-full dot-ring shrink-0 ${s.ops_visible?"bg-status-success":"bg-surface-border"}`,title:s.ops_visible?"Visible in Operations":"Enable in role settings to show in Operations"}),e.jsx("span",{className:`text-sm truncate ${m}`,children:g(s)})]}),e.jsx("span",{className:`flex items-center text-xs font-mono truncate ${u} text-text-tertiary transition-colors group-hover/row:text-text-secondary`,children:s.role}),e.jsx("span",{className:`flex items-center min-w-0 text-xs text-text-tertiary truncate ${u} transition-colors group-hover/row:text-text-secondary`,children:s.description??""}),e.jsx("div",{className:`flex items-center min-w-0 ${u}`,onClick:t=>t.stopPropagation(),children:s.parent_role&&e.jsx(S,{to:`/admin/roles/${encodeURIComponent(s.parent_role)}`,className:"min-w-0 truncate text-text-secondary transition-colors hover:text-accent",children:e.jsx(_,{role:s.parent_role,tone:"inherit"})})}),e.jsx("span",{className:`flex items-center justify-end text-sm tabular-nums ${u} transition-colors ${m}`,children:s.user_count>0?s.user_count:""}),e.jsxs("div",{className:"flex gap-3 items-center bg-surface-sunken px-3",children:[e.jsx("span",{className:`${y} ${m}`,children:s.sla_minutes??""}),e.jsx("span",{className:`${y} ${m}`,children:s.target_per_hour??""}),e.jsx("span",{className:`${y} ${m}`,children:s.worker_count??""})]})]})}function Y(){const s=P(),{data:a,isLoading:t}=$(),[c,x]=h.useState(!1),[i,j]=h.useState(""),n=(a==null?void 0:a.roles)??[],r=h.useMemo(()=>{const l=[...n].sort((d,f)=>g(d).localeCompare(g(f)));if(!i.trim())return l;const N=i.toLowerCase();return l.filter(d=>{var f;return d.role.toLowerCase().includes(N)||g(d).toLowerCase().includes(N)||((f=d.description)==null?void 0:f.toLowerCase().includes(N))})},[n,i]);return e.jsxs("div",{children:[e.jsx(k,{title:"Roles",docsHash:"#docs:dashboard.md:roles",actions:e.jsx("button",{onClick:()=>x(!0),className:"btn-primary text-xs flex items-center gap-1.5",children:"+ Add Role"})}),!t&&n.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs(R,{children:[e.jsx(L,{label:"Search",value:i,onChange:j,placeholder:`${n.length} roles…`}),i&&r.length!==n.length&&e.jsxs("span",{className:"text-2xs text-text-quaternary tabular-nums shrink-0",children:[r.length," of ",n.length]})]}),r.length>0&&e.jsx("div",{className:"sticky top-[60px] z-10 bg-surface",children:e.jsx(T,{})})]}),t?e.jsx("div",{className:"animate-pulse space-y-3 mt-6",children:[1,2,3,4,5].map(l=>e.jsx("div",{className:"h-10 bg-surface-sunken rounded w-full"},l))}):r.length===0?e.jsx("p",{className:"text-sm text-text-tertiary mt-8",children:i?"Clear the search to see all roles.":"Create a role to get started."}):e.jsx(e.Fragment,{children:e.jsx("div",{className:"divide-y divide-surface-border/30",children:r.map(l=>e.jsx(A,{role:l,onClick:()=>s(`/admin/roles/${encodeURIComponent(l.role)}`)},l.role))})}),e.jsx(E,{open:c,onClose:()=>x(!1)})]})}export{Y as RolesPage};
2
+ //# sourceMappingURL=RolesPage-BmsWq83e.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RolesPage-D4oaYgTg.js","sources":["../../src/pages/admin/roles/CreateRoleModal.tsx","../../src/pages/admin/roles/RolesPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useCreateRole } from '../../../api/roles';\nimport { Modal } from '../../../components/common/modal/Modal';\n\nexport function CreateRoleModal({\n open,\n onClose,\n}: {\n open: boolean;\n onClose: () => void;\n}) {\n const createRole = useCreateRole();\n const [roleName, setRoleName] = useState('');\n\n const [prevOpen, setPrevOpen] = useState(open);\n if (open !== prevOpen) {\n setPrevOpen(open);\n if (open) {\n setRoleName('');\n createRole.reset();\n }\n }\n\n const handleCreate = () => {\n const trimmed = roleName.trim().toLowerCase();\n if (!trimmed) return;\n createRole.mutate(trimmed, { onSuccess: onClose });\n };\n\n return (\n <Modal open={open} onClose={onClose} title=\"Create Role\">\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Role Name (required)\n </label>\n <input\n type=\"text\"\n value={roleName}\n onChange={(e) => setRoleName(e.target.value)}\n placeholder=\"e.g., reviewer\"\n className=\"input text-xs w-full\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleCreate();\n }}\n />\n <p className=\"text-2xs text-text-tertiary mt-1\">\n Lowercase letters, numbers, hyphens, and underscores only.\n </p>\n </div>\n\n {createRole.error && (\n <p className=\"text-xs text-status-error\">{(createRole.error as Error).message}</p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={onClose} className=\"btn-secondary text-xs\">\n Cancel\n </button>\n <button\n onClick={handleCreate}\n disabled={!roleName.trim() || createRole.isPending}\n className=\"btn-primary text-xs\"\n >\n {createRole.isPending ? 'Creating...' : 'Create'}\n </button>\n </div>\n </div>\n </Modal>\n );\n}\n","import { useState, useMemo } from 'react';\nimport { useNavigate, Link } from 'react-router-dom';\nimport { useRoleDetails, type RoleDetail } from '../../../api/roles';\nimport { FilterBar, FilterInput } from '../../../components/common/data/FilterBar';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { RolePill } from '../../../components/common/display/RolePill';\nimport { displayRoleTitle } from '../../../lib/role-display';\nimport { CreateRoleModal } from './CreateRoleModal';\n\n// ── Grid columns ──────────────────────────────────────────────────────────────\n// ROLE(dot+display name, 200px) | KEY(160px) | DESCRIPTION(1fr) | PRECEDED BY(130px) | MEMBERS(88px) | CAPACITY(204px)\n// The display name leads (user-set title, else Title Case derived from the\n// key — same formatter the Pace Board uses); the exact key is the secondary\n// field. The capacity block is a single combined cell that internally renders\n// SLA/m · Target/h · Staff flush to row edges.\nconst GRID = '200px 160px 1fr 130px 88px 204px';\n\nconst CELL_TEXT = 'text-text-secondary transition-colors group-hover/row:text-text-primary';\n// Header/cell sizes match the app's list standard (DataTable): text-2xs\n// headers, text-sm body cells.\nconst HDR = 'text-2xs font-semibold uppercase tracking-widest text-text-quaternary';\n\n// ── Table header ──────────────────────────────────────────────────────────────\n\nconst CAPACITY_COL_LABEL = `${HDR} text-right flex-1 min-w-0`;\n\n// py lives on each cell, not the grid — lets the capacity div stretch flush to row edges\nconst CELL_PY = 'py-2';\nconst ROW_PY = 'py-2.5';\n\nfunction TableHead() {\n return (\n <div\n className=\"grid gap-4 px-3 border-b border-surface-border bg-surface\"\n style={{ gridTemplateColumns: GRID }}\n >\n <span className={`${HDR} ${CELL_PY} flex items-center pl-[18px]`}>Role</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Key</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Description</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Preceded By</span>\n <span className={`${HDR} ${CELL_PY} flex items-center justify-end`}>Members</span>\n {/* Capacity block — no py, stretches to full row height by default (grid stretch alignment) */}\n <div className=\"flex gap-3 items-center bg-surface-sunken px-3\">\n <span className={CAPACITY_COL_LABEL}>SLA/M</span>\n <span className={CAPACITY_COL_LABEL}>Target/h</span>\n <span className={CAPACITY_COL_LABEL}>Staff</span>\n </div>\n </div>\n );\n}\n\n// ── Role row ──────────────────────────────────────────────────────────────────\n\nconst CAPACITY_COL_VAL = 'text-xs tabular-nums text-right flex-1 min-w-0 transition-colors';\n\nfunction RoleRow({\n role,\n onClick,\n}: {\n role: RoleDetail;\n onClick: () => void;\n}) {\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick(); }}\n className=\"grid gap-4 px-3 cursor-pointer group/row\"\n style={{ gridTemplateColumns: GRID }}\n >\n {/* ROLE — ops status circle inline with the display name (user-set\n title, else Title Case derived from the key) */}\n <div className={`flex items-center gap-2.5 min-w-0 ${ROW_PY}`}>\n <span\n className={`w-2.5 h-2.5 rounded-full dot-ring shrink-0 ${role.ops_visible ? 'bg-status-success' : 'bg-surface-border'}`}\n title={role.ops_visible ? 'Visible in Operations' : 'Enable in role settings to show in Operations'}\n />\n <span className={`text-sm truncate ${CELL_TEXT}`}>\n {displayRoleTitle(role)}\n </span>\n </div>\n\n {/* KEY — the exact technical id */}\n <span className={`flex items-center text-xs font-mono truncate ${ROW_PY} text-text-tertiary transition-colors group-hover/row:text-text-secondary`}>\n {role.role}\n </span>\n\n {/* DESCRIPTION — min-w-0 lets the 1fr grid cell shrink so truncate can\n actually render its ellipsis */}\n <span className={`flex items-center min-w-0 text-xs text-text-tertiary truncate ${ROW_PY} transition-colors group-hover/row:text-text-secondary`}>\n {role.description ?? ''}\n </span>\n\n {/* PRECEDED BY — the universal role pill (inbox + key), linked */}\n <div className={`flex items-center min-w-0 ${ROW_PY}`} onClick={(e) => e.stopPropagation()}>\n {role.parent_role && (\n <Link\n to={`/admin/roles/${encodeURIComponent(role.parent_role)}`}\n className=\"min-w-0 truncate text-text-secondary transition-colors hover:text-accent\"\n >\n <RolePill role={role.parent_role} tone=\"inherit\" />\n </Link>\n )}\n </div>\n\n {/* MEMBER COUNT — unset renders empty, not invisible text (screen\n readers and copy/paste see exactly what the eye sees) */}\n <span className={`flex items-center justify-end text-sm tabular-nums ${ROW_PY} transition-colors ${CELL_TEXT}`}>\n {role.user_count > 0 ? role.user_count : ''}\n </span>\n\n {/* SLA/m · Target/h · Staff — no py: stretches flush to full row height */}\n <div className=\"flex gap-3 items-center bg-surface-sunken px-3\">\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.sla_minutes ?? ''}\n </span>\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.target_per_hour ?? ''}\n </span>\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.worker_count ?? ''}\n </span>\n </div>\n </div>\n );\n}\n\n// ── Page ──────────────────────────────────────────────────────────────────────\n\nexport function RolesPage() {\n const navigate = useNavigate();\n const { data, isLoading } = useRoleDetails();\n const [showCreate, setShowCreate] = useState(false);\n const [search, setSearch] = useState('');\n\n const roles = data?.roles ?? [];\n\n // Rows lead with the display name, so they sort by it — and the search\n // matches it too (a derived \"Cad Designer\" is findable even when no title\n // is set).\n const filtered = useMemo(() => {\n const sorted = [...roles].sort((a, b) => displayRoleTitle(a).localeCompare(displayRoleTitle(b)));\n if (!search.trim()) return sorted;\n const q = search.toLowerCase();\n return sorted.filter(\n (r) =>\n r.role.toLowerCase().includes(q) ||\n displayRoleTitle(r).toLowerCase().includes(q) ||\n r.description?.toLowerCase().includes(q),\n );\n }, [roles, search]);\n\n return (\n <div>\n <PageHeader\n title=\"Roles\"\n docsHash=\"#docs:dashboard.md:roles\"\n actions={\n <button\n onClick={() => setShowCreate(true)}\n className=\"btn-primary text-xs flex items-center gap-1.5\"\n >\n + Add Role\n </button>\n }\n />\n\n {/* The standard full-width sticky filter band; the table header pins\n just beneath it on the shared 60px offset. */}\n {!isLoading && roles.length > 0 && (\n <>\n <FilterBar>\n <FilterInput\n label=\"Search\"\n value={search}\n onChange={setSearch}\n placeholder={`${roles.length} roles…`}\n />\n {search && filtered.length !== roles.length && (\n <span className=\"text-2xs text-text-quaternary tabular-nums shrink-0\">\n {filtered.length} of {roles.length}\n </span>\n )}\n </FilterBar>\n {filtered.length > 0 && (\n <div className=\"sticky top-[60px] z-10 bg-surface\">\n <TableHead />\n </div>\n )}\n </>\n )}\n\n {/* Table */}\n {isLoading ? (\n <div className=\"animate-pulse space-y-3 mt-6\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-10 bg-surface-sunken rounded w-full\" />\n ))}\n </div>\n ) : filtered.length === 0 ? (\n <p className=\"text-sm text-text-tertiary mt-8\">\n {search ? 'Clear the search to see all roles.' : 'Create a role to get started.'}\n </p>\n ) : (\n <>\n <div className=\"divide-y divide-surface-border/30\">\n {filtered.map((role) => (\n <RoleRow\n key={role.role}\n role={role}\n onClick={() => navigate(`/admin/roles/${encodeURIComponent(role.role)}`)}\n />\n ))}\n </div>\n </>\n )}\n\n <CreateRoleModal open={showCreate} onClose={() => setShowCreate(false)} />\n </div>\n );\n}\n"],"names":["CreateRoleModal","open","onClose","createRole","useCreateRole","roleName","setRoleName","useState","prevOpen","setPrevOpen","handleCreate","trimmed","jsx","Modal","jsxs","e","GRID","CELL_TEXT","HDR","CAPACITY_COL_LABEL","CELL_PY","ROW_PY","TableHead","CAPACITY_COL_VAL","RoleRow","role","onClick","displayRoleTitle","Link","RolePill","RolesPage","navigate","useNavigate","data","isLoading","useRoleDetails","showCreate","setShowCreate","search","setSearch","roles","filtered","useMemo","sorted","a","b","q","r","_a","PageHeader","Fragment","FilterBar","FilterInput","i"],"mappings":"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"}
1
+ {"version":3,"file":"RolesPage-BmsWq83e.js","sources":["../../src/pages/admin/roles/CreateRoleModal.tsx","../../src/pages/admin/roles/RolesPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useCreateRole } from '../../../api/roles';\nimport { Modal } from '../../../components/common/modal/Modal';\n\nexport function CreateRoleModal({\n open,\n onClose,\n}: {\n open: boolean;\n onClose: () => void;\n}) {\n const createRole = useCreateRole();\n const [roleName, setRoleName] = useState('');\n\n const [prevOpen, setPrevOpen] = useState(open);\n if (open !== prevOpen) {\n setPrevOpen(open);\n if (open) {\n setRoleName('');\n createRole.reset();\n }\n }\n\n const handleCreate = () => {\n const trimmed = roleName.trim().toLowerCase();\n if (!trimmed) return;\n createRole.mutate(trimmed, { onSuccess: onClose });\n };\n\n return (\n <Modal open={open} onClose={onClose} title=\"Create Role\">\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-2xs font-semibold uppercase tracking-widest text-text-tertiary mb-1\">\n Role Name (required)\n </label>\n <input\n type=\"text\"\n value={roleName}\n onChange={(e) => setRoleName(e.target.value)}\n placeholder=\"e.g., reviewer\"\n className=\"input text-xs w-full\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleCreate();\n }}\n />\n <p className=\"text-2xs text-text-tertiary mt-1\">\n Lowercase letters, numbers, hyphens, and underscores only.\n </p>\n </div>\n\n {createRole.error && (\n <p className=\"text-xs text-status-error\">{(createRole.error as Error).message}</p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button onClick={onClose} className=\"btn-secondary text-xs\">\n Cancel\n </button>\n <button\n onClick={handleCreate}\n disabled={!roleName.trim() || createRole.isPending}\n className=\"btn-primary text-xs\"\n >\n {createRole.isPending ? 'Creating...' : 'Create'}\n </button>\n </div>\n </div>\n </Modal>\n );\n}\n","import { useState, useMemo } from 'react';\nimport { useNavigate, Link } from 'react-router-dom';\nimport { useRoleDetails, type RoleDetail } from '../../../api/roles';\nimport { FilterBar, FilterInput } from '../../../components/common/data/FilterBar';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\nimport { RolePill } from '../../../components/common/display/RolePill';\nimport { displayRoleTitle } from '../../../lib/role-display';\nimport { CreateRoleModal } from './CreateRoleModal';\n\n// ── Grid columns ──────────────────────────────────────────────────────────────\n// ROLE(dot+display name, 200px) | KEY(160px) | DESCRIPTION(1fr) | PRECEDED BY(130px) | MEMBERS(88px) | CAPACITY(204px)\n// The display name leads (user-set title, else Title Case derived from the\n// key — same formatter the Pace Board uses); the exact key is the secondary\n// field. The capacity block is a single combined cell that internally renders\n// SLA/m · Target/h · Staff flush to row edges.\nconst GRID = '200px 160px 1fr 130px 88px 204px';\n\nconst CELL_TEXT = 'text-text-secondary transition-colors group-hover/row:text-text-primary';\n// Header/cell sizes match the app's list standard (DataTable): text-2xs\n// headers, text-sm body cells.\nconst HDR = 'text-2xs font-semibold uppercase tracking-widest text-text-quaternary';\n\n// ── Table header ──────────────────────────────────────────────────────────────\n\nconst CAPACITY_COL_LABEL = `${HDR} text-right flex-1 min-w-0`;\n\n// py lives on each cell, not the grid — lets the capacity div stretch flush to row edges\nconst CELL_PY = 'py-2';\nconst ROW_PY = 'py-2.5';\n\nfunction TableHead() {\n return (\n <div\n className=\"grid gap-4 px-3 border-b border-surface-border bg-surface\"\n style={{ gridTemplateColumns: GRID }}\n >\n <span className={`${HDR} ${CELL_PY} flex items-center pl-[18px]`}>Role</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Key</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Description</span>\n <span className={`${HDR} ${CELL_PY} flex items-center`}>Preceded By</span>\n <span className={`${HDR} ${CELL_PY} flex items-center justify-end`}>Members</span>\n {/* Capacity block — no py, stretches to full row height by default (grid stretch alignment) */}\n <div className=\"flex gap-3 items-center bg-surface-sunken px-3\">\n <span className={CAPACITY_COL_LABEL}>SLA/M</span>\n <span className={CAPACITY_COL_LABEL}>Target/h</span>\n <span className={CAPACITY_COL_LABEL}>Staff</span>\n </div>\n </div>\n );\n}\n\n// ── Role row ──────────────────────────────────────────────────────────────────\n\nconst CAPACITY_COL_VAL = 'text-xs tabular-nums text-right flex-1 min-w-0 transition-colors';\n\nfunction RoleRow({\n role,\n onClick,\n}: {\n role: RoleDetail;\n onClick: () => void;\n}) {\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick(); }}\n className=\"grid gap-4 px-3 cursor-pointer group/row\"\n style={{ gridTemplateColumns: GRID }}\n >\n {/* ROLE — ops status circle inline with the display name (user-set\n title, else Title Case derived from the key) */}\n <div className={`flex items-center gap-2.5 min-w-0 ${ROW_PY}`}>\n <span\n className={`w-2.5 h-2.5 rounded-full dot-ring shrink-0 ${role.ops_visible ? 'bg-status-success' : 'bg-surface-border'}`}\n title={role.ops_visible ? 'Visible in Operations' : 'Enable in role settings to show in Operations'}\n />\n <span className={`text-sm truncate ${CELL_TEXT}`}>\n {displayRoleTitle(role)}\n </span>\n </div>\n\n {/* KEY — the exact technical id */}\n <span className={`flex items-center text-xs font-mono truncate ${ROW_PY} text-text-tertiary transition-colors group-hover/row:text-text-secondary`}>\n {role.role}\n </span>\n\n {/* DESCRIPTION — min-w-0 lets the 1fr grid cell shrink so truncate can\n actually render its ellipsis */}\n <span className={`flex items-center min-w-0 text-xs text-text-tertiary truncate ${ROW_PY} transition-colors group-hover/row:text-text-secondary`}>\n {role.description ?? ''}\n </span>\n\n {/* PRECEDED BY — the universal role pill (inbox + key), linked */}\n <div className={`flex items-center min-w-0 ${ROW_PY}`} onClick={(e) => e.stopPropagation()}>\n {role.parent_role && (\n <Link\n to={`/admin/roles/${encodeURIComponent(role.parent_role)}`}\n className=\"min-w-0 truncate text-text-secondary transition-colors hover:text-accent\"\n >\n <RolePill role={role.parent_role} tone=\"inherit\" />\n </Link>\n )}\n </div>\n\n {/* MEMBER COUNT — unset renders empty, not invisible text (screen\n readers and copy/paste see exactly what the eye sees) */}\n <span className={`flex items-center justify-end text-sm tabular-nums ${ROW_PY} transition-colors ${CELL_TEXT}`}>\n {role.user_count > 0 ? role.user_count : ''}\n </span>\n\n {/* SLA/m · Target/h · Staff — no py: stretches flush to full row height */}\n <div className=\"flex gap-3 items-center bg-surface-sunken px-3\">\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.sla_minutes ?? ''}\n </span>\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.target_per_hour ?? ''}\n </span>\n <span className={`${CAPACITY_COL_VAL} ${CELL_TEXT}`}>\n {role.worker_count ?? ''}\n </span>\n </div>\n </div>\n );\n}\n\n// ── Page ──────────────────────────────────────────────────────────────────────\n\nexport function RolesPage() {\n const navigate = useNavigate();\n const { data, isLoading } = useRoleDetails();\n const [showCreate, setShowCreate] = useState(false);\n const [search, setSearch] = useState('');\n\n const roles = data?.roles ?? [];\n\n // Rows lead with the display name, so they sort by it — and the search\n // matches it too (a derived \"Cad Designer\" is findable even when no title\n // is set).\n const filtered = useMemo(() => {\n const sorted = [...roles].sort((a, b) => displayRoleTitle(a).localeCompare(displayRoleTitle(b)));\n if (!search.trim()) return sorted;\n const q = search.toLowerCase();\n return sorted.filter(\n (r) =>\n r.role.toLowerCase().includes(q) ||\n displayRoleTitle(r).toLowerCase().includes(q) ||\n r.description?.toLowerCase().includes(q),\n );\n }, [roles, search]);\n\n return (\n <div>\n <PageHeader\n title=\"Roles\"\n docsHash=\"#docs:dashboard.md:roles\"\n actions={\n <button\n onClick={() => setShowCreate(true)}\n className=\"btn-primary text-xs flex items-center gap-1.5\"\n >\n + Add Role\n </button>\n }\n />\n\n {/* The standard full-width sticky filter band; the table header pins\n just beneath it on the shared 60px offset. */}\n {!isLoading && roles.length > 0 && (\n <>\n <FilterBar>\n <FilterInput\n label=\"Search\"\n value={search}\n onChange={setSearch}\n placeholder={`${roles.length} roles…`}\n />\n {search && filtered.length !== roles.length && (\n <span className=\"text-2xs text-text-quaternary tabular-nums shrink-0\">\n {filtered.length} of {roles.length}\n </span>\n )}\n </FilterBar>\n {filtered.length > 0 && (\n <div className=\"sticky top-[60px] z-10 bg-surface\">\n <TableHead />\n </div>\n )}\n </>\n )}\n\n {/* Table */}\n {isLoading ? (\n <div className=\"animate-pulse space-y-3 mt-6\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-10 bg-surface-sunken rounded w-full\" />\n ))}\n </div>\n ) : filtered.length === 0 ? (\n <p className=\"text-sm text-text-tertiary mt-8\">\n {search ? 'Clear the search to see all roles.' : 'Create a role to get started.'}\n </p>\n ) : (\n <>\n <div className=\"divide-y divide-surface-border/30\">\n {filtered.map((role) => (\n <RoleRow\n key={role.role}\n role={role}\n onClick={() => navigate(`/admin/roles/${encodeURIComponent(role.role)}`)}\n />\n ))}\n </div>\n </>\n )}\n\n <CreateRoleModal open={showCreate} onClose={() => setShowCreate(false)} />\n </div>\n );\n}\n"],"names":["CreateRoleModal","open","onClose","createRole","useCreateRole","roleName","setRoleName","useState","prevOpen","setPrevOpen","handleCreate","trimmed","jsx","Modal","jsxs","e","GRID","CELL_TEXT","HDR","CAPACITY_COL_LABEL","CELL_PY","ROW_PY","TableHead","CAPACITY_COL_VAL","RoleRow","role","onClick","displayRoleTitle","Link","RolePill","RolesPage","navigate","useNavigate","data","isLoading","useRoleDetails","showCreate","setShowCreate","search","setSearch","roles","filtered","useMemo","sorted","a","b","q","r","_a","PageHeader","Fragment","FilterBar","FilterInput","i"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{j as a,a as v,d as ot}from"./vendor-query-DvWWuNIB.js";import{bJ as at,aK as it,bn as lt,bH as ct}from"./vendor-icons-DUYcKTjP.js";import{L as ut}from"./vendor-react-_9U4mKFr.js";import{g as w,H as dt,a as R,F as ft,j as pt,f as xt}from"./escalation-items-OWntjDaH.js";import{bl as mt,bm as bt,ax as gt,bn as W}from"./index-D7peGNN0.js";const yt=/\{\{\s*([^{}]+?)\s*\}\}/g;function le(t){return typeof t=="string"&&/\{\{[^{}]+\}\}/.test(t)}function L(t,e){if(!e)return;const s=t.indexOf("."),n=e[s===-1?t:t.slice(0,s)];if(s===-1)return n;if(!(n===null||typeof n!="object"))return w(n,t.slice(s+1))}function ce(t,e){let s=!1;const n=t.replace(yt,(r,o)=>{const i=L(o,e);return typeof i=="string"&&i.trim().length>0?i:typeof i=="number"&&Number.isFinite(i)?String(i):(s=!0,"")});return s?null:n}function M(t){return t!=null&&t!==""&&t!==!1&&t!==0}function ht(t){return t==null?"":typeof t=="object"?JSON.stringify(t):String(t)}function U(t,e){const s=t.indexOf("."),n=s===-1?t:t.slice(0,s);if(!dt.includes(n))return{known:!1,value:void 0};const r=e[n];if(s===-1)return{known:!0,value:r};try{return{known:!0,value:w(r,t.slice(s+1))}}catch{return{known:!0,value:void 0}}}function X(t,e){if(Array.isArray(t))return t.every(l=>X(l,e));if(typeof t!="string"||t.length===0||!e)return!0;const s=t.startsWith("!"),n=s?t.slice(1):t,r=n.indexOf("!="),o=n.indexOf("=");if(r!==-1||o!==-1&&o!==r+1){const l=r!==-1&&(o===-1||r<o),u=l?r:o,m=n.slice(0,u).trim(),b=n.slice(u+(l?2:1)).trim(),{known:h,value:k}=U(m,e);if(!h)return!0;const y=ht(k)===b,f=l?!y:y;return s?!f:f}const{known:i,value:c}=U(n,e);return i?s?!M(c):M(c):!0}function ue(t,e){return(t==null?void 0:t["x-lt-hide-if-empty"])!==!0?!1:e==null||e===""||e===!1||e===0}const Et="x-lt-hidden";function de(t){return(t==null?void 0:t[Et])===!0}function Nt({children:t,isRequired:e,htmlFor:s}){return a.jsxs("label",{htmlFor:s,className:"block text-2xs font-semibold uppercase tracking-wider text-text-secondary",children:[t,e&&a.jsx("span",{className:"text-status-error ml-0.5",children:"*"})]})}function At({id:t,children:e}){return a.jsx("p",{id:t,className:"text-2xs text-text-tertiary mt-0.5 leading-snug",children:e})}function fe({error:t,id:e}){return t?a.jsx("p",{id:e,role:"alert",className:"text-2xs text-status-error mt-1 animate-[field-error-in_0.3s_ease-out]",children:t}):null}function pe(t){return t?"input text-sm w-full mt-1 border-status-error/50 focus:border-status-error animate-[field-shake_0.4s_ease-in-out]":"input text-sm w-full mt-1"}function xe(t){return t?"select text-sm mt-1 min-w-[min(16rem,100%)] max-w-full border-status-error/50 focus:border-status-error animate-[field-shake_0.4s_ease-in-out]":"select text-sm mt-1 min-w-[min(16rem,100%)] max-w-full"}function wt({to:t,className:e=""}){return a.jsxs(ut,{to:t,title:"View details",tabIndex:-1,className:`inline-flex items-center justify-center w-6 h-6 shrink-0 rounded border border-surface-border text-accent/70 hover:text-accent hover:border-accent/60 hover:bg-accent/5 transition-colors ${e}`,children:[a.jsx(at,{className:"w-3.5 h-3.5",strokeWidth:2}),a.jsx("span",{className:"sr-only",children:"View details"})]})}function kt({message:t,onConfirm:e,onCancel:s,testId:n}){return a.jsxs("span",{className:"inline-flex flex-wrap items-center gap-2",role:"group","aria-label":"Confirm action","data-testid":n,children:[a.jsx("span",{className:"text-xs text-text-primary whitespace-normal",children:t}),a.jsx("button",{type:"button",onClick:e,className:"px-2 py-0.5 text-2xs font-medium text-text-inverse bg-accent rounded hover:bg-accent/90 transition-colors","data-testid":n?`${n}-yes`:void 0,autoFocus:!0,children:"Confirm"}),a.jsx("button",{type:"button",onClick:s,className:"px-2 py-0.5 text-2xs font-medium text-text-secondary border border-surface-border rounded hover:bg-surface-hover transition-colors","data-testid":n?`${n}-no`:void 0,children:"Cancel"})]})}const jt=(t,e,s)=>{s!=null&&s.badge||e()},vt=v.createContext(jt);function _t(){return v.useContext(vt)}function Tt({label:t,onConfirm:e,onCancel:s,testId:n}){const[r,o]=v.useState(""),i=()=>{r.trim()&&e(r.trim())};return a.jsxs("span",{className:"inline-flex flex-wrap items-center gap-2",role:"group","aria-label":t,"data-testid":n,children:[a.jsx("input",{type:"text",inputMode:"numeric",value:r,onChange:c=>o(c.target.value),onKeyDown:c=>{c.key==="Enter"&&i(),c.key==="Escape"&&s()},placeholder:"Code on the label","aria-label":"Code on the label",className:"input text-xs py-0.5 w-40 font-mono","data-testid":n?`${n}-input`:void 0,autoFocus:!0}),a.jsx("button",{type:"button",onClick:i,disabled:!r.trim(),className:"px-2 py-0.5 text-2xs font-medium text-text-inverse bg-accent rounded hover:bg-accent/90 disabled:opacity-40 disabled:cursor-not-allowed transition-colors","data-testid":n?`${n}-yes`:void 0,children:t}),a.jsx("button",{type:"button",onClick:s,className:"px-2 py-0.5 text-2xs font-medium text-text-secondary border border-surface-border rounded hover:bg-surface-hover transition-colors","data-testid":n?`${n}-no`:void 0,children:"Cancel"})]})}const Ct="x-lt-roster",Rt=200,H="items",Lt="any",x={WAITING:"waiting",IN:"in",FLAGGED:"flagged"},Ft="{{escalation.id}}",Y="—",J="batch_keys",B="batch_pending",Ot="batch_flags",It="accumulate_keys";function g(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function N(t){return typeof t=="string"?t?[t]:[]:Array.isArray(t)?t.filter(e=>typeof e=="string"&&e.length>0):[]}function z(t){const e=t==null?void 0:t[Ct];if(!g(e)||!g(e.expected))return;const s=e.expected;return g(s.query)||typeof s.list=="string"||s.batch===!0?e:void 0}function St(t){const e=g(t==null?void 0:t.properties)?t.properties:{},s={};for(const[n,r]of Object.entries(e)){const o=g(r)?z(r):void 0;o&&(s[n]=o)}return s}function Gt(t){return"query"in t.expected&&g(t.expected.query)?t.expected.query:null}function Dt(t,e){const s={};for(const[o,i]of Object.entries(t.facets??{})){if(typeof i!="string")continue;const c=R(i,e);if(!c||c===Y)return null;s[o]=c}const n=[...t.role?[t.role]:[],...N(t.roles)];if(!n.length&&!Object.keys(s).length)return null;const r=t.status&&t.status!==Lt?t.status:void 0;return{...n.length?{roles:n}:{},facets:s,...r?{status:r}:{},limit:Rt}}function T(t){if(g(t))return t;if(typeof t!="string"||!t)return null;try{const e=JSON.parse(t);return g(e)?e:null}catch{return null}}function $t(t){return{escalation:t,metadata:g(t.metadata)?t.metadata:T(t.metadata),envelope:T(t.envelope),payload:T(t.escalation_payload),resolver:T(t.resolver_payload)}}function Wt(t){const e=t.metadata??{},s=e[It];if(Array.isArray(s))return new Set(N(s));const n=w(t.envelope,"accumulate_items");if(g(n))return new Set(Object.keys(n));if(Array.isArray(e[B])){const r=new Set(N(e[B]));return new Set(N(e[J]).filter(o=>!r.has(o)))}return new Set}function Mt(t,e){if(!e)return t;const s=new Map;return t.flatMap(n=>{const r=(s.get(n)??0)+1;return s.set(n,r),[n,`${n}${e}${r}`]})}function Ut(t,e){const s=new Set,n=typeof t.countedBy=="string"&&t.countedBy?t.countedBy:void 0;for(const r of t.in??[H])(r===H?[...Wt(e)]:Mt(N(L(r,e)),n)).forEach(i=>s.add(i));return s}function Ht(t,e){const s=w(t.envelope,Ot),n=g(s)?s[e]:void 0;return g(n)&&typeof n.label=="string"&&n.label?n.label:void 0}function Bt(t,e){if(e)for(const s of t.flagged??[]){const n=N(s.when);if(!(!n.length||typeof s.label!="string")&&X(n,e))return s.label}}function P(t){const e=t.escalation.created_at,s=e instanceof Date?e.getTime():typeof e=="string"?Date.parse(e):NaN;return Number.isNaN(s)?0:s}function Pt(t,e,s){var c;const n=new Set,r=[],o=(l,u={})=>{!l||l===Y||n.has(l)||(n.add(l),r.push({key:l,...u}))},i=t.expected;if("query"in i){const l=t.key??Ft,u=new Map;for(const m of s){const b=R(l,m),h=u.get(b);(!h||P(m)>P(h))&&u.set(b,m)}for(const m of s){const b=R(l,m);o(b,{row:u.get(b)})}}else if("list"in i)N(L(i.list,e)).forEach(l=>o(l));else{const l=i.labels?L(i.labels,e):void 0;for(const u of N((c=e.metadata)==null?void 0:c[J]))o(u,g(l)&&u in l?{label:l[u]}:{})}return r}function V(t,e,s=[]){const n=Ut(t,e),r="batch"in t.expected,o=Pt(t,e,s).map(l=>{if(n.has(l.key))return{...l,state:x.IN};const u=r?Ht(e,l.key):Bt(t,l.row);return u?{...l,state:x.FLAGGED,flag:u}:{...l,state:x.WAITING}}),i=l=>o.filter(u=>u.state===l).length,c=i(x.WAITING);return{entries:o,counts:{expected:o.length,in:i(x.IN),flagged:i(x.FLAGGED),waiting:c,outstanding:c}}}function Vt(t){const{counts:e}=t,s=[`${e.in} of ${e.expected} in`],n=new Map;for(const r of t.entries)r.state===x.FLAGGED&&r.flag&&n.set(r.flag,(n.get(r.flag)??0)+1);for(const[r,o]of n)s.push(`${o} ${r}`);return s.join(" · ")}const K={waiting:"Waiting",in:"In",flagged:"Flagged"};function Kt({state:t}){return t===x.IN?a.jsx(it,{className:"w-4 h-4 text-status-success shrink-0",strokeWidth:2,"aria-hidden":!0}):t===x.FLAGGED?a.jsx(lt,{className:"w-4 h-4 text-status-warning shrink-0",strokeWidth:2,"aria-hidden":!0}):a.jsx(ct,{className:"w-4 h-4 text-text-tertiary shrink-0",strokeWidth:2,"aria-hidden":!0})}function qt({entry:t,item:e,hostCtx:s,columns:n,offered:r,asking:o,pending:i,error:c,onAction:l,onConfirm:u,onCancel:m}){var y;const b=t.row??s,h=t.state===x.IN,k=t.state===x.FLAGGED?t.flag??K.flagged:K[t.state];return a.jsxs("li",{className:"py-2 flex items-start gap-3 border-b border-surface-border/50 last:border-0","data-testid":`roster-entry-${t.key}`,"data-state":t.state,children:[a.jsx("span",{className:"pt-0.5",children:a.jsx(Kt,{state:t.state})}),a.jsx("div",{className:"min-w-0 flex-1 flex flex-wrap items-baseline gap-x-4 gap-y-0.5",children:n.map(f=>a.jsxs("span",{className:`text-sm min-w-0 ${h?"text-text-secondary":"text-text-primary"}`,children:[a.jsxs("span",{className:"sr-only",children:[f.label,": "]}),a.jsx(ft,{raw:pt(f.value,e,b),format:f.format})]},f.label))}),a.jsx("span",{className:`text-xs font-medium shrink-0 pt-0.5 ${h?"text-status-success":t.state===x.FLAGGED?"text-status-warning":"text-text-tertiary"}`,"data-testid":`roster-state-${t.key}`,children:k}),(r.length>0||o||c)&&a.jsxs("span",{className:"shrink-0 flex flex-col items-end gap-0.5",children:[(y=o==null?void 0:o.action.fill)!=null&&y.enterCode?a.jsx(Tt,{label:o.action.label,onConfirm:f=>u(f),onCancel:m,testId:`roster-code-${t.key}`}):o?a.jsx(kt,{message:R(o.action.confirm??"",b),onConfirm:()=>u(),onCancel:m,testId:`roster-confirm-${t.key}`}):a.jsx("span",{className:"flex gap-1",children:r.map(f=>a.jsx("button",{type:"button",onClick:()=>l(f),disabled:i,className:"px-2 py-0.5 text-2xs font-medium text-accent border border-accent/40 rounded hover:bg-accent/10 disabled:opacity-40 disabled:cursor-not-allowed transition-colors","data-testid":`roster-action-${t.key}`,children:f.action.label},f.index))}),c&&a.jsx("span",{className:"text-2xs text-status-error max-w-[16rem] text-right","data-testid":`roster-error-${t.key}`,children:c})]}),t.row&&a.jsx(wt,{to:`/escalations/detail/${String(t.row.escalation.id)}`})]})}function Qt(t,e){const s=Gt(t),n=s?Dt(s,e):null;return n?{roles:n.roles,status:n.status,facets:n.facets,limit:n.limit,include:xt({key:t.key,flagged:t.flagged})?"envelope":void 0}:null}function Xt(t,e){const s=Object.entries(St(t)),n=e??{},r=s.map(([,c])=>Qt(c,n)),o=ot({queries:r.map(c=>({...mt(c??{}),enabled:!!c}))}),i={};return s.forEach(([c,l],u)=>{if(!r[u]){i[c]=V(l,n);return}const m=o[u];if(!m.isSuccess){i[c]=null;return}const b=m.data.escalations.map(h=>$t(h));i[c]=V(l,n,b)}),i}function me(t){const e=Object.entries(t).filter(([,s])=>s!==null);return e.length?Object.fromEntries(e.map(([s,n])=>[s,n.counts])):null}const Yt="x-lt-actions",_={RESOLVE:"resolve",FILL:"fill",FLAG:"flag",UNFLAG:"unflag"},Jt={resolve:[x.WAITING],fill:[x.WAITING,x.FLAGGED],flag:[x.WAITING],unflag:[x.FLAGGED]};function C(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function Z(t){const e=[];return C(t.resolverPayload)&&e.push(_.RESOLVE),C(t.fill)&&e.push(_.FILL),C(t.flag)&&typeof t.flag.label=="string"&&t.flag.label&&e.push(_.FLAG),t.unflag===!0&&e.push(_.UNFLAG),e.length===1?e[0]:null}function zt(t){const e=t==null?void 0:t[Yt];return Array.isArray(e)?e.filter(s=>C(s)&&typeof s.label=="string"&&!!s.label&&Z(s)!==null):[]}function Zt(t,e,s){const n=Z(t);return!n||(n===_.RESOLVE?!e.row:!s)?!1:(t.when===void 0?Jt[n]:Array.isArray(t.when)?t.when:[t.when]).includes(e.state)}const te=[{label:"Item",value:"{{item.itemKey}}"}],q="continue",ee=[W.NEEDS_BADGE,W.ROLE_REFUSED];function A(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function Q(t,e,s){const n=w(t.envelope,e),r=A(n)?n[s]:void 0;return typeof r=="string"?r:void 0}function ne(t,e){const s=w(e.envelope,"accumulate_items"),n=w(e.envelope,"batch_items"),r=A(s)&&A(s[t.key])?s[t.key]:null,o=r&&A(r.payload)?r.payload:A(n)&&A(n[t.key])?n[t.key]:void 0,i=typeof(r==null?void 0:r.at)=="string"?r.at:r?void 0:Q(e,"batch_filled_at",t.key),c=typeof(r==null?void 0:r.actor)=="string"?r.actor:r?void 0:Q(e,"batch_filled_by",t.key);return{itemKey:t.key,at:i??"",...o?{payload:o}:{},...c?{actor:c}:{},...t.label!==void 0?{label:t.label}:{}}}function be({fieldKey:t,schema:e,escalationContext:s}){var G;const n=s??{},r=Xt({properties:{[t]:e??{}}},n)[t],o=z(e),i=!!o&&"batch"in o.expected,c=(e==null?void 0:e["x-lt-columns"])??te,l=zt(e),u=(e==null?void 0:e.title)??"Expected items",m=e==null?void 0:e.description,b=String(((G=n.escalation)==null?void 0:G.id)??""),h=bt(),k=_t(),[y,f]=v.useState(null),[tt,F]=v.useState(null),[et,O]=v.useState({}),I=async(d,p,E)=>{F(d.key),O(j=>({...j,[d.key]:""}));try{await h.mutateAsync({id:b,field:t,action:p.index,entry:d.key,code:E})}catch(j){const D=j.message;O(rt=>({...rt,[d.key]:D}));const $=j instanceof gt&&A(j.body)?j.body.outcome:void 0;typeof $=="string"&&ee.includes($)&&k(q,()=>I(d,p,E),{badge:D})}finally{F(null)}},S=(d,p,E)=>{f(null),k(q,()=>I(d,p,E))},nt=(d,p)=>{var E;p.action.confirm||(E=p.action.fill)!=null&&E.enterCode?f({key:d.key,offer:p}):S(d,p)},st=d=>l.map((p,E)=>({action:p,index:E})).filter(({action:p})=>Zt(p,d,i));return a.jsxs("div",{"data-field-key":t,"data-testid":`roster-widget-${t}`,children:[a.jsxs("div",{className:"flex items-baseline justify-between gap-3",children:[a.jsx(Nt,{children:u}),r&&a.jsx("span",{className:"text-sm font-medium text-text-primary","data-testid":`roster-headline-${t}`,children:Vt(r)})]}),m&&a.jsx(At,{children:m}),r?r.entries.length===0?a.jsx("p",{className:"text-xs text-text-tertiary italic py-2","data-testid":`roster-empty-${t}`,children:"Nothing expected."}):a.jsx("ul",{className:"mt-1","data-testid":`roster-list-${t}`,children:r.entries.map(d=>a.jsx(qt,{entry:d,item:ne(d,n),hostCtx:n,columns:c,offered:b?st(d):[],asking:(y==null?void 0:y.key)===d.key?y.offer:null,pending:tt===d.key,error:et[d.key]||void 0,onAction:p=>nt(d,p),onConfirm:p=>y&&S(d,y.offer,p),onCancel:()=>f(null)},d.key))}):a.jsx("p",{className:"text-xs text-text-tertiary py-2","data-testid":`roster-loading-${t}`,children:"Loading expected items…"})]})}export{vt as E,Nt as F,kt as I,wt as O,be as R,Yt as X,L as a,de as b,At as c,_t as d,X as e,fe as f,pe as g,le as h,ce as i,ue as j,$t as k,me as r,xe as s,Xt as u};
2
+ //# sourceMappingURL=RosterWidget-DR-T4Izu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RosterWidget-DR-T4Izu.js","sources":["../../../shared/form-validation/ctx-path.ts","../../../shared/form-validation/x-lt-show-if.ts","../../src/components/escalation/resolver-form/FieldChrome.tsx","../../src/components/escalation/widgets/OpenDetailButton.tsx","../../src/components/escalation/widgets/InlineConfirm.tsx","../../src/components/escalation/widgets/embed-write.ts","../../src/components/escalation/widgets/InlineCodeEntry.tsx","../../../shared/form-validation/x-lt-roster.ts","../../src/components/escalation/widgets/RosterEntryRow.tsx","../../src/hooks/useRosters.ts","../../../shared/form-validation/x-lt-actions.ts","../../src/components/escalation/widgets/RosterWidget.tsx"],"sourcesContent":["/**\n * Context-path resolution shared by every token that names a `\"domain.path\"`.\n *\n * A path may embed `{{domain.path}}` interpolation segments — the same\n * grammar as x-lt-help tokens — resolved against the live context before the\n * walk. This is what makes cascading selects work: a field can declare\n *\n * x-lt-options: \"lookup.geo.regions.{{resolver.country}}\"\n *\n * and its option list follows the submitter's country answer, re-resolved\n * locally on every edit. An unresolved segment means \"no concrete path yet\",\n * never a literal placeholder.\n */\nimport { getDeep } from './x-lt-bind';\n\nexport const INTERPOLATION_PATTERN = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\n/** Whether a token embeds `{{domain.path}}` interpolation segments. */\nexport function hasInterpolation(token: unknown): token is string {\n return typeof token === 'string' && /\\{\\{[^{}]+\\}\\}/.test(token);\n}\n\n/** Resolve a concrete `\"domain.path\"` (x-lt-bind syntax, `a.b[0].c`) against the context. */\nexport function resolveCtxPath(\n path: string,\n ctx: Record<string, unknown> | undefined,\n): unknown {\n if (!ctx) return undefined;\n const dot = path.indexOf('.');\n const root = ctx[dot === -1 ? path : path.slice(0, dot)];\n if (dot === -1) return root;\n if (root === null || typeof root !== 'object') return undefined;\n return getDeep(root, path.slice(dot + 1));\n}\n\n/**\n * Interpolate every `{{domain.path}}` segment against the context. Returns\n * the concrete path, or null when any segment resolves to nothing (missing,\n * null, empty string) or to a non-scalar — an unanswered cascade parent\n * yields no path, so the dependent field offers nothing.\n */\nexport function interpolatePath(\n template: string,\n ctx: Record<string, unknown> | undefined,\n): string | null {\n let unresolved = false;\n const concrete = template.replace(INTERPOLATION_PATTERN, (_match, rawPath: string) => {\n const value = resolveCtxPath(rawPath, ctx);\n if (typeof value === 'string' && value.trim().length > 0) return value;\n if (typeof value === 'number' && Number.isFinite(value)) return String(value);\n unresolved = true;\n return '';\n });\n return unresolved ? null : concrete;\n}\n","/**\n * x-lt-showIf — conditional field visibility. A form_schema property may\n * declare `x-lt-showIf: \"domain.path\"` to show the field only when the value\n * at that path is present and truthy. Prefix `!` to invert: show when absent\n * or falsy.\n *\n * Equality forms compare the resolved value's string form:\n * \"domain.path=VALUE\" — show when String(value) equals VALUE\n * \"domain.path!=VALUE\" — show when it does not\n * An absent value compares as the empty string, so `=X` is false and `!=X` is\n * true when the path is missing. VALUE is the raw remainder after the operator\n * (trimmed; no quoting). The truthy/! forms are unchanged.\n *\n * Domains follow the same `domain.path` convention as x-lt-help tokens:\n * metadata — the escalation row's metadata dict\n * payload — the escalation context payload (escalation_payload)\n * envelope — the workflow-sent input envelope\n * escalation — top-level escalation row fields\n * resolver — the submitted resolver payload\n * roster (each roster field's counts: `roster.orders.outstanding=0`)\n *\n * A missing or non-string condition always shows the field (opt-in, safe default).\n */\nimport { getDeep } from './x-lt-bind';\nimport { HELP_DOMAINS, type HelpTokenContext } from './x-lt-help';\n\nexport type ShowIfContext = HelpTokenContext;\n\nfunction isTruthy(value: unknown): boolean {\n return value !== null && value !== undefined && value !== '' && value !== false && value !== 0;\n}\n\n/** The value's comparable string form; absent compares as the empty string. */\nfunction asComparable(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\nfunction resolvePath(expr: string, ctx: ShowIfContext): { known: boolean; value: unknown } {\n const dot = expr.indexOf('.');\n const domain = (dot === -1 ? expr : expr.slice(0, dot)) as (typeof HELP_DOMAINS)[number];\n if (!(HELP_DOMAINS as readonly string[]).includes(domain)) return { known: false, value: undefined };\n const root = ctx[domain];\n if (dot === -1) return { known: true, value: root };\n try {\n return { known: true, value: getDeep(root, expr.slice(dot + 1)) };\n } catch {\n return { known: true, value: undefined };\n }\n}\n\n/**\n * Evaluates an x-lt-showIf condition. Returns true when the field should be\n * shown. Returns true when condition is absent, non-string, or context is null.\n * An array of conditions shows the field only when every one holds.\n */\nexport function evaluateShowIf(condition: unknown, ctx: ShowIfContext | null | undefined): boolean {\n if (Array.isArray(condition)) return condition.every((c) => evaluateShowIf(c, ctx));\n if (typeof condition !== 'string' || condition.length === 0) return true;\n if (!ctx) return true;\n const negate = condition.startsWith('!');\n const expr = negate ? condition.slice(1) : condition;\n\n // Equality forms — `!=` first so it never parses as `=` with a `!` in the path.\n const neqAt = expr.indexOf('!=');\n const eqAt = expr.indexOf('=');\n if (neqAt !== -1 || (eqAt !== -1 && eqAt !== neqAt + 1)) {\n const isNeq = neqAt !== -1 && (eqAt === -1 || neqAt < eqAt);\n const opAt = isNeq ? neqAt : eqAt;\n const path = expr.slice(0, opAt).trim();\n const expected = expr.slice(opAt + (isNeq ? 2 : 1)).trim();\n const { known, value } = resolvePath(path, ctx);\n if (!known) return true; // unknown domain — safe default, same as truthy form\n const matches = asComparable(value) === expected;\n const result = isNeq ? !matches : matches;\n return negate ? !result : result;\n }\n\n const { known, value } = resolvePath(expr, ctx);\n if (!known) return true;\n return negate ? !isTruthy(value) : isTruthy(value);\n}\n\n/**\n * `x-lt-hide-if-empty`: the field is suppressed while its value is empty\n * (null, undefined, '', false, or 0). Render and validation share this test.\n */\nexport function isHiddenIfEmpty(fieldSchema: Record<string, unknown> | undefined, value: unknown): boolean {\n if (fieldSchema?.['x-lt-hide-if-empty'] !== true) return false;\n return value === null || value === undefined || value === '' || value === false || value === 0;\n}\n\n/**\n * `x-lt-hidden: true`: the field never renders. A value set by the caller\n * (a workflow default, an API submitter) passes through to the payload and\n * is checked against the field's type and enum; the field is never required,\n * since no one on the form can answer it.\n */\nexport const X_LT_HIDDEN = 'x-lt-hidden';\n\nexport function isHiddenField(fieldSchema: Record<string, unknown> | undefined): boolean {\n return fieldSchema?.[X_LT_HIDDEN] === true;\n}\n","/**\n * Shared field chrome for the resolver form: label, helper, error, and the\n * input class recipes. One place styles every generated field.\n */\n\nexport function FieldLabel({ children, isRequired, htmlFor }: {\n children: React.ReactNode;\n isRequired?: boolean;\n htmlFor?: string;\n}) {\n // `block` is load-bearing: a <label> is inline by default, and the\n // label→instruction→control anatomy would then hold only by accident —\n // a full-width control wraps below, but a narrow control (number,\n // max-w-48) with no instruction line sits flush BESIDE the label. The\n // anatomy is a column; the label declares it.\n return (\n <label htmlFor={htmlFor} className=\"block text-2xs font-semibold uppercase tracking-wider text-text-secondary\">\n {children}\n {isRequired && <span className=\"text-status-error ml-0.5\">*</span>}\n </label>\n );\n}\n\nexport function FieldHelper({ id, children }: { id?: string; children: React.ReactNode }) {\n return (\n <p id={id} className=\"text-2xs text-text-tertiary mt-0.5 leading-snug\">\n {children}\n </p>\n );\n}\n\nexport function FieldError({ error, id }: { error?: string; id?: string }) {\n if (!error) return null;\n return (\n <p id={id} role=\"alert\" className=\"text-2xs text-status-error mt-1 animate-[field-error-in_0.3s_ease-out]\">\n {error}\n </p>\n );\n}\n\nexport function inputClass(hasError?: boolean): string {\n return hasError\n ? 'input text-sm w-full mt-1 border-status-error/50 focus:border-status-error animate-[field-shake_0.4s_ease-in-out]'\n : 'input text-sm w-full mt-1';\n}\n\n// Select shares the field recipe but adds the unified chevron (via .select).\n// Width follows the content, floored for presence and capped by the cell —\n// a one-word choice never stretches across the measure. The floor is\n// min(16rem, 100%): CSS lets min-width beat max-width, so a bare min-w-64\n// would overflow a cell narrower than 16rem — the cell always wins.\nexport function selectClass(hasError?: boolean): string {\n return hasError\n ? 'select text-sm mt-1 min-w-[min(16rem,100%)] max-w-full border-status-error/50 focus:border-status-error animate-[field-shake_0.4s_ease-in-out]'\n : 'select text-sm mt-1 min-w-[min(16rem,100%)] max-w-full';\n}\n","import { Link } from 'react-router-dom';\nimport { ArrowUpRight } from 'lucide-react';\n\n/**\n * The one affordance for opening an embedded row's detail page — a clear\n * icon-in-a-button, not a bare glyph, so the gesture reads as \"open this\". The\n * tooltip stays generic (\"View details\") rather than naming the record kind, so\n * the same control fits any embed. Non-focusable: the row/card click and the\n * canned actions carry the keyboard path; this is a pointer shortcut.\n */\nexport function OpenDetailButton({ to, className = '' }: { to: string; className?: string }) {\n return (\n <Link\n to={to}\n title=\"View details\"\n tabIndex={-1}\n className={`inline-flex items-center justify-center w-6 h-6 shrink-0 rounded border border-surface-border text-accent/70 hover:text-accent hover:border-accent/60 hover:bg-accent/5 transition-colors ${className}`}\n >\n <ArrowUpRight className=\"w-3.5 h-3.5\" strokeWidth={2} />\n <span className=\"sr-only\">View details</span>\n </Link>\n );\n}\n","/**\n * A confirmation asked in place, beside the control that asked it. Used by\n * embedded row actions instead of a browser dialog, which blocks a shared\n * station and every other tab.\n */\nexport function InlineConfirm({ message, onConfirm, onCancel, testId }: {\n message: string;\n onConfirm: () => void;\n onCancel: () => void;\n testId?: string;\n}) {\n return (\n <span className=\"inline-flex flex-wrap items-center gap-2\" role=\"group\" aria-label=\"Confirm action\" data-testid={testId}>\n <span className=\"text-xs text-text-primary whitespace-normal\">{message}</span>\n <button\n type=\"button\"\n onClick={onConfirm}\n className=\"px-2 py-0.5 text-2xs font-medium text-text-inverse bg-accent rounded hover:bg-accent/90 transition-colors\"\n data-testid={testId ? `${testId}-yes` : undefined}\n autoFocus\n >\n Confirm\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-2 py-0.5 text-2xs font-medium text-text-secondary border border-surface-border rounded hover:bg-surface-hover transition-colors\"\n data-testid={testId ? `${testId}-no` : undefined}\n >\n Cancel\n </button>\n </span>\n );\n}\n","import { createContext, useContext } from 'react';\n\nexport interface EmbedWriteOptions {\n /**\n * The action needs a badge (a role the person acting lacks): hold it for\n * the next badge, saying this. A page that takes no badges drops it.\n */\n badge?: string;\n}\n\n/** Runs an embedded row action, or holds it until the page can run it (a badge at a shared station). */\nexport type EmbedWriteGuard = (verb: string, run: () => Promise<void>, options?: EmbedWriteOptions) => void;\n\nconst runNow: EmbedWriteGuard = (_verb, run, options) => {\n if (!options?.badge) void run();\n};\n\n/**\n * The host page's gate for writes an embedded widget makes on other rows\n * (roster and list actions). Absent a provider, actions run directly.\n */\nexport const EmbedWriteContext = createContext<EmbedWriteGuard>(runNow);\n\nexport function useEmbedWrite(): EmbedWriteGuard {\n return useContext(EmbedWriteContext);\n}\n","import { useState } from 'react';\n\n/**\n * A code typed in place, beside the control that asked for it: a key-in for\n * an item whose barcode will not scan. The server checks the code against\n * the entry; Enter confirms.\n */\nexport function InlineCodeEntry({ label, onConfirm, onCancel, testId }: {\n label: string;\n onConfirm: (code: string) => void;\n onCancel: () => void;\n testId?: string;\n}) {\n const [code, setCode] = useState('');\n const submit = () => { if (code.trim()) onConfirm(code.trim()); };\n return (\n <span className=\"inline-flex flex-wrap items-center gap-2\" role=\"group\" aria-label={label} data-testid={testId}>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') submit();\n if (e.key === 'Escape') onCancel();\n }}\n placeholder=\"Code on the label\"\n aria-label=\"Code on the label\"\n className=\"input text-xs py-0.5 w-40 font-mono\"\n data-testid={testId ? `${testId}-input` : undefined}\n autoFocus\n />\n <button\n type=\"button\"\n onClick={submit}\n disabled={!code.trim()}\n className=\"px-2 py-0.5 text-2xs font-medium text-text-inverse bg-accent rounded hover:bg-accent/90 disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n data-testid={testId ? `${testId}-yes` : undefined}\n >\n {label}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-2 py-0.5 text-2xs font-medium text-text-secondary border border-surface-border rounded hover:bg-surface-hover transition-colors\"\n data-testid={testId ? `${testId}-no` : undefined}\n >\n Cancel\n </button>\n </span>\n );\n}\n","/**\n * x-lt-roster: every member a container row expects, each with its state.\n * A form field declares `x-lt-widget: \"roster\"` and an `x-lt-roster` block:\n *\n * \"x-lt-roster\": {\n * \"expected\": { \"query\": { \"roles\": [\"packing\"], \"facets\": { \"box\": \"{{escalation.id}}\" } } },\n * \"key\": \"{{metadata.orderId}}\",\n * \"in\": [\"items\", \"resolver.bags\"],\n * \"flagged\": [{ \"when\": \"resolver.outcome=missing\", \"label\": \"Missing\" }]\n * }\n *\n * The expected set is one of:\n * - `query`: member rows matched by role(s) and facets, any status unless\n * `status` narrows it. `key` names each row's entry (default its id).\n * - `list`: a `domain.path` naming an array of keys on the host row.\n * - `batch`: the host row's batch keys; `labels` names a per-key map.\n *\n * An entry is `in` when its key is held by the host row (`items`: the\n * accumulated keys, or a batch's filled keys) or appears in a named context\n * value (`resolver.<field>`, a string or an array). It is `flagged` when its\n * member row matches a `flagged` rule (or, on a batch set, when its key\n * carries a flag in `envelope.batch_flags`), and `waiting` otherwise. In wins over\n * flagged, so a member flagged on its own row and then added reads in.\n *\n * `countedBy` names the separator of counted keys (`<code>#1`, `<code>#2`):\n * the nth occurrence of a code in a named context value ticks `<code>#n`,\n * so two scans of one code tick both of its keys.\n *\n * Isomorphic: the dashboard renders the widget and the form's `roster.*`\n * context from it, and the API layer computes the same counts to evaluate\n * showIf and the roster submit guard at resolve.\n */\nimport { getDeep } from './x-lt-bind';\nimport { resolveCtxPath } from './ctx-path';\nimport { interpolateHelp, type HelpTokenContext } from './x-lt-help';\nimport { evaluateShowIf } from './x-lt-show-if';\n\nexport const X_LT_ROSTER = 'x-lt-roster';\nexport const ROSTER_WIDGET = 'roster';\n/** The context domain the counts are read from: `roster.<field>.outstanding`. */\nexport const ROSTER_DOMAIN = 'roster';\n/** The most member rows a query set reads. */\nexport const ROSTER_QUERY_LIMIT = 200;\n/** The `in` source naming the host row's held keys. */\nexport const ROSTER_ITEMS_SOURCE = 'items';\n/** `query.status` value that reads every status (the default). */\nexport const ROSTER_ANY_STATUS = 'any';\n\nexport const ROSTER_STATES = { WAITING: 'waiting', IN: 'in', FLAGGED: 'flagged' } as const;\nexport type RosterState = (typeof ROSTER_STATES)[keyof typeof ROSTER_STATES];\n\nexport interface RosterQuery {\n role?: string;\n roles?: string[];\n facets?: Record<string, string>;\n /** A status to narrow to; omitted or `any` reads every status. */\n status?: string;\n}\n\nexport type RosterExpected =\n | { query: RosterQuery }\n | { list: string }\n | { batch: true; labels?: string };\n\nexport interface RosterFlagRule {\n /** An x-lt-showIf condition over the member row's own context. */\n when: string | string[];\n label: string;\n}\n\nexport interface RosterDef {\n expected: RosterExpected;\n key?: string;\n in?: string[];\n flagged?: RosterFlagRule[];\n /** The separator of counted keys: the nth occurrence of a code ticks `<code><sep>n`. */\n countedBy?: string;\n}\n\n/** One member row's token context (the row's own domains). */\nexport type RosterRow = HelpTokenContext & { escalation: Record<string, unknown> };\n\nexport interface RosterEntry {\n key: string;\n state: RosterState;\n /** The matching flag rule's label, when flagged. */\n flag?: string;\n /** The member row, for a query set. */\n row?: RosterRow;\n /** The per-key label value from a batch's `labels` map. */\n label?: unknown;\n}\n\nexport interface RosterCounts {\n expected: number;\n in: number;\n flagged: number;\n waiting: number;\n /** Entries neither in nor flagged: the count a roster guard waits on. */\n outstanding: number;\n}\n\nexport interface RosterResult {\n entries: RosterEntry[];\n counts: RosterCounts;\n}\n\nconst DEFAULT_KEY = '{{escalation.id}}';\nconst MISSING = '—';\nconst BATCH_KEYS = 'batch_keys';\nconst BATCH_PENDING = 'batch_pending';\nconst BATCH_FLAGS = 'batch_flags';\nconst ACCUMULATE_KEYS = 'accumulate_keys';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strings(v: unknown): string[] {\n if (typeof v === 'string') return v ? [v] : [];\n if (!Array.isArray(v)) return [];\n return v.filter((s): s is string => typeof s === 'string' && s.length > 0);\n}\n\n/** The field's roster declaration, or undefined when it declares none or an unusable one. */\nexport function readRosterDef(fieldSchema: Record<string, unknown> | null | undefined): RosterDef | undefined {\n const raw = fieldSchema?.[X_LT_ROSTER];\n if (!isRecord(raw) || !isRecord(raw.expected)) return undefined;\n const e = raw.expected;\n const hasSource = isRecord(e.query) || typeof e.list === 'string' || e.batch === true;\n return hasSource ? (raw as unknown as RosterDef) : undefined;\n}\n\n/** Every roster field of a form schema, by field name. */\nexport function readRosterDefs(schema: Record<string, unknown> | null | undefined): Record<string, RosterDef> {\n const properties = isRecord(schema?.properties) ? (schema!.properties as Record<string, unknown>) : {};\n const defs: Record<string, RosterDef> = {};\n for (const [field, def] of Object.entries(properties)) {\n const roster = isRecord(def) ? readRosterDef(def) : undefined;\n if (roster) defs[field] = roster;\n }\n return defs;\n}\n\n/** The roster's member-row query, or null when its set is not a query. */\nexport function rosterQueryOf(def: RosterDef): RosterQuery | null {\n return 'query' in def.expected && isRecord(def.expected.query) ? def.expected.query : null;\n}\n\n/** The query's concrete params: facets interpolated, `any` status dropped. Null when the scope is empty. */\nexport function resolveRosterQuery(\n query: RosterQuery,\n ctx: HelpTokenContext,\n): { roles?: string[]; facets: Record<string, string>; status?: string; limit: number } | null {\n const facets: Record<string, string> = {};\n for (const [k, v] of Object.entries(query.facets ?? {})) {\n if (typeof v !== 'string') continue;\n const resolved = interpolateHelp(v, ctx);\n if (!resolved || resolved === MISSING) return null;\n facets[k] = resolved;\n }\n const roles = [...(query.role ? [query.role] : []), ...strings(query.roles)];\n if (!roles.length && !Object.keys(facets).length) return null;\n const status = query.status && query.status !== ROSTER_ANY_STATUS ? query.status : undefined;\n return { ...(roles.length ? { roles } : {}), facets, ...(status ? { status } : {}), limit: ROSTER_QUERY_LIMIT };\n}\n\nfunction parseJson(v: unknown): Record<string, unknown> | null {\n if (isRecord(v)) return v;\n if (typeof v !== 'string' || !v) return null;\n try {\n const parsed = JSON.parse(v);\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/** A member row's token context, from a row whose JSON columns are strings or objects. */\nexport function rosterRowContext(row: Record<string, unknown>): RosterRow {\n return {\n escalation: row,\n metadata: isRecord(row.metadata) ? row.metadata : parseJson(row.metadata),\n envelope: parseJson(row.envelope),\n payload: parseJson(row.escalation_payload),\n resolver: parseJson(row.resolver_payload),\n };\n}\n\n/** The keys the host row holds: its accumulated keys, or a batch's filled keys. */\nfunction heldKeys(ctx: HelpTokenContext): Set<string> {\n const metadata = ctx.metadata ?? {};\n const accumulated = metadata[ACCUMULATE_KEYS];\n if (Array.isArray(accumulated)) return new Set(strings(accumulated));\n const envelopeItems = getDeep(ctx.envelope, 'accumulate_items');\n if (isRecord(envelopeItems)) return new Set(Object.keys(envelopeItems));\n if (Array.isArray(metadata[BATCH_PENDING])) {\n const pending = new Set(strings(metadata[BATCH_PENDING]));\n return new Set(strings(metadata[BATCH_KEYS]).filter((k) => !pending.has(k)));\n }\n return new Set();\n}\n\n/** Each value as given, and with `countedBy` its nth occurrence also as `<value><sep>n`. */\nfunction countedKeys(values: string[], separator: string | undefined): string[] {\n if (!separator) return values;\n const seen = new Map<string, number>();\n return values.flatMap((v) => {\n const n = (seen.get(v) ?? 0) + 1;\n seen.set(v, n);\n return [v, `${v}${separator}${n}`];\n });\n}\n\nfunction arrivedKeys(def: RosterDef, ctx: HelpTokenContext): Set<string> {\n const arrived = new Set<string>();\n const separator = typeof def.countedBy === 'string' && def.countedBy ? def.countedBy : undefined;\n for (const source of def.in ?? [ROSTER_ITEMS_SOURCE]) {\n const keys = source === ROSTER_ITEMS_SOURCE\n ? [...heldKeys(ctx)]\n : countedKeys(strings(resolveCtxPath(source, ctx as Record<string, unknown>)), separator);\n keys.forEach((k) => arrived.add(k));\n }\n return arrived;\n}\n\n/** A batch key's flag label, from `envelope.batch_flags[key].label`. */\nfunction batchFlagLabel(ctx: HelpTokenContext, key: string): string | undefined {\n const flags = getDeep(ctx.envelope, BATCH_FLAGS);\n const flag = isRecord(flags) ? flags[key] : undefined;\n return isRecord(flag) && typeof flag.label === 'string' && flag.label ? flag.label : undefined;\n}\n\nfunction flagFor(def: RosterDef, row: RosterRow | undefined): string | undefined {\n if (!row) return undefined;\n for (const rule of def.flagged ?? []) {\n const conditions = strings(rule.when);\n if (!conditions.length || typeof rule.label !== 'string') continue;\n if (evaluateShowIf(conditions, row)) return rule.label;\n }\n return undefined;\n}\n\n/** A row's creation instant in ms; 0 when absent or unreadable. */\nfunction createdAt(row: RosterRow): number {\n const raw = row.escalation.created_at;\n const ms = raw instanceof Date ? raw.getTime() : typeof raw === 'string' ? Date.parse(raw) : NaN;\n return Number.isNaN(ms) ? 0 : ms;\n}\n\n/**\n * The expected keys in order, each with its member row (query) or label\n * (batch). Several rows sharing a key (a member that parked again) read as\n * one entry on the newest row, so its state and actions follow the live row.\n */\nfunction expectedEntries(\n def: RosterDef,\n ctx: HelpTokenContext,\n rows: RosterRow[],\n): Array<{ key: string; row?: RosterRow; label?: unknown }> {\n const seen = new Set<string>();\n const out: Array<{ key: string; row?: RosterRow; label?: unknown }> = [];\n const push = (key: string, extra: { row?: RosterRow; label?: unknown } = {}) => {\n if (!key || key === MISSING || seen.has(key)) return;\n seen.add(key);\n out.push({ key, ...extra });\n };\n const e = def.expected;\n if ('query' in e) {\n const template = def.key ?? DEFAULT_KEY;\n const newest = new Map<string, RosterRow>();\n for (const row of rows) {\n const key = interpolateHelp(template, row);\n const held = newest.get(key);\n if (!held || createdAt(row) > createdAt(held)) newest.set(key, row);\n }\n for (const row of rows) {\n const key = interpolateHelp(template, row);\n push(key, { row: newest.get(key) });\n }\n } else if ('list' in e) {\n strings(resolveCtxPath(e.list, ctx as Record<string, unknown>)).forEach((k) => push(k));\n } else {\n const labels = e.labels ? resolveCtxPath(e.labels, ctx as Record<string, unknown>) : undefined;\n for (const key of strings(ctx.metadata?.[BATCH_KEYS])) {\n push(key, isRecord(labels) && key in labels ? { label: labels[key] } : {});\n }\n }\n return out;\n}\n\n/**\n * Every expected entry with its state, and the counts. `rows` are the\n * member rows a query set read (ignored for list and batch sets). `ctx` is\n * the host row's context with the live form values under `resolver`.\n */\nexport function computeRoster(def: RosterDef, ctx: HelpTokenContext, rows: RosterRow[] = []): RosterResult {\n const arrived = arrivedKeys(def, ctx);\n const batch = 'batch' in def.expected;\n const entries: RosterEntry[] = expectedEntries(def, ctx, rows).map((e) => {\n if (arrived.has(e.key)) return { ...e, state: ROSTER_STATES.IN };\n const flag = batch ? batchFlagLabel(ctx, e.key) : flagFor(def, e.row);\n return flag ? { ...e, state: ROSTER_STATES.FLAGGED, flag } : { ...e, state: ROSTER_STATES.WAITING };\n });\n const count = (s: RosterState) => entries.filter((e) => e.state === s).length;\n const waiting = count(ROSTER_STATES.WAITING);\n return {\n entries,\n counts: {\n expected: entries.length,\n in: count(ROSTER_STATES.IN),\n flagged: count(ROSTER_STATES.FLAGGED),\n waiting,\n outstanding: waiting,\n },\n };\n}\n\n/** The header line: \"3 of 5 in · 1 Missing\". */\nexport function rosterHeadline(result: RosterResult): string {\n const { counts } = result;\n const parts = [`${counts.in} of ${counts.expected} in`];\n const byFlag = new Map<string, number>();\n for (const e of result.entries) {\n if (e.state === ROSTER_STATES.FLAGGED && e.flag) byFlag.set(e.flag, (byFlag.get(e.flag) ?? 0) + 1);\n }\n for (const [flag, n] of byFlag) parts.push(`${n} ${flag}`);\n return parts.join(' · ');\n}\n","import { CheckCircle2, Circle, Flag } from 'lucide-react';\nimport { OpenDetailButton } from './OpenDetailButton';\nimport { InlineConfirm } from './InlineConfirm';\nimport { InlineCodeEntry } from './InlineCodeEntry';\nimport { FormattedValue } from '../../common/display/FormattedValue';\nimport { interpolateHelp } from '../../../lib/x-lt-help';\nimport { interpolateItemTemplate, type EscalationItem } from '../../../lib/escalation-items';\nimport type { EmbedColumnDef } from '../../../lib/embed-row';\nimport type { RosterActionDef } from '../../../lib/x-lt-actions';\nimport { ROSTER_STATES, type RosterEntry, type RosterState } from '../../../lib/x-lt-roster';\nimport type { ShowIfContext } from '../../../lib/x-lt-show-if';\n\n/** An action the entry offers, with its index among the field's actions. */\nexport interface OfferedAction {\n index: number;\n action: RosterActionDef;\n}\n\nconst STATE_TEXT: Record<RosterState, string> = { waiting: 'Waiting', in: 'In', flagged: 'Flagged' };\n\nfunction StateMark({ state }: { state: RosterState }) {\n if (state === ROSTER_STATES.IN) return <CheckCircle2 className=\"w-4 h-4 text-status-success shrink-0\" strokeWidth={2} aria-hidden />;\n if (state === ROSTER_STATES.FLAGGED) return <Flag className=\"w-4 h-4 text-status-warning shrink-0\" strokeWidth={2} aria-hidden />;\n return <Circle className=\"w-4 h-4 text-text-tertiary shrink-0\" strokeWidth={2} aria-hidden />;\n}\n\n/**\n * One roster entry: its state mark, columns, state label, and the actions\n * its state offers. Columns read the member row's domains (a query set) or\n * the host row's, beside `{{item.*}}`: the entry key, a batch label, and\n * the item's payload, actor and time once it is in.\n */\nexport function RosterEntryRow({\n entry, item, hostCtx, columns, offered, asking, pending, error, onAction, onConfirm, onCancel,\n}: {\n entry: RosterEntry;\n item: EscalationItem & { label?: unknown };\n hostCtx: ShowIfContext;\n columns: EmbedColumnDef[];\n offered: OfferedAction[];\n /** The action asking in place: its confirmation, or its code entry. */\n asking: OfferedAction | null;\n pending: boolean;\n error?: string;\n onAction: (offer: OfferedAction) => void;\n onConfirm: (code?: string) => void;\n onCancel: () => void;\n}) {\n const ctx = entry.row ?? hostCtx;\n const done = entry.state === ROSTER_STATES.IN;\n const stateLabel = entry.state === ROSTER_STATES.FLAGGED ? entry.flag ?? STATE_TEXT.flagged : STATE_TEXT[entry.state];\n\n return (\n <li\n className=\"py-2 flex items-start gap-3 border-b border-surface-border/50 last:border-0\"\n data-testid={`roster-entry-${entry.key}`}\n data-state={entry.state}\n >\n <span className=\"pt-0.5\"><StateMark state={entry.state} /></span>\n <div className=\"min-w-0 flex-1 flex flex-wrap items-baseline gap-x-4 gap-y-0.5\">\n {columns.map((col) => (\n <span key={col.label} className={`text-sm min-w-0 ${done ? 'text-text-secondary' : 'text-text-primary'}`}>\n <span className=\"sr-only\">{col.label}: </span>\n <FormattedValue raw={interpolateItemTemplate(col.value, item, ctx)} format={col.format} />\n </span>\n ))}\n </div>\n <span\n className={`text-xs font-medium shrink-0 pt-0.5 ${done ? 'text-status-success' : entry.state === ROSTER_STATES.FLAGGED ? 'text-status-warning' : 'text-text-tertiary'}`}\n data-testid={`roster-state-${entry.key}`}\n >\n {stateLabel}\n </span>\n {(offered.length > 0 || asking || error) && (\n <span className=\"shrink-0 flex flex-col items-end gap-0.5\">\n {asking?.action.fill?.enterCode ? (\n <InlineCodeEntry\n label={asking.action.label}\n onConfirm={(code) => onConfirm(code)}\n onCancel={onCancel}\n testId={`roster-code-${entry.key}`}\n />\n ) : asking ? (\n <InlineConfirm\n message={interpolateHelp(asking.action.confirm ?? '', ctx)}\n onConfirm={() => onConfirm()}\n onCancel={onCancel}\n testId={`roster-confirm-${entry.key}`}\n />\n ) : (\n <span className=\"flex gap-1\">\n {offered.map((offer) => (\n <button\n key={offer.index}\n type=\"button\"\n onClick={() => onAction(offer)}\n disabled={pending}\n className=\"px-2 py-0.5 text-2xs font-medium text-accent border border-accent/40 rounded hover:bg-accent/10 disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n data-testid={`roster-action-${entry.key}`}\n >\n {offer.action.label}\n </button>\n ))}\n </span>\n )}\n {error && (\n <span className=\"text-2xs text-status-error max-w-[16rem] text-right\" data-testid={`roster-error-${entry.key}`}>{error}</span>\n )}\n </span>\n )}\n {entry.row && (\n <OpenDetailButton to={`/escalations/detail/${String(entry.row.escalation.id)}`} />\n )}\n </li>\n );\n}\n","import { useQueries } from '@tanstack/react-query';\nimport { escalationListQuery } from '../api/escalations';\nimport { schemaNeedsEnvelope } from '../lib/schema-needs-envelope';\nimport {\n computeRoster,\n readRosterDefs,\n resolveRosterQuery,\n rosterQueryOf,\n rosterRowContext,\n type RosterDef,\n type RosterResult,\n type RosterRow,\n} from '../lib/x-lt-roster';\nimport type { ShowIfContext } from '../lib/x-lt-show-if';\n\n/** A roster's result, or null while its member rows are loading or failed to load. */\nexport type RosterView = RosterResult | null;\n\nfunction listFiltersFor(def: RosterDef, ctx: ShowIfContext) {\n const query = rosterQueryOf(def);\n const scoped = query ? resolveRosterQuery(query, ctx) : null;\n if (!scoped) return null;\n return {\n roles: scoped.roles,\n status: scoped.status,\n facets: scoped.facets,\n limit: scoped.limit,\n include: schemaNeedsEnvelope({ key: def.key, flagged: def.flagged }) ? ('envelope' as const) : undefined,\n };\n}\n\n/**\n * Every roster of a form, by field name. Member rows load through the shared\n * list query, so a roster refreshes on the same escalation events as every\n * list, and the widget and the page read one cached result. `ctx` carries\n * the live form values under `resolver`.\n */\nexport function useRosters(\n schema: Record<string, unknown> | null | undefined,\n ctx: ShowIfContext | undefined,\n): Record<string, RosterView> {\n const defs = Object.entries(readRosterDefs(schema));\n const host = ctx ?? {};\n const filters = defs.map(([, def]) => listFiltersFor(def, host));\n const reads = useQueries({\n queries: filters.map((f) => ({\n ...escalationListQuery(f ?? {}),\n enabled: !!f,\n })),\n });\n\n const views: Record<string, RosterView> = {};\n defs.forEach(([field, def], i) => {\n if (!filters[i]) {\n views[field] = computeRoster(def, host);\n return;\n }\n const read = reads[i];\n if (!read.isSuccess) {\n views[field] = null;\n return;\n }\n const rows: RosterRow[] = read.data.escalations.map((e) => rosterRowContext(e as unknown as Record<string, unknown>));\n views[field] = computeRoster(def, host, rows);\n });\n return views;\n}\n\n/** The `roster` context domain: each loaded roster's counts. */\nexport function rosterContext(views: Record<string, RosterView>): Record<string, unknown> | null {\n const loaded = Object.entries(views).filter(([, v]) => v !== null);\n if (!loaded.length) return null;\n return Object.fromEntries(loaded.map(([field, v]) => [field, v!.counts]));\n}\n","/**\n * x-lt-actions on a roster field: the acts an entry offers, read the same way\n * by the dashboard (which shows them) and the API (which runs them). Each\n * action is exactly one act:\n *\n * { \"label\": \"Missing\", \"resolverPayload\": { \"outcome\": \"missing\" } } resolve the member row\n * { \"label\": \"Key in\", \"fill\": { \"payload\": { \"source\": \"key-in\" }, \"enterCode\": true } }\n * { \"label\": \"Missing\", \"flag\": { \"label\": \"Missing\" } } flag the batch key\n * { \"label\": \"Found it\", \"unflag\": true } clear the flag\n *\n * `when` names the entry states that offer it. `roles` limits who may run it:\n * the action shows to everyone and runs only for a person holding one of the\n * roles; `refuse` is what anyone else is told. The server re-reads the action\n * from the row's form schema and decides; the dashboard only displays.\n */\nimport { interpolateHelp, type HelpTokenContext } from './x-lt-help';\nimport { ROSTER_STATES, type RosterEntry, type RosterState } from './x-lt-roster';\nimport { displayGtin, isValidGtin, normalizeGtin } from '../scan-code/gtin';\n\nexport const X_LT_ACTIONS = 'x-lt-actions';\n\nexport const ROSTER_ACTION_KINDS = {\n RESOLVE: 'resolve',\n FILL: 'fill',\n FLAG: 'flag',\n UNFLAG: 'unflag',\n} as const;\nexport type RosterActionKind = (typeof ROSTER_ACTION_KINDS)[keyof typeof ROSTER_ACTION_KINDS];\n\n/** The separator of counted batch keys when the roster names none. */\nexport const ROSTER_KEY_SEPARATOR_DEFAULT = '#';\n\n/** The payload key a key-in records the entered code under. */\nexport const ENTERED_CODE_KEY = 'enteredCode';\n\nexport interface RosterActionDef {\n label: string;\n /** The entry states that offer it; the default follows the act. */\n when?: RosterState | RosterState[];\n /** A confirmation asked in place before firing; tokens interpolate per entry. */\n confirm?: string;\n /** Resolve the entry's member row with this canned payload (a query set). */\n resolverPayload?: Record<string, unknown>;\n /** Fill the entry's batch key; `enterCode` asks for the code and checks it against the key. */\n fill?: { payload?: Record<string, unknown>; enterCode?: boolean };\n /** Flag the entry's batch key. */\n flag?: { label: string; payload?: Record<string, unknown> };\n /** Clear the entry's flag. */\n unflag?: true;\n /** Runs only for a person holding one of these roles. */\n roles?: string[];\n /** What a person without the roles is told. */\n refuse?: string;\n}\n\nconst DEFAULT_WHEN: Record<RosterActionKind, RosterState[]> = {\n resolve: [ROSTER_STATES.WAITING],\n fill: [ROSTER_STATES.WAITING, ROSTER_STATES.FLAGGED],\n flag: [ROSTER_STATES.WAITING],\n unflag: [ROSTER_STATES.FLAGGED],\n};\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** The one act an action declares, or null when it declares none or several. */\nexport function rosterActionKind(action: RosterActionDef): RosterActionKind | null {\n const kinds: RosterActionKind[] = [];\n if (isRecord(action.resolverPayload)) kinds.push(ROSTER_ACTION_KINDS.RESOLVE);\n if (isRecord(action.fill)) kinds.push(ROSTER_ACTION_KINDS.FILL);\n if (isRecord(action.flag) && typeof action.flag.label === 'string' && action.flag.label) {\n kinds.push(ROSTER_ACTION_KINDS.FLAG);\n }\n if (action.unflag === true) kinds.push(ROSTER_ACTION_KINDS.UNFLAG);\n return kinds.length === 1 ? kinds[0] : null;\n}\n\n/** The field's actions with a label and exactly one act, in declared order. */\nexport function readRosterActions(fieldSchema: Record<string, unknown> | null | undefined): RosterActionDef[] {\n const raw = fieldSchema?.[X_LT_ACTIONS];\n if (!Array.isArray(raw)) return [];\n return raw.filter((a): a is RosterActionDef =>\n isRecord(a) && typeof a.label === 'string' && !!a.label && rosterActionKind(a as unknown as RosterActionDef) !== null);\n}\n\n/**\n * True when the entry offers the action: the entry is in one of its states,\n * and the act fits the entry (a resolve needs a member row; batch acts need\n * a batch set).\n */\nexport function offersAction(action: RosterActionDef, entry: RosterEntry, batch: boolean): boolean {\n const kind = rosterActionKind(action);\n if (!kind) return false;\n if (kind === ROSTER_ACTION_KINDS.RESOLVE ? !entry.row : !batch) return false;\n const when = action.when === undefined ? DEFAULT_WHEN[kind]\n : Array.isArray(action.when) ? action.when : [action.when];\n return when.includes(entry.state);\n}\n\n/** True when a person holding `roles` may run the action. */\nexport function holdsActionRole(action: Pick<RosterActionDef, 'roles'>, roles: string[], superadmin = false): boolean {\n const required = Array.isArray(action.roles) ? action.roles.filter((r) => typeof r === 'string' && r) : [];\n return required.length === 0 || superadmin || required.some((r) => roles.includes(r));\n}\n\n/** What a person without the action's roles is told. */\nexport function actionRefusal(action: Pick<RosterActionDef, 'label' | 'roles' | 'refuse'>): string {\n if (typeof action.refuse === 'string' && action.refuse.trim()) return action.refuse;\n const roles = (action.roles ?? []).filter(Boolean);\n return `${action.label} needs ${roles.length > 1 ? 'one of these roles' : 'the role'}: ${roles.join(', ')}.`;\n}\n\n/** Deep-walk a payload template: string leaves interpolate against the context; other types pass through typed. */\nexport function interpolatePayload(value: unknown, ctx: HelpTokenContext): unknown {\n if (typeof value === 'string') return interpolateHelp(value, ctx);\n if (Array.isArray(value)) return value.map((v) => interpolatePayload(v, ctx));\n if (isRecord(value)) {\n return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, interpolatePayload(v, ctx)]));\n }\n return value;\n}\n\n/** The code a batch key names, without its ordinal suffix. */\nexport function batchKeyCode(key: string, separator = ROSTER_KEY_SEPARATOR_DEFAULT): string {\n const at = key.lastIndexOf(separator);\n return at > 0 && /^[0-9]+$/.test(key.slice(at + 1)) ? key.slice(0, at) : key;\n}\n\n/** The batch key's code as its label prints it: a GTIN in its EAN-13 or EAN-8 form. */\nexport function printedKeyCode(key: string, separator?: string): string {\n const code = batchKeyCode(key, separator);\n return isValidGtin(code) ? displayGtin(normalizeGtin(code)) : code;\n}\n\n/**\n * True when an entered code names the batch key's code. GTINs compare in\n * their 14-digit form, so a UPC-A typed from the label matches its GTIN-14\n * key; anything else compares as trimmed text.\n */\nexport function enteredCodeMatches(entered: string, key: string, separator?: string): boolean {\n const typed = entered.trim();\n const code = batchKeyCode(key, separator);\n if (!typed) return false;\n if (isValidGtin(typed) && isValidGtin(code)) return normalizeGtin(typed) === normalizeGtin(code);\n return typed === code;\n}\n","import { useState } from 'react';\nimport { FieldLabel, FieldHelper } from '../resolver-form/FieldChrome';\nimport { RosterEntryRow, type OfferedAction } from './RosterEntryRow';\nimport { useEmbedWrite } from './embed-write';\nimport { useRosters } from '../../../hooks/useRosters';\nimport { ApiError } from '../../../api/client';\nimport { ROSTER_ACTION_OUTCOMES, useRosterAction } from '../../../api/escalations';\nimport { getDeep } from '../../../lib/x-lt-bind';\nimport type { EmbedColumnDef } from '../../../lib/embed-row';\nimport { offersAction, readRosterActions } from '../../../lib/x-lt-actions';\nimport { readRosterDef, rosterHeadline, type RosterEntry } from '../../../lib/x-lt-roster';\nimport type { EscalationItem } from '../../../lib/escalation-items';\nimport type { ShowIfContext } from '../../../lib/x-lt-show-if';\nimport type { WidgetProps } from './index';\n\nconst DEFAULT_COLUMNS: EmbedColumnDef[] = [{ label: 'Item', value: '{{item.itemKey}}' }];\nconst EMBED_WRITE_VERB = 'continue';\nconst BADGE_OUTCOMES: string[] = [ROSTER_ACTION_OUTCOMES.NEEDS_BADGE, ROSTER_ACTION_OUTCOMES.ROLE_REFUSED];\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** The value a per-key envelope map holds for the entry, when it is a string. */\nfunction keyed(ctx: ShowIfContext, map: string, key: string): string | undefined {\n const values = getDeep(ctx.envelope, map);\n const value = isRecord(values) ? values[key] : undefined;\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * The entry's `item` domain: its key, a batch label, and the item once it is\n * in. A held item reads its payload, time and actor from `accumulate_items`;\n * a batch entry reads its payload from `batch_items` and its time and actor\n * from `batch_filled_at` and `batch_filled_by`.\n */\nfunction itemFor(entry: RosterEntry, ctx: ShowIfContext): EscalationItem & { label?: unknown } {\n const held = getDeep(ctx.envelope, 'accumulate_items');\n const batchPayload = getDeep(ctx.envelope, 'batch_items');\n const h = isRecord(held) && isRecord(held[entry.key]) ? held[entry.key] as Record<string, unknown> : null;\n const payload = h && isRecord(h.payload) ? h.payload\n : isRecord(batchPayload) && isRecord(batchPayload[entry.key]) ? batchPayload[entry.key] as Record<string, unknown>\n : undefined;\n const at = typeof h?.at === 'string' ? h.at : h ? undefined : keyed(ctx, 'batch_filled_at', entry.key);\n const actor = typeof h?.actor === 'string' ? h.actor : h ? undefined : keyed(ctx, 'batch_filled_by', entry.key);\n return {\n itemKey: entry.key,\n at: at ?? '',\n ...(payload ? { payload } : {}),\n ...(actor ? { actor } : {}),\n ...(entry.label !== undefined ? { label: entry.label } : {}),\n };\n}\n\n/**\n * Roster widget: every member the host row expects, each marked waiting, in,\n * or flagged, with the count in the heading. The entries come from the same\n * shared computation the page reads for `roster.*` and the roster guard,\n * against the live form, so a scan into a form field ticks its entry in at\n * once and an arrival on any device ticks it in on the next escalation\n * event.\n *\n * Actions run on the server (`/escalations/:id/roster-action`), which\n * re-reads the declaration: a resolve of the entry's member row, or a fill,\n * flag or unflag of its batch key. An action limited to roles asks for a\n * badge of that role when the person acting lacks it.\n *\n * Schema usage:\n * \"x-lt-widget\": \"roster\",\n * \"x-lt-roster\": { \"expected\": { ... }, \"key\": \"...\", \"in\": [...], \"flagged\": [...] },\n * \"x-lt-columns\": [{ \"label\": \"Order\", \"value\": \"{{metadata.orderId}}\" }],\n * \"x-lt-actions\": [{ \"label\": \"Missing\", \"when\": \"waiting\",\n * \"resolverPayload\": { \"outcome\": \"missing\" },\n * \"confirm\": \"Mark {{metadata.orderId}} missing?\" }]\n *\n * Display-only: produces no value of its own in the resolver payload.\n */\nexport function RosterWidget({ fieldKey, schema, escalationContext }: WidgetProps) {\n const ctx = escalationContext ?? {};\n const view = useRosters({ properties: { [fieldKey]: schema ?? {} } }, ctx)[fieldKey];\n const def = readRosterDef(schema);\n const batch = !!def && 'batch' in def.expected;\n const columns = (schema?.['x-lt-columns'] as EmbedColumnDef[] | undefined) ?? DEFAULT_COLUMNS;\n const actions = readRosterActions(schema);\n const label = (schema?.title as string | undefined) ?? 'Expected items';\n const helperText = schema?.description as string | undefined;\n const hostId = String((ctx.escalation as Record<string, unknown> | undefined)?.id ?? '');\n\n const runAction = useRosterAction();\n const guard = useEmbedWrite();\n const [asking, setAsking] = useState<{ key: string; offer: OfferedAction } | null>(null);\n const [pendingKey, setPendingKey] = useState<string | null>(null);\n const [errors, setErrors] = useState<Record<string, string>>({});\n\n const fire = async (entry: RosterEntry, offer: OfferedAction, code?: string): Promise<void> => {\n setPendingKey(entry.key);\n setErrors((prev) => ({ ...prev, [entry.key]: '' }));\n try {\n await runAction.mutateAsync({ id: hostId, field: fieldKey, action: offer.index, entry: entry.key, code });\n } catch (err) {\n const message = (err as Error).message;\n setErrors((prev) => ({ ...prev, [entry.key]: message }));\n const outcome = err instanceof ApiError && isRecord(err.body) ? err.body.outcome : undefined;\n if (typeof outcome === 'string' && BADGE_OUTCOMES.includes(outcome)) {\n guard(EMBED_WRITE_VERB, () => fire(entry, offer, code), { badge: message });\n }\n } finally {\n setPendingKey(null);\n }\n };\n\n const act = (entry: RosterEntry, offer: OfferedAction, code?: string) => {\n setAsking(null);\n guard(EMBED_WRITE_VERB, () => fire(entry, offer, code));\n };\n\n const request = (entry: RosterEntry, offer: OfferedAction) => {\n if (offer.action.confirm || offer.action.fill?.enterCode) setAsking({ key: entry.key, offer });\n else act(entry, offer);\n };\n\n const offeredFor = (entry: RosterEntry): OfferedAction[] =>\n actions\n .map((action, index) => ({ action, index }))\n .filter(({ action }) => offersAction(action, entry, batch));\n\n return (\n <div data-field-key={fieldKey} data-testid={`roster-widget-${fieldKey}`}>\n <div className=\"flex items-baseline justify-between gap-3\">\n <FieldLabel>{label}</FieldLabel>\n {view && (\n <span className=\"text-sm font-medium text-text-primary\" data-testid={`roster-headline-${fieldKey}`}>\n {rosterHeadline(view)}\n </span>\n )}\n </div>\n {helperText && <FieldHelper>{helperText}</FieldHelper>}\n {!view ? (\n <p className=\"text-xs text-text-tertiary py-2\" data-testid={`roster-loading-${fieldKey}`}>Loading expected items…</p>\n ) : view.entries.length === 0 ? (\n <p className=\"text-xs text-text-tertiary italic py-2\" data-testid={`roster-empty-${fieldKey}`}>Nothing expected.</p>\n ) : (\n <ul className=\"mt-1\" data-testid={`roster-list-${fieldKey}`}>\n {view.entries.map((entry) => (\n <RosterEntryRow\n key={entry.key}\n entry={entry}\n item={itemFor(entry, ctx)}\n hostCtx={ctx}\n columns={columns}\n offered={hostId ? offeredFor(entry) : []}\n asking={asking?.key === entry.key ? asking.offer : null}\n pending={pendingKey === entry.key}\n error={errors[entry.key] || undefined}\n onAction={(offer) => request(entry, offer)}\n onConfirm={(code) => asking && act(entry, asking.offer, code)}\n onCancel={() => setAsking(null)}\n />\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"names":["INTERPOLATION_PATTERN","hasInterpolation","token","resolveCtxPath","path","ctx","dot","root","getDeep","interpolatePath","template","unresolved","concrete","_match","rawPath","value","isTruthy","asComparable","resolvePath","expr","domain","HELP_DOMAINS","evaluateShowIf","condition","c","negate","neqAt","eqAt","isNeq","opAt","expected","known","matches","result","isHiddenIfEmpty","fieldSchema","X_LT_HIDDEN","isHiddenField","FieldLabel","children","isRequired","htmlFor","jsxs","jsx","FieldHelper","id","FieldError","error","inputClass","hasError","selectClass","OpenDetailButton","to","className","Link","ArrowUpRight","InlineConfirm","message","onConfirm","onCancel","testId","runNow","_verb","run","options","EmbedWriteContext","createContext","useEmbedWrite","useContext","InlineCodeEntry","label","code","setCode","useState","submit","e","X_LT_ROSTER","ROSTER_QUERY_LIMIT","ROSTER_ITEMS_SOURCE","ROSTER_ANY_STATUS","ROSTER_STATES","DEFAULT_KEY","MISSING","BATCH_KEYS","BATCH_PENDING","BATCH_FLAGS","ACCUMULATE_KEYS","isRecord","v","strings","s","readRosterDef","raw","readRosterDefs","schema","properties","defs","field","def","roster","rosterQueryOf","resolveRosterQuery","query","facets","k","resolved","interpolateHelp","roles","status","parseJson","parsed","rosterRowContext","row","heldKeys","metadata","accumulated","envelopeItems","pending","countedKeys","values","separator","seen","n","arrivedKeys","arrived","source","batchFlagLabel","key","flags","flag","flagFor","rule","conditions","createdAt","ms","expectedEntries","rows","out","push","extra","newest","held","labels","_a","computeRoster","batch","entries","count","waiting","rosterHeadline","counts","parts","byFlag","STATE_TEXT","StateMark","state","CheckCircle2","Flag","Circle","RosterEntryRow","entry","item","hostCtx","columns","offered","asking","onAction","done","stateLabel","col","FormattedValue","interpolateItemTemplate","offer","listFiltersFor","scoped","schemaNeedsEnvelope","useRosters","host","filters","reads","useQueries","f","escalationListQuery","views","i","read","rosterContext","loaded","X_LT_ACTIONS","ROSTER_ACTION_KINDS","DEFAULT_WHEN","rosterActionKind","action","kinds","readRosterActions","a","offersAction","kind","DEFAULT_COLUMNS","EMBED_WRITE_VERB","BADGE_OUTCOMES","ROSTER_ACTION_OUTCOMES","keyed","map","itemFor","batchPayload","h","payload","at","actor","RosterWidget","fieldKey","escalationContext","view","actions","helperText","hostId","runAction","useRosterAction","guard","setAsking","pendingKey","setPendingKey","errors","setErrors","fire","prev","err","outcome","ApiError","act","request","offeredFor","index"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{j as s}from"./vendor-query-DvWWuNIB.js";import{B as c}from"./BotPicker-Ci5PUrkl.js";import{$ as m}from"./index-BLbZsMRG.js";import{g as o,az as x}from"./vendor-icons-C_DQEeBy.js";function j({selected:e,onChange:n}){const{user:t,isSuperAdmin:a,hasRoleType:i}=m(),r=a||i("admin");return s.jsx("div",{children:r?s.jsx(c,{selected:e,onChange:n}):s.jsx("div",{className:"flex items-center gap-1.5",children:e?s.jsxs(s.Fragment,{children:[s.jsx(o,{className:"w-3 h-3 text-accent/75 shrink-0",strokeWidth:1.5}),s.jsxs("span",{className:"text-2xs text-text-secondary",children:["Running as ",s.jsx("span",{className:"font-medium text-accent font-mono",children:e})]})]}):s.jsxs(s.Fragment,{children:[s.jsx(x,{className:"w-3 h-3 text-accent/75 shrink-0",strokeWidth:1.5}),s.jsxs("span",{className:"text-2xs text-text-secondary",children:["Running as ",s.jsx("span",{className:"font-medium text-accent",children:(t==null?void 0:t.displayName)||(t==null?void 0:t.userId)||"you"})]})]})})})}export{j as R};
2
- //# sourceMappingURL=RunAsSelector-Fptil_sB.js.map
1
+ import{j as s}from"./vendor-query-DvWWuNIB.js";import{B as c}from"./BotPicker-8bf0yOCT.js";import{$ as m}from"./index-D7peGNN0.js";import{g as o,az as x}from"./vendor-icons-DUYcKTjP.js";function j({selected:e,onChange:n}){const{user:t,isSuperAdmin:a,hasRoleType:i}=m(),r=a||i("admin");return s.jsx("div",{children:r?s.jsx(c,{selected:e,onChange:n}):s.jsx("div",{className:"flex items-center gap-1.5",children:e?s.jsxs(s.Fragment,{children:[s.jsx(o,{className:"w-3 h-3 text-accent/75 shrink-0",strokeWidth:1.5}),s.jsxs("span",{className:"text-2xs text-text-secondary",children:["Running as ",s.jsx("span",{className:"font-medium text-accent font-mono",children:e})]})]}):s.jsxs(s.Fragment,{children:[s.jsx(x,{className:"w-3 h-3 text-accent/75 shrink-0",strokeWidth:1.5}),s.jsxs("span",{className:"text-2xs text-text-secondary",children:["Running as ",s.jsx("span",{className:"font-medium text-accent",children:(t==null?void 0:t.displayName)||(t==null?void 0:t.userId)||"you"})]})]})})})}export{j as R};
2
+ //# sourceMappingURL=RunAsSelector-CyUWGp5P.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunAsSelector-Fptil_sB.js","sources":["../../src/components/common/form/RunAsSelector.tsx"],"sourcesContent":["import { Bot, UserCircle } from 'lucide-react';\nimport { BotPicker } from './BotPicker';\nimport { useAuth } from '../../../hooks/useAuth';\n\ninterface RunAsSelectorProps {\n selected: string;\n onChange: (botExternalId: string) => void;\n}\n\nexport function RunAsSelector({ selected, onChange }: RunAsSelectorProps) {\n const { user, isSuperAdmin, hasRoleType } = useAuth();\n const isAdmin = isSuperAdmin || hasRoleType('admin');\n\n return (\n <div>\n {isAdmin ? (\n <BotPicker selected={selected} onChange={onChange} />\n ) : (\n <div className=\"flex items-center gap-1.5\">\n {selected ? (\n <>\n <Bot className=\"w-3 h-3 text-accent/75 shrink-0\" strokeWidth={1.5} />\n <span className=\"text-2xs text-text-secondary\">\n Running as <span className=\"font-medium text-accent font-mono\">{selected}</span>\n </span>\n </>\n ) : (\n <>\n <UserCircle className=\"w-3 h-3 text-accent/75 shrink-0\" strokeWidth={1.5} />\n <span className=\"text-2xs text-text-secondary\">\n Running as <span className=\"font-medium text-accent\">{user?.displayName || user?.userId || 'you'}</span>\n </span>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["RunAsSelector","selected","onChange","user","isSuperAdmin","hasRoleType","useAuth","isAdmin","jsx","BotPicker","jsxs","Fragment","Bot","UserCircle"],"mappings":"0LASO,SAASA,EAAc,CAAE,SAAAC,EAAU,SAAAC,GAAgC,CACxE,KAAM,CAAE,KAAAC,EAAM,aAAAC,EAAc,YAAAC,CAAA,EAAgBC,EAAA,EACtCC,EAAUH,GAAgBC,EAAY,OAAO,EAEnD,OACEG,EAAAA,IAAC,MAAA,CACE,SAAAD,EACCC,EAAAA,IAACC,EAAA,CAAU,SAAAR,EAAoB,SAAAC,CAAA,CAAoB,EAEnDM,EAAAA,IAAC,MAAA,CAAI,UAAU,4BACZ,WACCE,EAAAA,KAAAC,WAAA,CACE,SAAA,CAAAH,EAAAA,IAACI,EAAA,CAAI,UAAU,kCAAkC,YAAa,IAAK,EACnEF,EAAAA,KAAC,OAAA,CAAK,UAAU,+BAA+B,SAAA,CAAA,cAClCF,EAAAA,IAAC,OAAA,CAAK,UAAU,oCAAqC,SAAAP,CAAA,CAAS,CAAA,CAAA,CAC3E,CAAA,CAAA,CACF,EAEAS,EAAAA,KAAAC,EAAAA,SAAA,CACE,SAAA,CAAAH,EAAAA,IAACK,EAAA,CAAW,UAAU,kCAAkC,YAAa,IAAK,EAC1EH,EAAAA,KAAC,OAAA,CAAK,UAAU,+BAA+B,SAAA,CAAA,cAClCF,EAAAA,IAAC,QAAK,UAAU,0BAA2B,2BAAM,eAAeL,GAAA,YAAAA,EAAM,SAAU,KAAA,CAAM,CAAA,CAAA,CACnG,CAAA,CAAA,CACF,EAEJ,EAEJ,CAEJ"}
1
+ {"version":3,"file":"RunAsSelector-CyUWGp5P.js","sources":["../../src/components/common/form/RunAsSelector.tsx"],"sourcesContent":["import { Bot, UserCircle } from 'lucide-react';\nimport { BotPicker } from './BotPicker';\nimport { useAuth } from '../../../hooks/useAuth';\n\ninterface RunAsSelectorProps {\n selected: string;\n onChange: (botExternalId: string) => void;\n}\n\nexport function RunAsSelector({ selected, onChange }: RunAsSelectorProps) {\n const { user, isSuperAdmin, hasRoleType } = useAuth();\n const isAdmin = isSuperAdmin || hasRoleType('admin');\n\n return (\n <div>\n {isAdmin ? (\n <BotPicker selected={selected} onChange={onChange} />\n ) : (\n <div className=\"flex items-center gap-1.5\">\n {selected ? (\n <>\n <Bot className=\"w-3 h-3 text-accent/75 shrink-0\" strokeWidth={1.5} />\n <span className=\"text-2xs text-text-secondary\">\n Running as <span className=\"font-medium text-accent font-mono\">{selected}</span>\n </span>\n </>\n ) : (\n <>\n <UserCircle className=\"w-3 h-3 text-accent/75 shrink-0\" strokeWidth={1.5} />\n <span className=\"text-2xs text-text-secondary\">\n Running as <span className=\"font-medium text-accent\">{user?.displayName || user?.userId || 'you'}</span>\n </span>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["RunAsSelector","selected","onChange","user","isSuperAdmin","hasRoleType","useAuth","isAdmin","jsx","BotPicker","jsxs","Fragment","Bot","UserCircle"],"mappings":"0LASO,SAASA,EAAc,CAAE,SAAAC,EAAU,SAAAC,GAAgC,CACxE,KAAM,CAAE,KAAAC,EAAM,aAAAC,EAAc,YAAAC,CAAA,EAAgBC,EAAA,EACtCC,EAAUH,GAAgBC,EAAY,OAAO,EAEnD,OACEG,EAAAA,IAAC,MAAA,CACE,SAAAD,EACCC,EAAAA,IAACC,EAAA,CAAU,SAAAR,EAAoB,SAAAC,CAAA,CAAoB,EAEnDM,EAAAA,IAAC,MAAA,CAAI,UAAU,4BACZ,WACCE,EAAAA,KAAAC,WAAA,CACE,SAAA,CAAAH,EAAAA,IAACI,EAAA,CAAI,UAAU,kCAAkC,YAAa,IAAK,EACnEF,EAAAA,KAAC,OAAA,CAAK,UAAU,+BAA+B,SAAA,CAAA,cAClCF,EAAAA,IAAC,OAAA,CAAK,UAAU,oCAAqC,SAAAP,CAAA,CAAS,CAAA,CAAA,CAC3E,CAAA,CAAA,CACF,EAEAS,EAAAA,KAAAC,EAAAA,SAAA,CACE,SAAA,CAAAH,EAAAA,IAACK,EAAA,CAAW,UAAU,kCAAkC,YAAa,IAAK,EAC1EH,EAAAA,KAAC,OAAA,CAAK,UAAU,+BAA+B,SAAA,CAAA,cAClCF,EAAAA,IAAC,QAAK,UAAU,0BAA2B,2BAAM,eAAeL,GAAA,YAAAA,EAAM,SAAU,KAAA,CAAM,CAAA,CAAA,CACnG,CAAA,CAAA,CACF,EAEJ,EAEJ,CAEJ"}
@@ -1,2 +1,2 @@
1
- import{a as c,j as e}from"./vendor-query-DvWWuNIB.js";import{al as A,br as m,bs as _,Z as E}from"./index-BLbZsMRG.js";import{G as I}from"./GrantScopeField-BzWGzBfA.js";import{P as D}from"./PageHeader-C4z1ExF7.js";import{a as P,h as F}from"./vendor-icons-C_DQEeBy.js";import{u as G}from"./vendor-react-_9U4mKFr.js";function L(r){for(let n=10;n<=99;n++)if(!r.has(n))return n;return null}function Y(){const r=G(),{data:n,isLoading:i}=A(),[x,l]=c.useState(!1),u=(n==null?void 0:n.schemes)??[],d=new Set(u.map(t=>t.version)),h=L(d);return e.jsxs("div",{children:[e.jsx(D,{title:"Scan Codes"}),e.jsx("p",{className:"text-sm text-text-secondary max-w-form mb-8",children:"A scan code addresses one of your escalations by a value on its label. Add a scheme, name it, and point it at the metadata facet the scanned value matches — then define what each scan does on its detail page."}),i?e.jsx("div",{className:"text-sm text-text-tertiary",children:"Loading…"}):e.jsxs("div",{className:"divide-y divide-surface-border border-y border-surface-border max-w-form",children:[u.map(t=>e.jsxs("div",{role:"button",tabIndex:0,onClick:()=>r(`/admin/scan-codes/${t.version}`),onKeyDown:p=>{(p.key==="Enter"||p.key===" ")&&r(`/admin/scan-codes/${t.version}`)},className:"flex items-center gap-4 py-3 px-2 cursor-pointer hover:bg-surface-sunken transition-colors",children:[e.jsx("span",{className:"w-9 h-8 flex items-center justify-center text-sm font-mono text-accent border border-surface-border rounded shrink-0",children:t.version}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-sm text-text-primary truncate",children:t.name}),e.jsxs("div",{className:"text-2xs text-text-tertiary truncate",children:[t.kind===m.IDENTITY&&"identity badge · ","target facet ",e.jsx("span",{className:"font-mono",children:t.target_facet})," · ",t.encoding==="fixed"?`fixed, ${t.target_length} digit target`:t.encoding==="gtin"?"manufacturer barcodes (UPC / EAN)":`delimited by "${t.delimiter}"`]})]}),e.jsx("span",{className:`text-2xs uppercase tracking-widest ${t.enabled?"text-status-success":"text-text-quaternary"}`,children:t.enabled?"Enabled":"Disabled"})]},t.version)),e.jsxs("div",{className:"flex items-center gap-4 py-3 px-2",children:[e.jsx("span",{className:"w-9 h-8 flex items-center justify-center text-text-quaternary shrink-0",children:e.jsx(P,{className:"w-4 h-4",strokeWidth:1.5})}),h==null?e.jsx("span",{className:"text-2xs text-text-quaternary",children:"All 90 scheme slots are in use."}):x?e.jsx(U,{version:h,onDone:t=>{l(!1),t!=null&&r(`/admin/scan-codes/${t}`)}}):e.jsxs("button",{type:"button",onClick:()=>l(!0),className:"flex items-center gap-1.5 text-sm text-accent hover:text-accent-hover",children:[e.jsx(F,{className:"w-3.5 h-3.5"})," Add a scheme"]})]})]})]})}function U({version:r,onDone:n}){const i=_(),{data:x}=E(),[l,u]=c.useState(""),[d,h]=c.useState(m.ACTION),[t,p]=c.useState(""),[o,y]=c.useState(""),[N,v]=c.useState(8),[b,k]=c.useState(3600),[j,w]=c.useState(0),[f,S]=c.useState("action"),C=Array.from(new Set(((x==null?void 0:x.roles)??[]).map(s=>s.priority_facet).filter(Boolean))),a=d===m.IDENTITY,g=b>=1&&b<=86400,T=async()=>{!l||!t||!o||a&&!g||(await i.mutateAsync({version:r,name:l,kind:d,target_facet:t,encoding:o,target_length:o==="fixed"?N:null,grant_ttl_seconds:a?b:null,grant_max_uses:a?j:0,grant_scope:a?f:"action"}),n(r))};return e.jsxs("div",{className:"flex-1 space-y-3",children:[e.jsxs("p",{className:"text-2xs text-text-tertiary",children:["Codes for this scheme start with ",e.jsx("span",{className:"font-mono text-text-secondary",children:r})," — assigned for you."]}),e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Kind ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?"A badge — scanning it primes who acts next.":"An item code — scanning it acts on the item."}),e.jsxs("select",{value:d,onChange:s=>h(s.target.value),className:"select",children:[e.jsx("option",{value:m.ACTION,children:"Action"}),e.jsx("option",{value:m.IDENTITY,children:"Identity"})]})]}),e.jsxs("label",{className:"block flex-1 min-w-[16rem]",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Name ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?'Say who the badge identifies — "Associate badge".':'Say what the target identifies — "Printer serial".'}),e.jsx("input",{value:l,onChange:s=>u(s.target.value),className:"input w-full",placeholder:a?"Associate badge":"Printer serial",autoFocus:!0})]}),e.jsxs("label",{className:"block flex-1 min-w-[16rem]",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Target facet ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?"The user-metadata key the badge matches (e.g. badge_id).":"The escalation metadata key the scanned value matches."}),e.jsx("input",{value:t,onChange:s=>p(s.target.value),className:"input w-full font-mono",placeholder:a?"badge_id":"serialNumber",list:"scan-facet-suggestions"}),e.jsx("datalist",{id:"scan-facet-suggestions",children:C.map(s=>e.jsx("option",{value:s},s))})]})]}),a&&e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Grant TTL (seconds) ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"How long a badge scan stays primed (1–86400)."}),e.jsx("input",{type:"number",min:1,max:86400,value:b,onChange:s=>k(Number(s.target.value)),className:"input w-[12rem]"}),!g&&e.jsx("span",{className:"block text-2xs text-status-error mt-1",children:"Enter 1–86400 seconds."})]}),e.jsxs("label",{className:"block",children:[e.jsx("span",{className:"block text-xs text-text-secondary mb-1",children:"Grant max uses"}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"0 = TTL-bound; n = the grant covers n acts."}),e.jsx("input",{type:"number",min:0,value:j,onChange:s=>w(Number(s.target.value)),className:"input w-[12rem]"})]}),e.jsx(I,{value:f,onChange:S})]}),e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Code style ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"Delimited for QR / Code 128, fixed digits for numeric labels, manufacturer barcodes for the codes printed on products."}),e.jsxs("select",{value:o,onChange:s=>y(s.target.value),className:"select",children:[e.jsx("option",{value:"",disabled:!0,children:"Choose…"}),e.jsx("option",{value:"delimited",children:"Delimited text (QR / Code 128)"}),e.jsx("option",{value:"fixed",children:"Fixed digits (UPC)"}),!a&&e.jsx("option",{value:"gtin",children:"Manufacturer barcode (UPC / EAN)"})]})]}),o==="fixed"&&e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Target digits ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"How many digits the target occupies."}),e.jsx("input",{type:"number",min:1,max:20,value:N,onChange:s=>v(Number(s.target.value)),className:"input w-[12rem]"})]})]}),i.error&&e.jsx("p",{className:"text-xs text-status-error",children:i.error.message}),e.jsxs("div",{className:"flex gap-3",children:[e.jsx("button",{type:"button",onClick:()=>n(null),className:"btn-secondary text-xs",children:"Discard"}),e.jsx("button",{type:"button",onClick:T,disabled:!l||!t||!o||a&&!g||i.isPending,className:"btn-primary text-xs",children:"Create scheme"})]})]})}export{Y as ScanCodesPage};
2
- //# sourceMappingURL=ScanCodesPage-BeZA_YJW.js.map
1
+ import{a as c,j as e}from"./vendor-query-DvWWuNIB.js";import{al as A,bt as m,bu as _,Z as E}from"./index-D7peGNN0.js";import{G as I}from"./GrantScopeField-BzWGzBfA.js";import{P as D}from"./PageHeader-BSWIspNB.js";import{a as P,h as F}from"./vendor-icons-DUYcKTjP.js";import{u as G}from"./vendor-react-_9U4mKFr.js";function L(r){for(let n=10;n<=99;n++)if(!r.has(n))return n;return null}function Y(){const r=G(),{data:n,isLoading:i}=A(),[x,l]=c.useState(!1),u=(n==null?void 0:n.schemes)??[],d=new Set(u.map(t=>t.version)),h=L(d);return e.jsxs("div",{children:[e.jsx(D,{title:"Scan Codes"}),e.jsx("p",{className:"text-sm text-text-secondary max-w-form mb-8",children:"A scan code addresses one of your escalations by a value on its label. Add a scheme, name it, and point it at the metadata facet the scanned value matches — then define what each scan does on its detail page."}),i?e.jsx("div",{className:"text-sm text-text-tertiary",children:"Loading…"}):e.jsxs("div",{className:"divide-y divide-surface-border border-y border-surface-border max-w-form",children:[u.map(t=>e.jsxs("div",{role:"button",tabIndex:0,onClick:()=>r(`/admin/scan-codes/${t.version}`),onKeyDown:p=>{(p.key==="Enter"||p.key===" ")&&r(`/admin/scan-codes/${t.version}`)},className:"flex items-center gap-4 py-3 px-2 cursor-pointer hover:bg-surface-sunken transition-colors",children:[e.jsx("span",{className:"w-9 h-8 flex items-center justify-center text-sm font-mono text-accent border border-surface-border rounded shrink-0",children:t.version}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-sm text-text-primary truncate",children:t.name}),e.jsxs("div",{className:"text-2xs text-text-tertiary truncate",children:[t.kind===m.IDENTITY&&"identity badge · ","target facet ",e.jsx("span",{className:"font-mono",children:t.target_facet})," · ",t.encoding==="fixed"?`fixed, ${t.target_length} digit target`:t.encoding==="gtin"?"manufacturer barcodes (UPC / EAN)":`delimited by "${t.delimiter}"`]})]}),e.jsx("span",{className:`text-2xs uppercase tracking-widest ${t.enabled?"text-status-success":"text-text-quaternary"}`,children:t.enabled?"Enabled":"Disabled"})]},t.version)),e.jsxs("div",{className:"flex items-center gap-4 py-3 px-2",children:[e.jsx("span",{className:"w-9 h-8 flex items-center justify-center text-text-quaternary shrink-0",children:e.jsx(P,{className:"w-4 h-4",strokeWidth:1.5})}),h==null?e.jsx("span",{className:"text-2xs text-text-quaternary",children:"All 90 scheme slots are in use."}):x?e.jsx(U,{version:h,onDone:t=>{l(!1),t!=null&&r(`/admin/scan-codes/${t}`)}}):e.jsxs("button",{type:"button",onClick:()=>l(!0),className:"flex items-center gap-1.5 text-sm text-accent hover:text-accent-hover",children:[e.jsx(F,{className:"w-3.5 h-3.5"})," Add a scheme"]})]})]})]})}function U({version:r,onDone:n}){const i=_(),{data:x}=E(),[l,u]=c.useState(""),[d,h]=c.useState(m.ACTION),[t,p]=c.useState(""),[o,y]=c.useState(""),[N,v]=c.useState(8),[b,k]=c.useState(3600),[j,w]=c.useState(0),[f,S]=c.useState("action"),C=Array.from(new Set(((x==null?void 0:x.roles)??[]).map(s=>s.priority_facet).filter(Boolean))),a=d===m.IDENTITY,g=b>=1&&b<=86400,T=async()=>{!l||!t||!o||a&&!g||(await i.mutateAsync({version:r,name:l,kind:d,target_facet:t,encoding:o,target_length:o==="fixed"?N:null,grant_ttl_seconds:a?b:null,grant_max_uses:a?j:0,grant_scope:a?f:"action"}),n(r))};return e.jsxs("div",{className:"flex-1 space-y-3",children:[e.jsxs("p",{className:"text-2xs text-text-tertiary",children:["Codes for this scheme start with ",e.jsx("span",{className:"font-mono text-text-secondary",children:r})," — assigned for you."]}),e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Kind ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?"A badge — scanning it primes who acts next.":"An item code — scanning it acts on the item."}),e.jsxs("select",{value:d,onChange:s=>h(s.target.value),className:"select",children:[e.jsx("option",{value:m.ACTION,children:"Action"}),e.jsx("option",{value:m.IDENTITY,children:"Identity"})]})]}),e.jsxs("label",{className:"block flex-1 min-w-[16rem]",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Name ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?'Say who the badge identifies — "Associate badge".':'Say what the target identifies — "Printer serial".'}),e.jsx("input",{value:l,onChange:s=>u(s.target.value),className:"input w-full",placeholder:a?"Associate badge":"Printer serial",autoFocus:!0})]}),e.jsxs("label",{className:"block flex-1 min-w-[16rem]",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Target facet ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:a?"The user-metadata key the badge matches (e.g. badge_id).":"The escalation metadata key the scanned value matches."}),e.jsx("input",{value:t,onChange:s=>p(s.target.value),className:"input w-full font-mono",placeholder:a?"badge_id":"serialNumber",list:"scan-facet-suggestions"}),e.jsx("datalist",{id:"scan-facet-suggestions",children:C.map(s=>e.jsx("option",{value:s},s))})]})]}),a&&e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Grant TTL (seconds) ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"How long a badge scan stays primed (1–86400)."}),e.jsx("input",{type:"number",min:1,max:86400,value:b,onChange:s=>k(Number(s.target.value)),className:"input w-[12rem]"}),!g&&e.jsx("span",{className:"block text-2xs text-status-error mt-1",children:"Enter 1–86400 seconds."})]}),e.jsxs("label",{className:"block",children:[e.jsx("span",{className:"block text-xs text-text-secondary mb-1",children:"Grant max uses"}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"0 = TTL-bound; n = the grant covers n acts."}),e.jsx("input",{type:"number",min:0,value:j,onChange:s=>w(Number(s.target.value)),className:"input w-[12rem]"})]}),e.jsx(I,{value:f,onChange:S})]}),e.jsxs("div",{className:"flex flex-wrap gap-4",children:[e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Code style ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"Delimited for QR / Code 128, fixed digits for numeric labels, manufacturer barcodes for the codes printed on products."}),e.jsxs("select",{value:o,onChange:s=>y(s.target.value),className:"select",children:[e.jsx("option",{value:"",disabled:!0,children:"Choose…"}),e.jsx("option",{value:"delimited",children:"Delimited text (QR / Code 128)"}),e.jsx("option",{value:"fixed",children:"Fixed digits (UPC)"}),!a&&e.jsx("option",{value:"gtin",children:"Manufacturer barcode (UPC / EAN)"})]})]}),o==="fixed"&&e.jsxs("label",{className:"block",children:[e.jsxs("span",{className:"block text-xs text-text-secondary mb-1",children:["Target digits ",e.jsx("span",{className:"text-status-error",children:"*"})]}),e.jsx("span",{className:"block text-2xs text-text-tertiary mb-1",children:"How many digits the target occupies."}),e.jsx("input",{type:"number",min:1,max:20,value:N,onChange:s=>v(Number(s.target.value)),className:"input w-[12rem]"})]})]}),i.error&&e.jsx("p",{className:"text-xs text-status-error",children:i.error.message}),e.jsxs("div",{className:"flex gap-3",children:[e.jsx("button",{type:"button",onClick:()=>n(null),className:"btn-secondary text-xs",children:"Discard"}),e.jsx("button",{type:"button",onClick:T,disabled:!l||!t||!o||a&&!g||i.isPending,className:"btn-primary text-xs",children:"Create scheme"})]})]})}export{Y as ScanCodesPage};
2
+ //# sourceMappingURL=ScanCodesPage-Cy05JOt4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScanCodesPage-BeZA_YJW.js","sources":["../../src/pages/admin/scan-codes/ScanCodesPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { ScanBarcode, Plus } from 'lucide-react';\nimport { useScanSchemes, useUpsertScanScheme, SCAN_SCHEME_KINDS, type ScanSchemeKind } from '../../../api/scan-codes';\nimport { useRoleDetails } from '../../../api/roles';\nimport { GrantScopeField } from './GrantScopeField';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\n\n/** The first free two-digit scheme index (10-99), or null when all are taken. */\nfunction nextSchemeVersion(used: Set<number>): number | null {\n for (let v = 10; v <= 99; v++) if (!used.has(v)) return v;\n return null;\n}\n\n/**\n * Scan schemes — the named things a scanned code addresses. Each scheme owns a\n * two-digit index (assigned automatically, starting at 10) and declares which\n * escalation metadata facet the scanned target resolves against and how the\n * string parses. Its rules live on the scheme's detail page. Operators name\n * schemes; the index is a printing detail, never something to pick.\n */\nexport function ScanCodesPage() {\n const navigate = useNavigate();\n const { data, isLoading } = useScanSchemes();\n const [creating, setCreating] = useState(false);\n\n const schemes = data?.schemes ?? [];\n const used = new Set(schemes.map((s) => s.version));\n const next = nextSchemeVersion(used);\n\n return (\n <div>\n <PageHeader title=\"Scan Codes\" />\n <p className=\"text-sm text-text-secondary max-w-form mb-8\">\n A scan code addresses one of your escalations by a value on its label. Add a\n scheme, name it, and point it at the metadata facet the scanned value matches —\n then define what each scan does on its detail page.\n </p>\n\n {isLoading ? (\n <div className=\"text-sm text-text-tertiary\">Loading…</div>\n ) : (\n <div className=\"divide-y divide-surface-border border-y border-surface-border max-w-form\">\n {schemes.map((scheme) => (\n <div\n key={scheme.version}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/admin/scan-codes/${scheme.version}`)}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') navigate(`/admin/scan-codes/${scheme.version}`); }}\n className=\"flex items-center gap-4 py-3 px-2 cursor-pointer hover:bg-surface-sunken transition-colors\"\n >\n <span className=\"w-9 h-8 flex items-center justify-center text-sm font-mono text-accent border border-surface-border rounded shrink-0\">\n {scheme.version}\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm text-text-primary truncate\">{scheme.name}</div>\n <div className=\"text-2xs text-text-tertiary truncate\">\n {scheme.kind === SCAN_SCHEME_KINDS.IDENTITY && 'identity badge · '}\n target facet <span className=\"font-mono\">{scheme.target_facet}</span>\n {' · '}{scheme.encoding === 'fixed'\n ? `fixed, ${scheme.target_length} digit target`\n : scheme.encoding === 'gtin'\n ? 'manufacturer barcodes (UPC / EAN)'\n : `delimited by \"${scheme.delimiter}\"`}\n </div>\n </div>\n <span className={`text-2xs uppercase tracking-widest ${scheme.enabled ? 'text-status-success' : 'text-text-quaternary'}`}>\n {scheme.enabled ? 'Enabled' : 'Disabled'}\n </span>\n </div>\n ))}\n <div className=\"flex items-center gap-4 py-3 px-2\">\n <span className=\"w-9 h-8 flex items-center justify-center text-text-quaternary shrink-0\">\n <ScanBarcode className=\"w-4 h-4\" strokeWidth={1.5} />\n </span>\n {next == null ? (\n <span className=\"text-2xs text-text-quaternary\">All 90 scheme slots are in use.</span>\n ) : !creating ? (\n <button\n type=\"button\"\n onClick={() => setCreating(true)}\n className=\"flex items-center gap-1.5 text-sm text-accent hover:text-accent-hover\"\n >\n <Plus className=\"w-3.5 h-3.5\" /> Add a scheme\n </button>\n ) : (\n <NewSchemeForm\n version={next}\n onDone={(version) => {\n setCreating(false);\n if (version != null) navigate(`/admin/scan-codes/${version}`);\n }}\n />\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction NewSchemeForm({\n version,\n onDone,\n}: {\n version: number;\n onDone: (version: number | null) => void;\n}) {\n const upsert = useUpsertScanScheme();\n const { data: roles } = useRoleDetails();\n const [name, setName] = useState('');\n const [kind, setKind] = useState<ScanSchemeKind>(SCAN_SCHEME_KINDS.ACTION);\n const [targetFacet, setTargetFacet] = useState('');\n const [encoding, setEncoding] = useState<'' | 'fixed' | 'delimited' | 'gtin'>('');\n const [targetLength, setTargetLength] = useState(8);\n const [grantTtlSeconds, setGrantTtlSeconds] = useState(3600);\n const [grantMaxUses, setGrantMaxUses] = useState(0);\n const [grantScope, setGrantScope] = useState<'action' | 'subject'>('action');\n\n const priorityFacets = Array.from(\n new Set((roles?.roles ?? []).map((r) => r.priority_facet).filter(Boolean) as string[]),\n );\n\n const isIdentity = kind === SCAN_SCHEME_KINDS.IDENTITY;\n const ttlValid = grantTtlSeconds >= 1 && grantTtlSeconds <= 86_400;\n\n const submit = async () => {\n if (!name || !targetFacet || !encoding) return;\n if (isIdentity && !ttlValid) return;\n await upsert.mutateAsync({\n version,\n name,\n kind,\n target_facet: targetFacet,\n encoding,\n target_length: encoding === 'fixed' ? targetLength : null,\n grant_ttl_seconds: isIdentity ? grantTtlSeconds : null,\n grant_max_uses: isIdentity ? grantMaxUses : 0,\n grant_scope: isIdentity ? grantScope : 'action',\n });\n onDone(version);\n };\n\n return (\n <div className=\"flex-1 space-y-3\">\n <p className=\"text-2xs text-text-tertiary\">\n Codes for this scheme start with <span className=\"font-mono text-text-secondary\">{version}</span> — assigned for you.\n </p>\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Kind <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity\n ? 'A badge — scanning it primes who acts next.'\n : 'An item code — scanning it acts on the item.'}\n </span>\n <select value={kind} onChange={(e) => setKind(e.target.value as ScanSchemeKind)} className=\"select\">\n <option value={SCAN_SCHEME_KINDS.ACTION}>Action</option>\n <option value={SCAN_SCHEME_KINDS.IDENTITY}>Identity</option>\n </select>\n </label>\n <label className=\"block flex-1 min-w-[16rem]\">\n <span className=\"block text-xs text-text-secondary mb-1\">Name <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity ? 'Say who the badge identifies — \"Associate badge\".' : 'Say what the target identifies — \"Printer serial\".'}\n </span>\n <input value={name} onChange={(e) => setName(e.target.value)} className=\"input w-full\" placeholder={isIdentity ? 'Associate badge' : 'Printer serial'} autoFocus />\n </label>\n <label className=\"block flex-1 min-w-[16rem]\">\n <span className=\"block text-xs text-text-secondary mb-1\">Target facet <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity\n ? 'The user-metadata key the badge matches (e.g. badge_id).'\n : 'The escalation metadata key the scanned value matches.'}\n </span>\n <input\n value={targetFacet}\n onChange={(e) => setTargetFacet(e.target.value)}\n className=\"input w-full font-mono\"\n placeholder={isIdentity ? 'badge_id' : 'serialNumber'}\n list=\"scan-facet-suggestions\"\n />\n <datalist id=\"scan-facet-suggestions\">\n {priorityFacets.map((f) => <option key={f} value={f} />)}\n </datalist>\n </label>\n </div>\n {isIdentity && (\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Grant TTL (seconds) <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">How long a badge scan stays primed (1–86400).</span>\n <input\n type=\"number\" min={1} max={86400}\n value={grantTtlSeconds}\n onChange={(e) => setGrantTtlSeconds(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n {!ttlValid && <span className=\"block text-2xs text-status-error mt-1\">Enter 1–86400 seconds.</span>}\n </label>\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Grant max uses</span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">0 = TTL-bound; n = the grant covers n acts.</span>\n <input\n type=\"number\" min={0}\n value={grantMaxUses}\n onChange={(e) => setGrantMaxUses(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n </label>\n <GrantScopeField value={grantScope} onChange={setGrantScope} />\n </div>\n )}\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Code style <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">Delimited for QR / Code 128, fixed digits for numeric labels, manufacturer barcodes for the codes printed on products.</span>\n <select value={encoding} onChange={(e) => setEncoding(e.target.value as any)} className=\"select\">\n <option value=\"\" disabled>Choose…</option>\n <option value=\"delimited\">Delimited text (QR / Code 128)</option>\n <option value=\"fixed\">Fixed digits (UPC)</option>\n {!isIdentity && <option value=\"gtin\">Manufacturer barcode (UPC / EAN)</option>}\n </select>\n </label>\n {encoding === 'fixed' && (\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Target digits <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">How many digits the target occupies.</span>\n <input\n type=\"number\" min={1} max={20}\n value={targetLength}\n onChange={(e) => setTargetLength(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n </label>\n )}\n </div>\n {upsert.error && <p className=\"text-xs text-status-error\">{(upsert.error as Error).message}</p>}\n <div className=\"flex gap-3\">\n <button type=\"button\" onClick={() => onDone(null)} className=\"btn-secondary text-xs\">Discard</button>\n <button\n type=\"button\"\n onClick={submit}\n disabled={!name || !targetFacet || !encoding || (isIdentity && !ttlValid) || upsert.isPending}\n className=\"btn-primary text-xs\"\n >\n Create scheme\n </button>\n </div>\n </div>\n );\n}\n"],"names":["nextSchemeVersion","used","v","ScanCodesPage","navigate","useNavigate","data","isLoading","useScanSchemes","creating","setCreating","useState","schemes","s","next","jsx","PageHeader","jsxs","scheme","e","SCAN_SCHEME_KINDS","ScanBarcode","NewSchemeForm","version","Plus","onDone","upsert","useUpsertScanScheme","roles","useRoleDetails","name","setName","kind","setKind","targetFacet","setTargetFacet","encoding","setEncoding","targetLength","setTargetLength","grantTtlSeconds","setGrantTtlSeconds","grantMaxUses","setGrantMaxUses","grantScope","setGrantScope","priorityFacets","r","isIdentity","ttlValid","submit","f","GrantScopeField"],"mappings":"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"}
1
+ {"version":3,"file":"ScanCodesPage-Cy05JOt4.js","sources":["../../src/pages/admin/scan-codes/ScanCodesPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { ScanBarcode, Plus } from 'lucide-react';\nimport { useScanSchemes, useUpsertScanScheme, SCAN_SCHEME_KINDS, type ScanSchemeKind } from '../../../api/scan-codes';\nimport { useRoleDetails } from '../../../api/roles';\nimport { GrantScopeField } from './GrantScopeField';\nimport { PageHeader } from '../../../components/common/layout/PageHeader';\n\n/** The first free two-digit scheme index (10-99), or null when all are taken. */\nfunction nextSchemeVersion(used: Set<number>): number | null {\n for (let v = 10; v <= 99; v++) if (!used.has(v)) return v;\n return null;\n}\n\n/**\n * Scan schemes — the named things a scanned code addresses. Each scheme owns a\n * two-digit index (assigned automatically, starting at 10) and declares which\n * escalation metadata facet the scanned target resolves against and how the\n * string parses. Its rules live on the scheme's detail page. Operators name\n * schemes; the index is a printing detail, never something to pick.\n */\nexport function ScanCodesPage() {\n const navigate = useNavigate();\n const { data, isLoading } = useScanSchemes();\n const [creating, setCreating] = useState(false);\n\n const schemes = data?.schemes ?? [];\n const used = new Set(schemes.map((s) => s.version));\n const next = nextSchemeVersion(used);\n\n return (\n <div>\n <PageHeader title=\"Scan Codes\" />\n <p className=\"text-sm text-text-secondary max-w-form mb-8\">\n A scan code addresses one of your escalations by a value on its label. Add a\n scheme, name it, and point it at the metadata facet the scanned value matches —\n then define what each scan does on its detail page.\n </p>\n\n {isLoading ? (\n <div className=\"text-sm text-text-tertiary\">Loading…</div>\n ) : (\n <div className=\"divide-y divide-surface-border border-y border-surface-border max-w-form\">\n {schemes.map((scheme) => (\n <div\n key={scheme.version}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/admin/scan-codes/${scheme.version}`)}\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') navigate(`/admin/scan-codes/${scheme.version}`); }}\n className=\"flex items-center gap-4 py-3 px-2 cursor-pointer hover:bg-surface-sunken transition-colors\"\n >\n <span className=\"w-9 h-8 flex items-center justify-center text-sm font-mono text-accent border border-surface-border rounded shrink-0\">\n {scheme.version}\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm text-text-primary truncate\">{scheme.name}</div>\n <div className=\"text-2xs text-text-tertiary truncate\">\n {scheme.kind === SCAN_SCHEME_KINDS.IDENTITY && 'identity badge · '}\n target facet <span className=\"font-mono\">{scheme.target_facet}</span>\n {' · '}{scheme.encoding === 'fixed'\n ? `fixed, ${scheme.target_length} digit target`\n : scheme.encoding === 'gtin'\n ? 'manufacturer barcodes (UPC / EAN)'\n : `delimited by \"${scheme.delimiter}\"`}\n </div>\n </div>\n <span className={`text-2xs uppercase tracking-widest ${scheme.enabled ? 'text-status-success' : 'text-text-quaternary'}`}>\n {scheme.enabled ? 'Enabled' : 'Disabled'}\n </span>\n </div>\n ))}\n <div className=\"flex items-center gap-4 py-3 px-2\">\n <span className=\"w-9 h-8 flex items-center justify-center text-text-quaternary shrink-0\">\n <ScanBarcode className=\"w-4 h-4\" strokeWidth={1.5} />\n </span>\n {next == null ? (\n <span className=\"text-2xs text-text-quaternary\">All 90 scheme slots are in use.</span>\n ) : !creating ? (\n <button\n type=\"button\"\n onClick={() => setCreating(true)}\n className=\"flex items-center gap-1.5 text-sm text-accent hover:text-accent-hover\"\n >\n <Plus className=\"w-3.5 h-3.5\" /> Add a scheme\n </button>\n ) : (\n <NewSchemeForm\n version={next}\n onDone={(version) => {\n setCreating(false);\n if (version != null) navigate(`/admin/scan-codes/${version}`);\n }}\n />\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction NewSchemeForm({\n version,\n onDone,\n}: {\n version: number;\n onDone: (version: number | null) => void;\n}) {\n const upsert = useUpsertScanScheme();\n const { data: roles } = useRoleDetails();\n const [name, setName] = useState('');\n const [kind, setKind] = useState<ScanSchemeKind>(SCAN_SCHEME_KINDS.ACTION);\n const [targetFacet, setTargetFacet] = useState('');\n const [encoding, setEncoding] = useState<'' | 'fixed' | 'delimited' | 'gtin'>('');\n const [targetLength, setTargetLength] = useState(8);\n const [grantTtlSeconds, setGrantTtlSeconds] = useState(3600);\n const [grantMaxUses, setGrantMaxUses] = useState(0);\n const [grantScope, setGrantScope] = useState<'action' | 'subject'>('action');\n\n const priorityFacets = Array.from(\n new Set((roles?.roles ?? []).map((r) => r.priority_facet).filter(Boolean) as string[]),\n );\n\n const isIdentity = kind === SCAN_SCHEME_KINDS.IDENTITY;\n const ttlValid = grantTtlSeconds >= 1 && grantTtlSeconds <= 86_400;\n\n const submit = async () => {\n if (!name || !targetFacet || !encoding) return;\n if (isIdentity && !ttlValid) return;\n await upsert.mutateAsync({\n version,\n name,\n kind,\n target_facet: targetFacet,\n encoding,\n target_length: encoding === 'fixed' ? targetLength : null,\n grant_ttl_seconds: isIdentity ? grantTtlSeconds : null,\n grant_max_uses: isIdentity ? grantMaxUses : 0,\n grant_scope: isIdentity ? grantScope : 'action',\n });\n onDone(version);\n };\n\n return (\n <div className=\"flex-1 space-y-3\">\n <p className=\"text-2xs text-text-tertiary\">\n Codes for this scheme start with <span className=\"font-mono text-text-secondary\">{version}</span> — assigned for you.\n </p>\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Kind <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity\n ? 'A badge — scanning it primes who acts next.'\n : 'An item code — scanning it acts on the item.'}\n </span>\n <select value={kind} onChange={(e) => setKind(e.target.value as ScanSchemeKind)} className=\"select\">\n <option value={SCAN_SCHEME_KINDS.ACTION}>Action</option>\n <option value={SCAN_SCHEME_KINDS.IDENTITY}>Identity</option>\n </select>\n </label>\n <label className=\"block flex-1 min-w-[16rem]\">\n <span className=\"block text-xs text-text-secondary mb-1\">Name <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity ? 'Say who the badge identifies — \"Associate badge\".' : 'Say what the target identifies — \"Printer serial\".'}\n </span>\n <input value={name} onChange={(e) => setName(e.target.value)} className=\"input w-full\" placeholder={isIdentity ? 'Associate badge' : 'Printer serial'} autoFocus />\n </label>\n <label className=\"block flex-1 min-w-[16rem]\">\n <span className=\"block text-xs text-text-secondary mb-1\">Target facet <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">\n {isIdentity\n ? 'The user-metadata key the badge matches (e.g. badge_id).'\n : 'The escalation metadata key the scanned value matches.'}\n </span>\n <input\n value={targetFacet}\n onChange={(e) => setTargetFacet(e.target.value)}\n className=\"input w-full font-mono\"\n placeholder={isIdentity ? 'badge_id' : 'serialNumber'}\n list=\"scan-facet-suggestions\"\n />\n <datalist id=\"scan-facet-suggestions\">\n {priorityFacets.map((f) => <option key={f} value={f} />)}\n </datalist>\n </label>\n </div>\n {isIdentity && (\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Grant TTL (seconds) <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">How long a badge scan stays primed (1–86400).</span>\n <input\n type=\"number\" min={1} max={86400}\n value={grantTtlSeconds}\n onChange={(e) => setGrantTtlSeconds(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n {!ttlValid && <span className=\"block text-2xs text-status-error mt-1\">Enter 1–86400 seconds.</span>}\n </label>\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Grant max uses</span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">0 = TTL-bound; n = the grant covers n acts.</span>\n <input\n type=\"number\" min={0}\n value={grantMaxUses}\n onChange={(e) => setGrantMaxUses(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n </label>\n <GrantScopeField value={grantScope} onChange={setGrantScope} />\n </div>\n )}\n <div className=\"flex flex-wrap gap-4\">\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Code style <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">Delimited for QR / Code 128, fixed digits for numeric labels, manufacturer barcodes for the codes printed on products.</span>\n <select value={encoding} onChange={(e) => setEncoding(e.target.value as any)} className=\"select\">\n <option value=\"\" disabled>Choose…</option>\n <option value=\"delimited\">Delimited text (QR / Code 128)</option>\n <option value=\"fixed\">Fixed digits (UPC)</option>\n {!isIdentity && <option value=\"gtin\">Manufacturer barcode (UPC / EAN)</option>}\n </select>\n </label>\n {encoding === 'fixed' && (\n <label className=\"block\">\n <span className=\"block text-xs text-text-secondary mb-1\">Target digits <span className=\"text-status-error\">*</span></span>\n <span className=\"block text-2xs text-text-tertiary mb-1\">How many digits the target occupies.</span>\n <input\n type=\"number\" min={1} max={20}\n value={targetLength}\n onChange={(e) => setTargetLength(Number(e.target.value))}\n className=\"input w-[12rem]\"\n />\n </label>\n )}\n </div>\n {upsert.error && <p className=\"text-xs text-status-error\">{(upsert.error as Error).message}</p>}\n <div className=\"flex gap-3\">\n <button type=\"button\" onClick={() => onDone(null)} className=\"btn-secondary text-xs\">Discard</button>\n <button\n type=\"button\"\n onClick={submit}\n disabled={!name || !targetFacet || !encoding || (isIdentity && !ttlValid) || upsert.isPending}\n className=\"btn-primary text-xs\"\n >\n Create scheme\n </button>\n </div>\n </div>\n );\n}\n"],"names":["nextSchemeVersion","used","v","ScanCodesPage","navigate","useNavigate","data","isLoading","useScanSchemes","creating","setCreating","useState","schemes","s","next","jsx","PageHeader","jsxs","scheme","e","SCAN_SCHEME_KINDS","ScanBarcode","NewSchemeForm","version","Plus","onDone","upsert","useUpsertScanScheme","roles","useRoleDetails","name","setName","kind","setKind","targetFacet","setTargetFacet","encoding","setEncoding","targetLength","setTargetLength","grantTtlSeconds","setGrantTtlSeconds","grantMaxUses","setGrantMaxUses","grantScope","setGrantScope","priorityFacets","r","isIdentity","ttlValid","submit","f","GrantScopeField"],"mappings":"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"}