@cccsaurora/howler-ui 2.18.0 → 2.19.0-cases.862

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 (342) hide show
  1. package/api/index.d.ts +4 -0
  2. package/api/index.js +10 -2
  3. package/api/search/case.d.ts +4 -0
  4. package/api/search/case.js +8 -0
  5. package/api/search/facet/hit.d.ts +1 -3
  6. package/api/search/facet/index.d.ts +3 -1
  7. package/api/search/index.d.ts +2 -1
  8. package/api/search/index.js +2 -1
  9. package/api/socket/index.d.ts +3 -0
  10. package/api/socket/index.js +6 -0
  11. package/api/socket/viewers.d.ts +2 -0
  12. package/api/socket/viewers.js +8 -0
  13. package/api/socket/viewers.test.js +44 -0
  14. package/api/v2/case/index.d.ts +9 -0
  15. package/api/v2/case/index.js +21 -0
  16. package/api/v2/case/items.d.ts +6 -0
  17. package/api/v2/case/items.js +18 -0
  18. package/api/v2/case/rules.d.ts +6 -0
  19. package/api/v2/case/rules.js +18 -0
  20. package/api/v2/index.d.ts +4 -0
  21. package/api/v2/index.js +6 -0
  22. package/api/v2/search/facet.d.ts +3 -0
  23. package/api/v2/search/facet.js +12 -0
  24. package/api/v2/search/index.d.ts +5 -0
  25. package/api/v2/search/index.js +24 -0
  26. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  27. package/components/app/App.js +52 -12
  28. package/components/app/hooks/useMatchers.d.ts +1 -1
  29. package/components/app/hooks/useMatchers.js +23 -11
  30. package/components/app/hooks/useMatchers.test.js +22 -22
  31. package/components/app/hooks/useTitle.js +5 -5
  32. package/components/app/providers/FavouritesProvider.js +2 -2
  33. package/components/app/providers/ModalProvider.d.ts +1 -0
  34. package/components/app/providers/ParameterProvider.d.ts +9 -2
  35. package/components/app/providers/ParameterProvider.js +165 -240
  36. package/components/app/providers/ParameterProvider.test.js +346 -94
  37. package/components/app/providers/RecordProvider.d.ts +23 -0
  38. package/components/app/providers/{HitProvider.js → RecordProvider.js} +42 -42
  39. package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
  40. package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +12 -17
  41. package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +52 -71
  42. package/components/app/providers/SocketProvider.d.ts +11 -2
  43. package/components/app/providers/SocketProvider.js +18 -5
  44. package/components/app/providers/UserListProvider.js +28 -8
  45. package/components/elements/ContextMenu.d.ts +56 -0
  46. package/components/elements/ContextMenu.js +109 -0
  47. package/components/elements/ContextMenu.test.d.ts +1 -0
  48. package/components/elements/ContextMenu.test.js +215 -0
  49. package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
  50. package/components/elements/ObjectDetails.d.ts +6 -0
  51. package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
  52. package/components/elements/PluginTypography.d.ts +2 -1
  53. package/components/elements/PluginTypography.js +3 -2
  54. package/components/elements/UserList.d.ts +5 -2
  55. package/components/elements/UserList.js +18 -8
  56. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  57. package/components/elements/case/CaseCard.d.ts +12 -0
  58. package/components/elements/case/CaseCard.js +42 -0
  59. package/components/elements/case/CasePreview.d.ts +6 -0
  60. package/components/elements/case/CasePreview.js +17 -0
  61. package/components/elements/case/StatusIcon.d.ts +5 -0
  62. package/components/elements/case/StatusIcon.js +13 -0
  63. package/components/elements/display/ChipPopper.d.ts +1 -1
  64. package/components/elements/display/ChipPopper.js +5 -5
  65. package/components/elements/display/HowlerCard.js +1 -1
  66. package/components/elements/display/Modal.js +2 -0
  67. package/components/elements/hit/HitActions.js +4 -4
  68. package/components/elements/hit/HitBanner.d.ts +1 -0
  69. package/components/elements/hit/HitBanner.js +34 -51
  70. package/components/elements/hit/HitCard.d.ts +2 -0
  71. package/components/elements/hit/HitCard.js +7 -7
  72. package/components/elements/hit/HitLabels.js +2 -2
  73. package/components/elements/hit/HitOutline.d.ts +1 -0
  74. package/components/elements/hit/HitOutline.js +3 -3
  75. package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
  76. package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +10 -4
  77. package/components/elements/hit/HitSummary.d.ts +2 -1
  78. package/components/elements/hit/HitSummary.js +6 -5
  79. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  80. package/components/elements/hit/elements/AnalyticLink.d.ts +9 -0
  81. package/components/elements/hit/elements/AnalyticLink.js +22 -0
  82. package/components/elements/hit/elements/Assigned.js +6 -3
  83. package/components/elements/hit/elements/Assigned.test.d.ts +1 -0
  84. package/components/elements/hit/elements/Assigned.test.js +65 -0
  85. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  86. package/components/elements/hit/related/RelatedRecords.js +63 -0
  87. package/components/elements/observable/ObservableCard.d.ts +6 -0
  88. package/components/elements/observable/ObservableCard.js +22 -0
  89. package/components/elements/observable/ObservablePreview.d.ts +6 -0
  90. package/components/elements/observable/ObservablePreview.js +12 -0
  91. package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
  92. package/components/elements/{hit/HitComments.js → record/RecordComments.js} +29 -28
  93. package/components/{routes/hits/search/HitContextMenu.d.ts → elements/record/RecordContextMenu.d.ts} +3 -3
  94. package/components/elements/record/RecordContextMenu.js +268 -0
  95. package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
  96. package/components/{routes/hits/search/HitContextMenu.test.js → elements/record/RecordContextMenu.test.js} +190 -39
  97. package/components/elements/record/RecordRelated.d.ts +7 -0
  98. package/components/elements/record/RecordRelated.js +34 -0
  99. package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
  100. package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +15 -13
  101. package/components/elements/view/ViewTitle.d.ts +1 -0
  102. package/components/elements/view/ViewTitle.js +9 -2
  103. package/components/hooks/useHitActions.d.ts +1 -1
  104. package/components/hooks/useHitActions.js +4 -4
  105. package/components/hooks/useMyPreferences.js +10 -1
  106. package/components/hooks/useMySearch.js +2 -2
  107. package/components/hooks/useMySitemap.js +4 -1
  108. package/components/hooks/useMyTheme.js +9 -2
  109. package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
  110. package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
  111. package/components/hooks/useRelatedRecords.d.ts +13 -0
  112. package/components/hooks/useRelatedRecords.js +32 -0
  113. package/components/routes/403.d.ts +3 -0
  114. package/components/routes/403.js +10 -0
  115. package/components/routes/action/edit/ActionEditor.js +3 -3
  116. package/components/routes/action/useMyActionFunctions.js +4 -1
  117. package/components/routes/action/view/ActionDetails.js +6 -1
  118. package/components/routes/action/view/ActionSearch.js +5 -4
  119. package/components/routes/action/view/markdown/integrations.en.md.js +1 -1
  120. package/components/routes/action/view/markdown/integrations.fr.md.js +1 -1
  121. package/components/routes/advanced/QueryBuilder.js +1 -1
  122. package/components/routes/advanced/QueryEditor.js +3 -3
  123. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  124. package/components/routes/analytics/AnalyticDetails.js +2 -2
  125. package/components/routes/analytics/AnalyticSearch.js +1 -1
  126. package/components/routes/cases/CaseViewer.d.ts +2 -0
  127. package/components/routes/cases/CaseViewer.js +44 -0
  128. package/components/routes/cases/CaseViewer.test.d.ts +1 -0
  129. package/components/routes/cases/CaseViewer.test.js +133 -0
  130. package/components/routes/cases/Cases.d.ts +2 -0
  131. package/components/routes/cases/Cases.js +148 -0
  132. package/components/routes/cases/constants.d.ts +6 -0
  133. package/components/routes/cases/constants.js +6 -0
  134. package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
  135. package/components/routes/cases/detail/AlertPanel.js +33 -0
  136. package/components/routes/cases/detail/CaseAssets.d.ts +11 -0
  137. package/components/routes/cases/detail/CaseAssets.js +104 -0
  138. package/components/routes/cases/detail/CaseAssets.test.d.ts +1 -0
  139. package/components/routes/cases/detail/CaseAssets.test.js +167 -0
  140. package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
  141. package/components/routes/cases/detail/CaseDashboard.js +66 -0
  142. package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
  143. package/components/routes/cases/detail/CaseDetails.js +70 -0
  144. package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
  145. package/components/routes/cases/detail/CaseOverview.js +43 -0
  146. package/components/routes/cases/detail/CaseRules.d.ts +7 -0
  147. package/components/routes/cases/detail/CaseRules.js +57 -0
  148. package/components/routes/cases/detail/CaseRules.test.d.ts +1 -0
  149. package/components/routes/cases/detail/CaseRules.test.js +221 -0
  150. package/components/routes/cases/detail/CaseSidebar.d.ts +8 -0
  151. package/components/routes/cases/detail/CaseSidebar.js +107 -0
  152. package/components/routes/cases/detail/CaseSidebar.test.d.ts +1 -0
  153. package/components/routes/cases/detail/CaseSidebar.test.js +266 -0
  154. package/components/routes/cases/detail/CaseTask.d.ts +11 -0
  155. package/components/routes/cases/detail/CaseTask.js +66 -0
  156. package/components/routes/cases/detail/CaseTimeline.d.ts +12 -0
  157. package/components/routes/cases/detail/CaseTimeline.js +106 -0
  158. package/components/routes/cases/detail/CaseTimeline.test.d.ts +1 -0
  159. package/components/routes/cases/detail/CaseTimeline.test.js +320 -0
  160. package/components/routes/cases/detail/CreateRuleDialog.d.ts +9 -0
  161. package/components/routes/cases/detail/CreateRuleDialog.js +163 -0
  162. package/components/routes/cases/detail/CreateRuleDialog.test.d.ts +1 -0
  163. package/components/routes/cases/detail/CreateRuleDialog.test.js +259 -0
  164. package/components/routes/cases/detail/ItemPage.d.ts +6 -0
  165. package/components/routes/cases/detail/ItemPage.js +95 -0
  166. package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
  167. package/components/routes/cases/detail/RelatedCasePanel.js +34 -0
  168. package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
  169. package/components/routes/cases/detail/TaskPanel.js +52 -0
  170. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +11 -0
  171. package/components/routes/cases/detail/aggregates/CaseAggregate.js +24 -0
  172. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
  173. package/components/routes/cases/detail/aggregates/SourceAggregate.js +26 -0
  174. package/components/routes/cases/detail/assets/Asset.d.ts +14 -0
  175. package/components/routes/cases/detail/assets/Asset.js +12 -0
  176. package/components/routes/cases/detail/assets/Asset.test.d.ts +1 -0
  177. package/components/routes/cases/detail/assets/Asset.test.js +72 -0
  178. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +20 -0
  179. package/components/routes/cases/detail/sidebar/CaseFolder.js +83 -0
  180. package/components/routes/cases/detail/sidebar/CaseFolder.test.d.ts +1 -0
  181. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +295 -0
  182. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +34 -0
  183. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +103 -0
  184. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +1 -0
  185. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +363 -0
  186. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +25 -0
  187. package/components/routes/cases/detail/sidebar/FolderEntry.js +88 -0
  188. package/components/routes/cases/detail/sidebar/FolderEntry.test.d.ts +1 -0
  189. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +206 -0
  190. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +5 -0
  191. package/components/routes/cases/detail/sidebar/RootDropZone.js +33 -0
  192. package/components/routes/cases/detail/sidebar/types.d.ts +9 -0
  193. package/components/routes/cases/detail/sidebar/utils.d.ts +3 -0
  194. package/components/routes/cases/detail/sidebar/utils.js +29 -0
  195. package/components/routes/cases/detail/sidebar/utils.test.d.ts +1 -0
  196. package/components/routes/cases/detail/sidebar/utils.test.js +82 -0
  197. package/components/routes/cases/hooks/useCase.d.ts +13 -0
  198. package/components/routes/cases/hooks/useCase.js +69 -0
  199. package/components/routes/cases/hooks/useCase.test.d.ts +1 -0
  200. package/components/routes/cases/hooks/useCase.test.js +141 -0
  201. package/components/routes/cases/modals/AddToCaseModal.d.ts +7 -0
  202. package/components/routes/cases/modals/AddToCaseModal.js +59 -0
  203. package/components/routes/cases/modals/AddToCaseModal.test.d.ts +1 -0
  204. package/components/routes/cases/modals/AddToCaseModal.test.js +313 -0
  205. package/components/routes/cases/modals/CaseRecordRow.d.ts +9 -0
  206. package/components/routes/cases/modals/CaseRecordRow.js +15 -0
  207. package/components/routes/cases/modals/CreateCaseModal.d.ts +7 -0
  208. package/components/routes/cases/modals/CreateCaseModal.js +55 -0
  209. package/components/routes/cases/modals/CreateCaseModal.test.d.ts +1 -0
  210. package/components/routes/cases/modals/CreateCaseModal.test.js +358 -0
  211. package/components/routes/cases/modals/RenameItemModal.d.ts +9 -0
  212. package/components/routes/cases/modals/RenameItemModal.js +48 -0
  213. package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
  214. package/components/routes/cases/modals/ResolveModal.js +115 -0
  215. package/components/routes/cases/modals/ResolveModal.test.d.ts +1 -0
  216. package/components/routes/cases/modals/ResolveModal.test.js +394 -0
  217. package/components/routes/cases/modals/hooks.d.ts +7 -0
  218. package/components/routes/cases/modals/hooks.js +44 -0
  219. package/components/routes/cases/modals/types.d.ts +5 -0
  220. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +6 -0
  221. package/components/routes/cases/search/CaseAssigneeFilter.js +33 -0
  222. package/components/routes/cases/search/CaseAssigneeFilter.test.d.ts +1 -0
  223. package/components/routes/cases/search/CaseAssigneeFilter.test.js +127 -0
  224. package/components/routes/cases/search/CaseDateFilter.d.ts +13 -0
  225. package/components/routes/cases/search/CaseDateFilter.js +26 -0
  226. package/components/routes/cases/search/CaseDateFilter.test.d.ts +1 -0
  227. package/components/routes/cases/search/CaseDateFilter.test.js +115 -0
  228. package/components/routes/cases/search/CaseStatusFilter.d.ts +6 -0
  229. package/components/routes/cases/search/CaseStatusFilter.js +13 -0
  230. package/components/routes/cases/search/CaseStatusFilter.test.d.ts +1 -0
  231. package/components/routes/cases/search/CaseStatusFilter.test.js +86 -0
  232. package/components/routes/dossiers/DossierEditor.js +2 -2
  233. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  234. package/components/routes/help/ActionIntroductionDocumentation.js +1 -1
  235. package/components/routes/help/ApiDocumentation.js +1 -1
  236. package/components/routes/help/HitBannerDocumentation.js +1 -0
  237. package/components/routes/help/HitDocumentation.js +1 -3
  238. package/components/routes/help/markdown/en/retention.md.js +1 -1
  239. package/components/routes/help/markdown/fr/retention.md.js +1 -1
  240. package/components/routes/hits/search/InformationPane.d.ts +1 -0
  241. package/components/routes/hits/search/InformationPane.js +50 -63
  242. package/components/routes/hits/search/LayoutSettings.js +3 -3
  243. package/components/routes/hits/search/QuerySettings.js +2 -1
  244. package/components/routes/hits/search/QuerySettings.test.js +14 -9
  245. package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +9 -9
  246. package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
  247. package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
  248. package/components/routes/hits/search/SearchPane.js +26 -49
  249. package/components/routes/hits/search/ViewLink.js +3 -3
  250. package/components/routes/hits/search/ViewLink.test.js +8 -8
  251. package/components/routes/hits/search/grid/AddColumnModal.js +5 -4
  252. package/components/routes/hits/search/grid/EnhancedCell.d.ts +2 -1
  253. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  254. package/components/routes/hits/search/grid/HitGrid.js +20 -18
  255. package/components/routes/hits/search/grid/{HitRow.d.ts → RecordRow.d.ts} +3 -2
  256. package/components/routes/hits/search/grid/{HitRow.js → RecordRow.js} +10 -8
  257. package/components/routes/hits/search/shared/IndexPicker.d.ts +2 -0
  258. package/components/routes/hits/search/shared/IndexPicker.js +20 -0
  259. package/components/routes/hits/view/HitViewer.js +12 -13
  260. package/components/routes/home/AddNewCard.js +1 -1
  261. package/components/routes/home/ViewCard.js +47 -41
  262. package/components/routes/observables/ObservableViewer.d.ts +7 -0
  263. package/components/routes/observables/ObservableViewer.js +27 -0
  264. package/components/routes/overviews/OverviewViewer.js +2 -2
  265. package/components/routes/overviews/template/en.md.js +1 -1
  266. package/components/routes/overviews/template/fr.md.js +1 -1
  267. package/components/routes/views/ViewComposer.js +46 -19
  268. package/locales/en/translation.json +125 -3
  269. package/locales/fr/translation.json +123 -3
  270. package/models/WithMetadata.d.ts +2 -1
  271. package/models/entities/generated/ApiType.d.ts +1 -1
  272. package/models/entities/generated/AttachmentsFile.d.ts +12 -0
  273. package/models/entities/generated/Case.d.ts +28 -0
  274. package/models/entities/generated/DestinationOriginal.d.ts +19 -0
  275. package/models/entities/generated/EmailAttachment.d.ts +8 -0
  276. package/models/entities/generated/EmailParent.d.ts +19 -0
  277. package/models/entities/generated/Enrichments.d.ts +7 -0
  278. package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
  279. package/models/entities/generated/Hit.d.ts +1 -0
  280. package/models/entities/generated/Howler.d.ts +0 -5
  281. package/models/entities/generated/HttpResponse.d.ts +11 -0
  282. package/models/entities/generated/Item.d.ts +9 -0
  283. package/models/entities/generated/Observable.d.ts +85 -0
  284. package/models/entities/generated/ObservableCloud.d.ts +20 -0
  285. package/models/entities/generated/ObservableDestination.d.ts +23 -0
  286. package/models/entities/generated/ObservableEmail.d.ts +30 -0
  287. package/models/entities/generated/ObservableFile.d.ts +36 -0
  288. package/models/entities/generated/ObservableHowler.d.ts +42 -0
  289. package/models/entities/generated/ObservableHttp.d.ts +11 -0
  290. package/models/entities/generated/ObservableObserver.d.ts +21 -0
  291. package/models/entities/generated/ObservableOrganization.d.ts +7 -0
  292. package/models/entities/generated/ObservableProcess.d.ts +34 -0
  293. package/models/entities/generated/ObservableSource.d.ts +23 -0
  294. package/models/entities/generated/ObservableThreat.d.ts +21 -0
  295. package/models/entities/generated/ObservableTls.d.ts +12 -0
  296. package/models/entities/generated/ObserverIngress.d.ts +9 -0
  297. package/models/entities/generated/Rule.d.ts +6 -9
  298. package/models/entities/generated/Task.d.ts +10 -0
  299. package/models/entities/generated/Threat.d.ts +2 -2
  300. package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
  301. package/models/entities/generated/View.d.ts +1 -0
  302. package/models/socket/CaseUpdate.d.ts +5 -0
  303. package/models/socket/ViewersUpdate.d.ts +4 -0
  304. package/package.json +23 -8
  305. package/plugins/clue/components/ClueTypography.js +2 -2
  306. package/plugins/clue/utils.d.ts +2 -1
  307. package/tests/mocks.d.ts +11 -1
  308. package/tests/mocks.js +12 -7
  309. package/tests/server-handlers.js +6 -1
  310. package/tests/utils.d.ts +4 -0
  311. package/tests/utils.js +20 -0
  312. package/utils/constants.d.ts +4 -3
  313. package/utils/constants.js +6 -0
  314. package/utils/hitFunctions.d.ts +2 -1
  315. package/utils/hitFunctions.js +4 -4
  316. package/utils/menuUtils.js +1 -1
  317. package/utils/socketUtils.d.ts +14 -0
  318. package/utils/socketUtils.js +17 -1
  319. package/utils/socketUtils.test.d.ts +1 -0
  320. package/utils/socketUtils.test.js +59 -0
  321. package/utils/typeUtils.d.ts +7 -0
  322. package/utils/typeUtils.js +27 -0
  323. package/utils/viewUtils.js +3 -0
  324. package/components/app/providers/HitProvider.d.ts +0 -22
  325. package/components/elements/display/icons/BundleButton.d.ts +0 -6
  326. package/components/elements/display/icons/BundleButton.js +0 -32
  327. package/components/elements/hit/HitRelated.d.ts +0 -6
  328. package/components/elements/hit/HitRelated.js +0 -7
  329. package/components/routes/help/BundleDocumentation.d.ts +0 -3
  330. package/components/routes/help/BundleDocumentation.js +0 -12
  331. package/components/routes/help/markdown/en/bundles.md.js +0 -1
  332. package/components/routes/help/markdown/fr/bundles.md.js +0 -1
  333. package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
  334. package/components/routes/hits/search/BundleParentMenu.js +0 -32
  335. package/components/routes/hits/search/BundleScroller.d.ts +0 -2
  336. package/components/routes/hits/search/BundleScroller.js +0 -6
  337. package/components/routes/hits/search/HitContextMenu.js +0 -229
  338. /package/{components/app/providers/HitSearchProvider.test.d.ts → api/socket/viewers.test.d.ts} +0 -0
  339. /package/components/{routes/hits/search/HitContextMenu.test.d.ts → app/providers/RecordSearchProvider.test.d.ts} +0 -0
  340. /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
  341. /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
  342. /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
@@ -4,8 +4,8 @@ import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
4
4
  import AssignUserDrawer from '@cccsaurora/howler-ui/components/app/drawers/AssignUserDrawer';
5
5
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
6
  import { AppDrawerContext } from '@cccsaurora/howler-ui/components/app/providers/AppDrawerProvider';
7
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
7
  import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
8
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
9
9
  import RationaleModal from '@cccsaurora/howler-ui/components/elements/display/modals/RationaleModal';
10
10
  import { useCallback, useContext, useMemo, useState } from 'react';
11
11
  import { useTranslation } from 'react-i18next';
@@ -31,7 +31,7 @@ const useHitActions = (_hits) => {
31
31
  const { showModal } = useContext(ModalContext);
32
32
  const { showWarningMessage } = useMySnackbar();
33
33
  const { dispatchApi } = useMyApi();
34
- const updateHit = useContextSelector(HitContext, ctx => ctx.updateHit);
34
+ const updateHit = useContextSelector(RecordContext, ctx => ctx.updateRecord);
35
35
  const [loading, setLoading] = useState(false);
36
36
  const hits = useMemo(() => (Array.isArray(_hits) ? _hits : [_hits]).filter(_hit => !!_hit), [_hits]);
37
37
  const canVote = useMemo(() => hits.every(hit => hit?.howler.assignment !== user.username || hit?.howler.status === 'in-progress'), [hits, user.username]);
@@ -90,9 +90,9 @@ const useHitActions = (_hits) => {
90
90
  }
91
91
  }
92
92
  }, [dispatchApi, hits, selectedVote, updateHit, user.email]);
93
- const assess = useCallback(async (assessment, skipRationale = false) => {
93
+ const assess = useCallback(async (assessment, skipRationale = false, providedRationale = null) => {
94
94
  const rationale = skipRationale
95
- ? t('rationale.default', { assessment })
95
+ ? (providedRationale ?? t('rationale.default', { assessment }))
96
96
  : await new Promise(res => {
97
97
  showModal(_jsx(RationaleModal, { hits: hits, onSubmit: _rationale => {
98
98
  res(_rationale);
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Api, Article, Book, Code, Dashboard, Description, ExitToApp, FormatListBulleted, Help, HelpCenter, Key, ManageSearch, QueryStats, SavedSearch, Search, Settings, SettingsSuggest, Shield, Storage, SupervisorAccount, Terminal, Topic } from '@mui/icons-material';
2
+ import { Api, Article, Book, BookRounded, Code, Dashboard, Description, ExitToApp, FormatListBulleted, Help, HelpCenter, Key, ManageSearch, QueryStats, SavedSearch, Search, Settings, SettingsSuggest, Shield, Storage, SupervisorAccount, Terminal, Topic } from '@mui/icons-material';
3
3
  import { Stack } from '@mui/material';
4
4
  import { AppBrand } from '@cccsaurora/howler-ui/branding/AppBrand';
5
5
  import { AppBarContext } from '@cccsaurora/howler-ui/components/app/providers/AppBarProvider';
@@ -24,6 +24,15 @@ const useMyPreferences = () => {
24
24
  icon: _jsx(Dashboard, {})
25
25
  }
26
26
  },
27
+ {
28
+ type: 'item',
29
+ element: {
30
+ id: 'cases',
31
+ i18nKey: 'route.cases',
32
+ route: '/cases',
33
+ icon: _jsx(BookRounded, {})
34
+ }
35
+ },
27
36
  {
28
37
  type: 'group',
29
38
  element: {
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Alert, Box, Typography } from '@mui/material';
3
3
  import api from '@cccsaurora/howler-ui/api';
4
- import HitQuickSearch from '@cccsaurora/howler-ui/components/elements/hit/HitQuickSearch';
4
+ import HitPreview from '@cccsaurora/howler-ui/components/elements/hit/HitPreview';
5
5
  import { useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Link, useNavigate } from 'react-router-dom';
@@ -40,7 +40,7 @@ const useMySearch = () => {
40
40
  },
41
41
  headerRenderer: (state) => (state.result?.error || !state.items) && (_jsx(Box, { sx: { p: 1, pb: 0, textAlign: 'center' }, children: state.result?.error ? (_jsx(Alert, { severity: "error", color: "error", children: t('hit.search.invalid') })) : ((!state.items || state.items.length === 0) && (_jsx(Typography, { sx: { mb: -1, color: 'text.secondary' }, children: t('hit.quicksearch') }))) })),
42
42
  itemRenderer: (item, options) => {
43
- return (_jsx(Link, { to: `/hits/${item.id}`, style: { flex: 1, textDecoration: 'none', color: 'inherit', overflow: 'hidden' }, children: _jsx(HitQuickSearch, { hit: item.item, options: options }) }));
43
+ return (_jsx(Link, { to: `/hits/${item.id}`, style: { flex: 1, textDecoration: 'none', color: 'inherit', overflow: 'hidden' }, children: _jsx(HitPreview, { hit: item.item, options: options }) }));
44
44
  }
45
45
  }), [navigate, pageCount, t]);
46
46
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Article, Book, Code, CreateNewFolder, Dashboard, Description, Edit, EditNote, FormatListBulleted, Help, Info, Key, Person, PersonSearch, QueryStats, SavedSearch, Search, Settings, SettingsSuggest, Shield, Storage, Terminal, Topic, Work } from '@mui/icons-material';
2
+ import { Article, Book, BookRounded, Code, CreateNewFolder, Dashboard, Description, Edit, EditNote, FormatListBulleted, Help, Info, Key, Person, PersonSearch, QueryStats, SavedSearch, Search, Settings, SettingsSuggest, Shield, Storage, Terminal, Topic, Work } from '@mui/icons-material';
3
3
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
4
4
  import { useMemo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
@@ -24,6 +24,9 @@ const useMySitemap = () => {
24
24
  return useMemo(() => ({
25
25
  routes: [
26
26
  { path: '/', title: t('route.home'), isRoot: true, icon: _jsx(Dashboard, {}) },
27
+ { path: '/cases', title: t('route.cases'), isRoot: true, icon: _jsx(BookRounded, {}) },
28
+ { path: '/cases/:id', title: t('route.cases.view'), breadcrumbs: ['/cases'] },
29
+ { path: '/cases/:id/*', title: t('route.cases.view'), isLeaf: true, breadcrumbs: ['/cases'] },
27
30
  { path: '/admin/users', title: t('route.admin.user.search'), isRoot: true, icon: _jsx(PersonSearch, {}) },
28
31
  {
29
32
  path: '/admin/users/:id',
@@ -1,9 +1,16 @@
1
1
  const DEFAULT_THEME = {
2
+ components: {
3
+ MuiChip: {
4
+ defaultProps: {
5
+ size: 'small'
6
+ }
7
+ }
8
+ },
2
9
  palette: {
3
10
  dark: {
4
11
  background: {
5
- default: '#202020',
6
- paper: '#202020'
12
+ default: '#181818',
13
+ paper: '#181818'
7
14
  },
8
15
  primary: {
9
16
  main: '#7DA1DB'
@@ -1,8 +1,8 @@
1
1
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
2
  import type React from 'react';
3
- declare const useHitSelection: () => {
3
+ declare const useRecordSelection: () => {
4
4
  lastSelected: string;
5
5
  setLastSelected: React.Dispatch<React.SetStateAction<string>>;
6
6
  onClick: (e: React.MouseEvent<HTMLDivElement>, hit: Hit) => void;
7
7
  };
8
- export default useHitSelection;
8
+ export default useRecordSelection;
@@ -1,20 +1,14 @@
1
- import { useAppBreadcrumbs } from '@cccsaurora/howler-ui/commons/components/app/hooks';
2
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
3
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
4
1
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
5
- import useMySitemap from '@cccsaurora/howler-ui/components/hooks/useMySitemap';
2
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
3
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
6
4
  import { useCallback, useState } from 'react';
7
- import { useNavigate } from 'react-router-dom';
8
5
  import { useContextSelector } from 'use-context-selector';
9
- const useHitSelection = () => {
10
- const navigate = useNavigate();
11
- const { setItems } = useAppBreadcrumbs();
12
- const { routes } = useMySitemap();
13
- const response = useContextSelector(HitSearchContext, ctx => ctx.response);
14
- const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
15
- const addHitToSelection = useContextSelector(HitContext, ctx => ctx.addHitToSelection);
16
- const removeHitFromSelection = useContextSelector(HitContext, ctx => ctx.removeHitFromSelection);
17
- const clearSelectedHits = useContextSelector(HitContext, ctx => ctx.clearSelectedHits);
6
+ const useRecordSelection = () => {
7
+ const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
8
+ const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
9
+ const addHitToSelection = useContextSelector(RecordContext, ctx => ctx.addRecordToSelection);
10
+ const removeHitFromSelection = useContextSelector(RecordContext, ctx => ctx.removeRecordFromSelection);
11
+ const clearSelectedHits = useContextSelector(RecordContext, ctx => ctx.clearSelectedRecords);
18
12
  const setSelected = useContextSelector(ParameterContext, ctx => ctx.setSelected);
19
13
  const [lastSelected, setLastSelected] = useState(null);
20
14
  const onClick = useCallback((e, hit) => {
@@ -47,32 +41,17 @@ const useHitSelection = () => {
47
41
  e.stopPropagation();
48
42
  return;
49
43
  }
50
- if (hit.howler.is_bundle) {
51
- const searchRoute = routes.find(_route => _route.path.startsWith(location.pathname.replace(/^(\/.*)\/.+/, '$1')));
52
- const newBreadcrumb = {
53
- ...searchRoute,
54
- path: location.pathname + location.search
55
- };
56
- setItems([{ route: newBreadcrumb, matcher: null }]);
57
- navigate(`/bundles/${hit.howler.id}?span=date.range.all&query=howler.id%3A*&offset=0`);
58
- clearSelectedHits(hit.howler.id);
59
- }
60
- else {
61
- clearSelectedHits(hit.howler.id);
62
- setSelected(hit.howler.id);
63
- }
44
+ clearSelectedHits(hit.howler.id);
45
+ setSelected(hit.howler.id);
64
46
  }, [
65
47
  addHitToSelection,
66
48
  clearSelectedHits,
67
49
  lastSelected,
68
- navigate,
69
50
  removeHitFromSelection,
70
- response,
71
- routes,
51
+ response?.items,
72
52
  selectedHits,
73
- setItems,
74
53
  setSelected
75
54
  ]);
76
55
  return { lastSelected, setLastSelected, onClick };
77
56
  };
78
- export default useHitSelection;
57
+ export default useRecordSelection;
@@ -0,0 +1,13 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
+ import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
4
+ import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
5
+ type MixedRecords = Hit | Observable | Case;
6
+ /**
7
+ * Fetches records matching the provided IDs from the hit, observable, and case indexes.
8
+ *
9
+ * @param ids - List of howler.id / case_id values to look up.
10
+ * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
11
+ */
12
+ declare const useRelatedRecords: <T = MixedRecords>(ids: string[], enabled?: boolean) => WithMetadata<T>[];
13
+ export default useRelatedRecords;
@@ -0,0 +1,32 @@
1
+ import api from '@cccsaurora/howler-ui/api';
2
+ import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
+ import { useEffect, useState } from 'react';
4
+ /**
5
+ * Fetches records matching the provided IDs from the hit, observable, and case indexes.
6
+ *
7
+ * @param ids - List of howler.id / case_id values to look up.
8
+ * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
9
+ */
10
+ const useRelatedRecords = (ids, enabled = true) => {
11
+ const { dispatchApi } = useMyApi();
12
+ const [records, setRecords] = useState([]);
13
+ useEffect(() => {
14
+ if (!enabled || ids.length === 0) {
15
+ if (records.length > 0) {
16
+ setRecords([]);
17
+ }
18
+ return;
19
+ }
20
+ (async () => {
21
+ const joined = ids.join(' OR ');
22
+ const result = await dispatchApi(api.v2.search.post('hit,observable,case', {
23
+ query: `howler.id:(${joined}) OR case_id:(${joined})`
24
+ }), { throwError: false, showError: true });
25
+ if (result) {
26
+ setRecords(result.items);
27
+ }
28
+ })();
29
+ }, [dispatchApi, enabled, ids, records.length]);
30
+ return records;
31
+ };
32
+ export default useRelatedRecords;
@@ -0,0 +1,3 @@
1
+ import type { FC } from 'react';
2
+ declare const PermissionDeniedPage: FC;
3
+ export default PermissionDeniedPage;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PersonOff } from '@mui/icons-material';
3
+ import { Box, Typography } from '@mui/material';
4
+ import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
5
+ import { useTranslation } from 'react-i18next';
6
+ const PermissionDeniedPage = () => {
7
+ const { t } = useTranslation();
8
+ return (_jsxs(PageCenter, { width: "75%", children: [_jsx(Box, { pt: 6, textAlign: "center", fontSize: 200, children: _jsx(PersonOff, { color: "secondary", fontSize: "inherit" }) }), _jsx(Box, { pb: 2, children: _jsx(Typography, { variant: "h2", children: t('page.403.title') }) }), _jsx(Box, { children: _jsx(Typography, { variant: "h5", children: t('page.403.description') }) })] }));
9
+ };
10
+ export default PermissionDeniedPage;
@@ -7,7 +7,7 @@ import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCente
7
7
  import { FieldContext } from '@cccsaurora/howler-ui/components/app/providers/FieldProvider';
8
8
  import SocketBadge from '@cccsaurora/howler-ui/components/elements/display/icons/SocketBadge';
9
9
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
10
- import HitQuery from '@cccsaurora/howler-ui/components/routes/hits/search/HitQuery';
10
+ import RecordQuery from '@cccsaurora/howler-ui/components/routes/hits/search/RecordQuery';
11
11
  import { difference, uniq } from 'lodash-es';
12
12
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
13
13
  import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
@@ -54,7 +54,7 @@ const ActionEditor = () => {
54
54
  }, [triggers]);
55
55
  useEffect(() => {
56
56
  dispatchApi(api.action.operations.get())
57
- .then(_operations => _operations.filter(a => difference(a.roles, user.roles).length < 1))
57
+ .then(_operations => _operations.filter(a => difference(a.roles, user.roles).length < a.roles.length))
58
58
  .then(setOperations);
59
59
  if (responseQuery) {
60
60
  onSearch(responseQuery);
@@ -102,7 +102,7 @@ const ActionEditor = () => {
102
102
  : disabled && userOperations.length > 0
103
103
  ? t('route.actions.trigger.disabled.explanation')
104
104
  : null, children: component }, trigger));
105
- }) }) }), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "end", sx: { mb: -1 }, children: [_jsx(Typography, { sx: theme => ({ color: theme.palette.text.disabled, fontStyle: 'italic', mb: 0.5 }), variant: "body2", children: t('hit.search.prompt') }), _jsx(SocketBadge, { size: "small" })] }), _jsx(HitQuery, { triggerSearch: onSearch }), response ? (_jsx(QueryResultText, { count: response.total, query: responseQuery })) : (_jsx(Typography, { sx: theme => ({
105
+ }) }) }), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "end", sx: { mb: -1 }, children: [_jsx(Typography, { sx: theme => ({ color: theme.palette.text.disabled, fontStyle: 'italic', mb: 0.5 }), variant: "body2", children: t('hit.search.prompt') }), _jsx(SocketBadge, { size: "small" })] }), _jsx(RecordQuery, { triggerSearch: onSearch }), response ? (_jsx(QueryResultText, { count: response.total, query: responseQuery })) : (_jsx(Typography, { sx: theme => ({
106
106
  color: theme.palette.text.secondary,
107
107
  fontSize: '0.9em',
108
108
  fontStyle: 'italic',
@@ -142,7 +142,10 @@ const useMyActionFunctions = () => {
142
142
  showErrorMessage(_jsx(Trans, { i18nKey: "actions.error", values: { action: actionName, messages: errors.map(error => error.message).join(', ') } }));
143
143
  }
144
144
  if (skipped.length > 0) {
145
- showInfoMessage(_jsx(Trans, { i18nKey: "actions.skipped", values: { action: actionName, messages: skipped.map(skippedAction => skippedAction.message).join(', ') } }));
145
+ showInfoMessage(_jsx(Trans, { i18nKey: "actions.skipped", values: {
146
+ action: actionName,
147
+ messages: skipped.map(skippedAction => skippedAction.message).join(', ')
148
+ } }));
146
149
  }
147
150
  if (succeeded.length > 0) {
148
151
  showSuccessMessage(_jsx(Trans, { i18nKey: "actions.succeeded", values: { action: actionName } }));
@@ -58,7 +58,12 @@ const ActionDetails = () => {
58
58
  }
59
59
  // eslint-disable-next-line react-hooks/exhaustive-deps
60
60
  }, [action?.query]);
61
- return (_jsx(PageCenter, { maxWidth: "1500px", textAlign: "left", height: "100%", children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { variant: "h5", children: action?.name }), action?.owner_id && _jsx(HowlerAvatar, { sx: { width: 32, height: 32 }, userId: action.owner_id })] }), _jsx(Phrase, { fullWidth: true, value: action?.query, disabled: true, size: "small", onChange: () => { }, startAdornment: _jsx(IconButton, { onClick: () => onSearch(action?.query), children: _jsx(Search, { fontSize: "small" }) }) }), _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [response && _jsx(QueryResultText, { count: response.total, query: action?.query }), _jsx(FlexOne, {}), (action?.owner_id === user.username || user.roles?.includes('admin')) && (_jsx(Button, { startIcon: _jsx(Delete, {}), size: "small", variant: "outlined", color: "error", onClick: () => deleteAction(action?.action_id), children: t('delete') })), _jsx(Button, { startIcon: _jsx(PlayCircleOutline, {}), size: "small", variant: "outlined", color: "success", onClick: () => executeAction(action?.action_id), children: t('route.actions.execute') }), (action?.owner_id === user.username || user.roles?.includes('admin')) && (_jsx(Button, { startIcon: _jsx(Edit, {}), size: "small", variant: "outlined", component: Link, to: `/action/${params.id}/edit`, children: t('route.actions.edit') }))] }), user.roles.includes('automation_advanced') && (_jsx(FormGroup, { children: _jsx(Stack, { direction: "row", spacing: 1, children: action?.operations
61
+ const editRoles = user.roles.includes('automation_basic') || user.roles.includes('automation_advanced');
62
+ const execRoles = editRoles ||
63
+ user.roles.includes('admin') ||
64
+ user.roles.includes('actionrunner_basic') ||
65
+ user.roles.includes('actionrunner_advanced');
66
+ return (_jsx(PageCenter, { maxWidth: "1500px", textAlign: "left", height: "100%", children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { variant: "h5", children: action?.name }), action?.owner_id && _jsx(HowlerAvatar, { sx: { width: 32, height: 32 }, userId: action.owner_id })] }), _jsx(Phrase, { fullWidth: true, value: action?.query, disabled: true, size: "small", onChange: () => { }, startAdornment: _jsx(IconButton, { onClick: () => onSearch(action?.query), children: _jsx(Search, { fontSize: "small" }) }) }), _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [response && _jsx(QueryResultText, { count: response.total, query: action?.query }), _jsx(FlexOne, {}), ((action?.owner_id === user.username && editRoles) || user.roles?.includes('admin')) && (_jsx(Button, { startIcon: _jsx(Delete, {}), size: "small", variant: "outlined", color: "error", onClick: () => deleteAction(action?.action_id), children: t('delete') })), execRoles && (_jsx(Button, { startIcon: _jsx(PlayCircleOutline, {}), size: "small", variant: "outlined", color: "success", onClick: () => executeAction(action?.action_id), children: t('route.actions.execute') })), ((action?.owner_id === user.username && editRoles) || user.roles?.includes('admin')) && (_jsx(Button, { startIcon: _jsx(Edit, {}), size: "small", variant: "outlined", component: Link, to: `/action/${params.id}/edit`, children: t('route.actions.edit') }))] }), user.roles.includes('automation_advanced') && (_jsx(FormGroup, { children: _jsx(Stack, { direction: "row", spacing: 1, children: action?.operations
62
67
  ?.map(a => (operations ?? []).find(_action => _action.id === a.operation_id)?.triggers ?? [])
63
68
  .reduce((acc, triggers) => acc.filter(_t => triggers.includes(_t)))
64
69
  .map(trigger => (_jsx(FormControlLabel, { control: _jsx(Checkbox, { name: trigger, onChange: onTriggerChange, checked: action?.triggers?.includes(trigger) ?? false }), label: t(`route.actions.trigger.${trigger}`) }, trigger))) }) })), loading &&
@@ -97,6 +97,7 @@ const ActionSearch = () => {
97
97
  onSearch();
98
98
  // eslint-disable-next-line react-hooks/exhaustive-deps
99
99
  }, [searchModifiers]);
100
+ const editRoles = user.roles.includes('automation_basic') || user.roles.includes('automation_advanced');
100
101
  // Search result list item renderer.
101
102
  const renderer = useCallback(({ item }, classRenderer) => {
102
103
  return (_jsxs(Card, { onClick: () => navigate(`/action/${item.item.action_id}`), variant: "outlined", className: classRenderer(), sx: {
@@ -104,14 +105,14 @@ const ActionSearch = () => {
104
105
  transitionProperty: 'border-color',
105
106
  cursor: 'pointer',
106
107
  mt: 1
107
- }, children: [_jsx(CardHeader, { title: _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h5", children: item.item.name }), item.item.triggers.length > 0 && (_jsx(Tooltip, { title: _jsx(Trans, { i18nKey: "route.actions.trigger.description", values: { triggers: item.item.triggers.join(', ') }, components: { bold: _jsx("strong", {}) } }), children: _jsx(Engineering, {}) })), _jsx(FlexOne, {}), (item.item.owner_id === user.username || user.roles?.includes('admin')) && (_jsx(IconButton, { size: "small", onClick: async (e) => {
108
+ }, children: [_jsx(CardHeader, { title: _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h5", children: item.item.name }), item.item.triggers.length > 0 && (_jsx(Tooltip, { title: _jsx(Trans, { i18nKey: "route.actions.trigger.description", values: { triggers: item.item.triggers.join(', ') }, components: { bold: _jsx("strong", {}) } }), children: _jsx(Engineering, {}) })), _jsx(FlexOne, {}), ((item.item.owner_id === user.username && editRoles) || user.roles?.includes('admin')) && (_jsx(IconButton, { size: "small", onClick: async (e) => {
108
109
  e.preventDefault();
109
110
  e.stopPropagation();
110
111
  await deleteAction(item.item.action_id);
111
112
  onSearch();
112
- }, children: _jsx(Delete, {}) })), _jsx(HowlerAvatar, { sx: { width: 24, height: 24, marginRight: '8px !important' }, userId: item.item.owner_id })] }), subheader: item.item.query }), _jsx(CardContent, { sx: { paddingTop: 0 }, children: _jsx(Grid, { container: true, spacing: 1, children: item.item.operations.map(d => (_jsx(Grid, { item: true, children: _jsx(Chip, { size: "small", label: t(`operations.${d.operation_id}`) }) }, d.operation_id))) }) })] }, item.item.name));
113
- }, [deleteAction, navigate, onSearch, t, user.roles, user.username]);
114
- return (_jsx(ItemManager, { onSearch: onSearch, onCreate: () => navigate('/action/execute'), onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.actions.search.prompt') }), searchFilters: _jsx(Autocomplete, { multiple: true, size: "small", value: searchModifiers, onChange: (__, values) => setSearchModifiers(values), getOptionLabel: trigger => t(`route.actions.trigger.${trigger}`), options: VALID_ACTION_TRIGGERS, renderInput: params => (_jsx(TextField, { ...params, sx: { maxWidth: '500px' }, label: t('route.actions.trigger') })) }), renderer: renderer, response: response, createPrompt: "route.actions.create", searchPrompt: "route.actions.search", createIcon: _jsx(Terminal, { sx: { mr: 1 } }) }));
113
+ }, children: _jsx(Delete, {}) })), _jsx(HowlerAvatar, { sx: { width: 24, height: 24, marginRight: '8px !important' }, userId: item.item.owner_id })] }), subheader: item.item.query }), _jsx(CardContent, { sx: { paddingTop: 0 }, children: _jsx(Grid, { container: true, spacing: 1, children: item.item.operations.map(d => (_jsx(Grid, { item: true, children: _jsx(Chip, { label: t(`operations.${d.operation_id}`) }) }, d.operation_id))) }) })] }, item.item.name));
114
+ }, [deleteAction, editRoles, navigate, onSearch, t, user.roles, user.username]);
115
+ return (_jsx(ItemManager, { onSearch: onSearch, onCreate: editRoles ? () => navigate('/action/execute') : undefined, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.actions.search.prompt') }), searchFilters: _jsx(Autocomplete, { multiple: true, size: "small", value: searchModifiers, onChange: (__, values) => setSearchModifiers(values), getOptionLabel: trigger => t(`route.actions.trigger.${trigger}`), options: VALID_ACTION_TRIGGERS, renderInput: params => (_jsx(TextField, { ...params, sx: { maxWidth: '500px' }, label: t('route.actions.trigger') })) }), renderer: renderer, response: response, createPrompt: "route.actions.create", searchPrompt: "route.actions.search", createIcon: _jsx(Terminal, { sx: { mr: 1 } }) }));
115
116
  };
116
117
  const ActionSearchProvider = () => {
117
118
  return (_jsx(TuiListProvider, { children: _jsx(ActionSearch, {}) }));
@@ -1 +1 @@
1
- export default "# Integrations and Plugins\n\n> **Note:** This page is fallback documentation. In `Integrations.tsx`, when plugins provide integration views, those plugin tabs/content are rendered and this markdown is replaced.\n\nHowler plugins let you extend both UI behavior and rendering paths without modifying core screens directly. Plugins are installed through the plugin store and then invoked across the app through `executeFunction(...)` hooks.\n\n## How the plugin system works\n\n- `HowlerPlugin` is the base class that defines extension points.\n- `howlerPluginStore` keeps global plugin state (installed plugins, lead formats, pivot formats, operations, routes, menus, sitemap entries).\n- On activation, each plugin can register named functions in the runtime plugin store.\n- The app calls those functions via `pluginStore.executeFunction(...)` in specific locations.\n\nIn practice, this means plugins can contribute features incrementally rather than replacing full pages.\n\n## What plugins can add\n\nFrom `HowlerPlugin.ts` and store usage, plugins can provide:\n\n- **Lead formats** (`addLead`) with:\n\t- a lead editor form (`lead.<format>.form`)\n\t- a lead renderer (`lead.<format>`)\n- **Pivot formats** (`addPivot`) with:\n\t- a pivot form (`pivot.<format>.form`)\n\t- a pivot link renderer (`pivot.<format>`)\n- **Custom action operations** (`addOperation`) with:\n\t- operation editor UI (`operation.<id>`)\n\t- operation help docs (`operation.<id>.documentation`)\n- **Menu entries**:\n\t- user menu items\n\t- admin menu items\n\t- main menu insertions/dividers\n- **Routing/navigation**:\n\t- routes\n\t- sitemap entries and breadcrumbs behavior\n- **Global extension hooks**:\n\t- `provider()` wrapper for app-wide context\n\t- `setup()` startup logic\n\t- `localization(...)` translation bundles\n\t- `helpers()` custom handlebars helpers\n\t- `typography(...)` and `chip(...)` custom UI rendering\n\t- `actions(...)` hit actions\n\t- `status(...)` hit banner/status widgets\n\t- `support()`, `help()`, and section-specific `settings(...)`\n\t- `documentation(md)` markdown post-processing\n\t- `on(event, hit)` event callback\n\n## Where hooks are executed\n\n`executeFunction(...)` is used throughout the app to render plugin output at runtime, for example:\n\n- lead rendering and lead form editors\n- pivot rendering and pivot form editors\n- custom operation editors and docs\n- plugin actions in hit views/context menus\n- hit status/banner components\n- typography/chip wrappers\n- plugin providers and startup setup\n- settings sections (`admin`, `local`, `profile`, `security`)\n- help/support panels\n- markdown documentation transforms\n\n## Clue plugin example\n\nThe Clue plugin (`ui/src/plugins/clue/index.tsx`) demonstrates a typical plugin:\n\n- registers localization bundles in English/French\n- provides a plugin provider + setup hook\n- adds a custom lead format (`clue`) with:\n\t- a lead form component\n\t- a renderer that parses lead metadata and renders a `Fetcher`\n- adds a custom pivot format (`clue`) with form + renderer\n- provides custom handlebars helpers\n- overrides plugin typography/chip renderers\n\nThis is the main pattern to follow when adding a new integration.\n"
1
+ export default "# Integrations and Plugins\n\n> **Note:** This page is fallback documentation. In `Integrations.tsx`, when plugins provide integration views, those plugin tabs/content are rendered and this markdown is replaced.\n\nHowler plugins let you extend both UI behavior and rendering paths without modifying core screens directly. Plugins are installed through the plugin store and then invoked across the app through `executeFunction(...)` hooks.\n\n## How the plugin system works\n\n- `HowlerPlugin` is the base class that defines extension points.\n- `howlerPluginStore` keeps global plugin state (installed plugins, lead formats, pivot formats, operations, routes, menus, sitemap entries).\n- On activation, each plugin can register named functions in the runtime plugin store.\n- The app calls those functions via `pluginStore.executeFunction(...)` in specific locations.\n\nIn practice, this means plugins can contribute features incrementally rather than replacing full pages.\n\n## What plugins can add\n\nFrom `HowlerPlugin.ts` and store usage, plugins can provide:\n\n- **Lead formats** (`addLead`) with:\n - a lead editor form (`lead.<format>.form`)\n - a lead renderer (`lead.<format>`)\n- **Pivot formats** (`addPivot`) with:\n - a pivot form (`pivot.<format>.form`)\n - a pivot link renderer (`pivot.<format>`)\n- **Custom action operations** (`addOperation`) with:\n - operation editor UI (`operation.<id>`)\n - operation help docs (`operation.<id>.documentation`)\n- **Menu entries**:\n - user menu items\n - admin menu items\n - main menu insertions/dividers\n- **Routing/navigation**:\n - routes\n - sitemap entries and breadcrumbs behavior\n- **Global extension hooks**:\n - `provider()` wrapper for app-wide context\n - `setup()` startup logic\n - `localization(...)` translation bundles\n - `helpers()` custom handlebars helpers\n - `typography(...)` and `chip(...)` custom UI rendering\n - `actions(...)` hit actions\n - `status(...)` hit banner/status widgets\n - `support()`, `help()`, and section-specific `settings(...)`\n - `documentation(md)` markdown post-processing\n - `on(event, hit)` event callback\n\n## Where hooks are executed\n\n`executeFunction(...)` is used throughout the app to render plugin output at runtime, for example:\n\n- lead rendering and lead form editors\n- pivot rendering and pivot form editors\n- custom operation editors and docs\n- plugin actions in hit views/context menus\n- hit status/banner components\n- typography/chip wrappers\n- plugin providers and startup setup\n- settings sections (`admin`, `local`, `profile`, `security`)\n- help/support panels\n- markdown documentation transforms\n\n## Clue plugin example\n\nThe Clue plugin (`ui/src/plugins/clue/index.tsx`) demonstrates a typical plugin:\n\n- registers localization bundles in English/French\n- provides a plugin provider + setup hook\n- adds a custom lead format (`clue`) with:\n - a lead form component\n - a renderer that parses lead metadata and renders a `Fetcher`\n- adds a custom pivot format (`clue`) with form + renderer\n- provides custom handlebars helpers\n- overrides plugin typography/chip renderers\n\nThis is the main pattern to follow when adding a new integration.\n"
@@ -1 +1 @@
1
- export default "# Int\u00e9grations et plugins\n\n> **Remarque :** cette page est une documentation de secours. Dans `Integrations.tsx`, quand des plugins fournissent des vues d\u2019int\u00e9gration, ces onglets/contenus plugins sont affich\u00e9s et ce markdown est remplac\u00e9.\n\nLes plugins Howler permettent d\u2019\u00e9tendre le comportement de l\u2019interface et le rendu sans modifier directement les \u00e9crans principaux. Les plugins sont install\u00e9s via le magasin de plugins, puis appel\u00e9s dans l\u2019application \u00e0 l\u2019aide des points d\u2019extension `executeFunction(...)`.\n\n## Fonctionnement du syst\u00e8me de plugins\n\n- `HowlerPlugin` est la classe de base qui d\u00e9finit les points d\u2019extension.\n- `howlerPluginStore` conserve l\u2019\u00e9tat global (plugins install\u00e9s, formats de lead, formats de pivot, op\u00e9rations, routes, menus, sitemap).\n- \u00c0 l\u2019activation, un plugin enregistre des fonctions nomm\u00e9es dans le magasin de plugins.\n- L\u2019application ex\u00e9cute ensuite ces fonctions via `pluginStore.executeFunction(...)` \u00e0 des endroits pr\u00e9cis.\n\nCe m\u00e9canisme permet d\u2019ajouter des capacit\u00e9s de fa\u00e7on incr\u00e9mentale, sans remplacer des pages compl\u00e8tes.\n\n## Ce qu\u2019un plugin peut ajouter\n\nD\u2019apr\u00e8s `HowlerPlugin.ts` et les usages du store, un plugin peut fournir :\n\n- **Formats de lead** (`addLead`) avec :\n\t- un formulaire d\u2019\u00e9dition (`lead.<format>.form`)\n\t- un rendu (`lead.<format>`)\n- **Formats de pivot** (`addPivot`) avec :\n\t- un formulaire (`pivot.<format>.form`)\n\t- un rendu de lien pivot (`pivot.<format>`)\n- **Op\u00e9rations d\u2019action personnalis\u00e9es** (`addOperation`) avec :\n\t- l\u2019UI d\u2019\u00e9dition de l\u2019op\u00e9ration (`operation.<id>`)\n\t- la documentation de l\u2019op\u00e9ration (`operation.<id>.documentation`)\n- **Entr\u00e9es de menu** :\n\t- menu utilisateur\n\t- menu administrateur\n\t- insertions/s\u00e9parateurs dans le menu principal\n- **Routage/navigation** :\n\t- routes\n\t- entr\u00e9es de sitemap et logique de fil d\u2019Ariane\n- **Points d\u2019extension globaux** :\n\t- `provider()` pour injecter un contexte global\n\t- `setup()` au d\u00e9marrage\n\t- `localization(...)` pour les traductions\n\t- `helpers()` pour les helpers handlebars\n\t- `typography(...)` et `chip(...)` pour le rendu UI\n\t- `actions(...)` pour les actions sur les hits\n\t- `status(...)` pour la banni\u00e8re/statut d\u2019un hit\n\t- `support()`, `help()` et `settings(...)` par section\n\t- `documentation(md)` pour post-traiter du markdown\n\t- `on(event, hit)` pour les \u00e9v\u00e9nements\n\n## O\u00f9 les hooks sont ex\u00e9cut\u00e9s\n\n`executeFunction(...)` est utilis\u00e9 dans plusieurs parties de l\u2019UI, notamment pour :\n\n- le rendu des leads et leurs formulaires\n- le rendu des pivots et leurs formulaires\n- les \u00e9diteurs d\u2019op\u00e9rations et leur documentation\n- les actions plugin dans les vues/context menus de hit\n- les composants de statut/banni\u00e8re des hits\n- les composants typographie/chip\n- les providers plugins et la logique `setup`\n- les sections de param\u00e8tres (`admin`, `local`, `profile`, `security`)\n- les vues d\u2019aide/support\n- la transformation de markdown de documentation\n\n## Exemple : plugin Clue\n\nLe plugin Clue (`ui/src/plugins/clue/index.tsx`) montre un exemple concret :\n\n- enregistre des bundles de traduction EN/FR\n- expose un provider et un hook de setup\n- ajoute un format de lead `clue` avec :\n\t- un composant de formulaire\n\t- un renderer qui lit les m\u00e9tadonn\u00e9es du lead et affiche un `Fetcher`\n- ajoute un format de pivot `clue` (formulaire + rendu)\n- fournit des helpers handlebars personnalis\u00e9s\n- fournit des rendus personnalis\u00e9s pour `typography` et `chip`\n\nC\u2019est le mod\u00e8le recommand\u00e9 pour d\u00e9velopper de nouvelles int\u00e9grations.\n"
1
+ export default "# Int\u00e9grations et plugins\n\n> **Remarque :** cette page est une documentation de secours. Dans `Integrations.tsx`, quand des plugins fournissent des vues d\u2019int\u00e9gration, ces onglets/contenus plugins sont affich\u00e9s et ce markdown est remplac\u00e9.\n\nLes plugins Howler permettent d\u2019\u00e9tendre le comportement de l\u2019interface et le rendu sans modifier directement les \u00e9crans principaux. Les plugins sont install\u00e9s via le magasin de plugins, puis appel\u00e9s dans l\u2019application \u00e0 l\u2019aide des points d\u2019extension `executeFunction(...)`.\n\n## Fonctionnement du syst\u00e8me de plugins\n\n- `HowlerPlugin` est la classe de base qui d\u00e9finit les points d\u2019extension.\n- `howlerPluginStore` conserve l\u2019\u00e9tat global (plugins install\u00e9s, formats de lead, formats de pivot, op\u00e9rations, routes, menus, sitemap).\n- \u00c0 l\u2019activation, un plugin enregistre des fonctions nomm\u00e9es dans le magasin de plugins.\n- L\u2019application ex\u00e9cute ensuite ces fonctions via `pluginStore.executeFunction(...)` \u00e0 des endroits pr\u00e9cis.\n\nCe m\u00e9canisme permet d\u2019ajouter des capacit\u00e9s de fa\u00e7on incr\u00e9mentale, sans remplacer des pages compl\u00e8tes.\n\n## Ce qu\u2019un plugin peut ajouter\n\nD\u2019apr\u00e8s `HowlerPlugin.ts` et les usages du store, un plugin peut fournir :\n\n- **Formats de lead** (`addLead`) avec :\n - un formulaire d\u2019\u00e9dition (`lead.<format>.form`)\n - un rendu (`lead.<format>`)\n- **Formats de pivot** (`addPivot`) avec :\n - un formulaire (`pivot.<format>.form`)\n - un rendu de lien pivot (`pivot.<format>`)\n- **Op\u00e9rations d\u2019action personnalis\u00e9es** (`addOperation`) avec :\n - l\u2019UI d\u2019\u00e9dition de l\u2019op\u00e9ration (`operation.<id>`)\n - la documentation de l\u2019op\u00e9ration (`operation.<id>.documentation`)\n- **Entr\u00e9es de menu** :\n - menu utilisateur\n - menu administrateur\n - insertions/s\u00e9parateurs dans le menu principal\n- **Routage/navigation** :\n - routes\n - entr\u00e9es de sitemap et logique de fil d\u2019Ariane\n- **Points d\u2019extension globaux** :\n - `provider()` pour injecter un contexte global\n - `setup()` au d\u00e9marrage\n - `localization(...)` pour les traductions\n - `helpers()` pour les helpers handlebars\n - `typography(...)` et `chip(...)` pour le rendu UI\n - `actions(...)` pour les actions sur les hits\n - `status(...)` pour la banni\u00e8re/statut d\u2019un hit\n - `support()`, `help()` et `settings(...)` par section\n - `documentation(md)` pour post-traiter du markdown\n - `on(event, hit)` pour les \u00e9v\u00e9nements\n\n## O\u00f9 les hooks sont ex\u00e9cut\u00e9s\n\n`executeFunction(...)` est utilis\u00e9 dans plusieurs parties de l\u2019UI, notamment pour :\n\n- le rendu des leads et leurs formulaires\n- le rendu des pivots et leurs formulaires\n- les \u00e9diteurs d\u2019op\u00e9rations et leur documentation\n- les actions plugin dans les vues/context menus de hit\n- les composants de statut/banni\u00e8re des hits\n- les composants typographie/chip\n- les providers plugins et la logique `setup`\n- les sections de param\u00e8tres (`admin`, `local`, `profile`, `security`)\n- les vues d\u2019aide/support\n- la transformation de markdown de documentation\n\n## Exemple : plugin Clue\n\nLe plugin Clue (`ui/src/plugins/clue/index.tsx`) montre un exemple concret :\n\n- enregistre des bundles de traduction EN/FR\n- expose un provider et un hook de setup\n- ajoute un format de lead `clue` avec :\n - un composant de formulaire\n - un renderer qui lit les m\u00e9tadonn\u00e9es du lead et affiche un `Fetcher`\n- ajoute un format de pivot `clue` (formulaire + rendu)\n- fournit des helpers handlebars personnalis\u00e9s\n- fournit des rendus personnalis\u00e9s pour `typography` et `chip`\n\nC\u2019est le mod\u00e8le recommand\u00e9 pour d\u00e9velopper de nouvelles int\u00e9grations.\n"
@@ -233,7 +233,7 @@ const QueryBuilder = () => {
233
233
  height: '100%',
234
234
  '& .MuiFormControl-root': { height: '100%', '& > div': { height: '100%' } }
235
235
  }
236
- ], slotProps: { paper: { sx: { minWidth: '600px' } } } }), _jsx(Card, { variant: "outlined", sx: { flex: 1, maxWidth: '350px', minWidth: '210px' }, children: _jsxs(Stack, { spacing: 0.5, sx: { px: 1, alignItems: 'start' }, children: [_jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { whiteSpace: 'nowrap' }, children: [t('route.advanced.rows'), ": ", STEPS[rows]] }), _jsx(Slider, { size: "small", valueLabelDisplay: "off", value: rows, onChange: (_, value) => setRows(value), min: 0, max: 9, step: 1, marks: true, track: false, sx: { py: 0.5 } })] }) })] }), type === 'lucene' && (_jsx(Autocomplete, { size: "small", getOptionLabel: opt => t(`route.advanced.query.type.${opt}`), options: LUCENE_QUERY_OPTIONS, value: queryType, onChange: (_event, value) => setQueryType(value), renderInput: params => (_jsx(TextField, { ...params, label: t('route.advanced.query.lucene.type'), sx: { minWidth: '230px' } })), renderOption: (props, option) => (_jsx(ListItemText, { ...props, sx: { flexDirection: 'column', alignItems: 'start !important' }, primary: t(`route.advanced.query.type.${option}`), secondary: t(`route.advanced.query.type.${option}.description`) })) })), queryType === 'groupby' && (_jsx(Autocomplete, { size: "small", options: fieldOptions, value: groupByField, onChange: (__, value) => setGroupByField(value), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.pivot.field') }), sx: { minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), allFields && queryType !== 'facet' ? (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: allFields, onChange: (__, checked) => setAllFields(checked) }), label: t('route.advanced.fields.all'), sx: { '& > span': { color: 'text.secondary' }, alignSelf: 'start' } })) : (_jsx(Autocomplete, { fullWidth: true, renderTags: values => values.length <= 3 ? (_jsx(Stack, { direction: "row", spacing: 0.5, children: values.map(_value => (_jsx(Chip, { size: "small", label: _value }, _value))) })) : (_jsx(Tooltip, { title: _jsx(Stack, { spacing: 1, children: values.map(_value => (_jsx("span", { children: _value }, _value))) }), children: _jsx(Chip, { size: "small", label: values.length }) })), multiple: true, size: "small", options: fieldOptions, value: fields, onChange: (__, values) => (values.length > 0 ? setFields(values) : setAllFields(true)), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.fields') }), sx: { maxWidth: '500px', width: '20vw', minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), _jsx(FlexOne, {}), type === 'lucene' &&
236
+ ], slotProps: { paper: { sx: { minWidth: '600px' } } } }), _jsx(Card, { variant: "outlined", sx: { flex: 1, maxWidth: '350px', minWidth: '210px' }, children: _jsxs(Stack, { spacing: 0.5, sx: { px: 1, alignItems: 'start' }, children: [_jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { whiteSpace: 'nowrap' }, children: [t('route.advanced.rows'), ": ", STEPS[rows]] }), _jsx(Slider, { size: "small", valueLabelDisplay: "off", value: rows, onChange: (_, value) => setRows(value), min: 0, max: 9, step: 1, marks: true, track: false, sx: { py: 0.5 } })] }) })] }), type === 'lucene' && (_jsx(Autocomplete, { size: "small", getOptionLabel: opt => t(`route.advanced.query.type.${opt}`), options: LUCENE_QUERY_OPTIONS, value: queryType, onChange: (_event, value) => setQueryType(value), renderInput: params => (_jsx(TextField, { ...params, label: t('route.advanced.query.lucene.type'), sx: { minWidth: '230px' } })), renderOption: (props, option) => (_jsx(ListItemText, { ...props, sx: { flexDirection: 'column', alignItems: 'start !important' }, primary: t(`route.advanced.query.type.${option}`), secondary: t(`route.advanced.query.type.${option}.description`) })) })), queryType === 'groupby' && (_jsx(Autocomplete, { size: "small", options: fieldOptions, value: groupByField, onChange: (__, value) => setGroupByField(value), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.pivot.field') }), sx: { minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), allFields && queryType !== 'facet' ? (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: allFields, onChange: (__, checked) => setAllFields(checked) }), label: t('route.advanced.fields.all'), sx: { '& > span': { color: 'text.secondary' }, alignSelf: 'start' } })) : (_jsx(Autocomplete, { fullWidth: true, renderTags: values => values.length <= 3 ? (_jsx(Stack, { direction: "row", spacing: 0.5, children: values.map(_value => (_jsx(Chip, { label: _value }, _value))) })) : (_jsx(Tooltip, { title: _jsx(Stack, { spacing: 1, children: values.map(_value => (_jsx("span", { children: _value }, _value))) }), children: _jsx(Chip, { label: values.length }) })), multiple: true, size: "small", options: fieldOptions, value: fields, onChange: (__, values) => (values.length > 0 ? setFields(values) : setAllFields(true)), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.fields') }), sx: { maxWidth: '500px', width: '20vw', minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), _jsx(FlexOne, {}), type === 'lucene' &&
237
237
  (smallButtons ? (_jsx(Tooltip, { title: t('route.advanced.open'), children: _jsx(IconButton, { color: "primary", sx: { alignSelf: 'center' }, component: Link, disabled: !response, to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}`, children: _jsx(OpenInNew, { fontSize: "small" }) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", startIcon: _jsx(OpenInNew, {}), component: Link, disabled: !response, ...{ to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}` }, children: t('route.advanced.open') }))), smallButtons ? (_jsx(Tooltip, { title: response ? t('route.advanced.create.rule') : t('route.advanced.create.rule.disabled'), children: _jsx(IconButton, { size: "small", sx: { alignSelf: 'center' }, color: "info", onClick: onCreateRule, disabled: !response, children: _jsx(SsidChart, {}) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", color: "info", startIcon: _jsx(SsidChart, {}), onClick: onCreateRule, disabled: !response, ...{ to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}` }, tooltip: !response && t('route.advanced.create.rule.disabled'), children: t('route.advanced.create.rule') }))] }), _jsxs(Box, { width: "100%", height: "calc(100vh - 112px)", sx: { position: 'relative', overflow: 'hidden', borderTop: `thin solid ${theme.palette.divider}` }, children: [_jsx(Box, { sx: {
238
238
  position: 'absolute',
239
239
  top: 0,
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMonaco } from '@monaco-editor/react';
3
3
  import { Box, useTheme } from '@mui/material';
4
4
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
5
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
5
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
6
6
  import ThemedEditor from '@cccsaurora/howler-ui/components/elements/ThemedEditor';
7
7
  import { memo, useCallback, useContext, useEffect, useMemo } from 'react';
8
8
  import { useContextSelector } from 'use-context-selector';
@@ -20,8 +20,8 @@ const QueryEditor = ({ query, setQuery, onMount, language = 'lucene', fontSize =
20
20
  const yamlCompletion = useYamlCompletionProvider();
21
21
  const eqlCompletion = useEQLCompletionProvider();
22
22
  const historyCompletion = useHistoryCompletionProvider();
23
- const fzfSearch = useContextSelector(HitSearchContext, ctx => ctx?.fzfSearch ?? false);
24
- const setFzfSearch = useContextSelector(HitSearchContext, ctx => ctx?.setFzfSearch);
23
+ const fzfSearch = useContextSelector(RecordSearchContext, ctx => ctx?.fzfSearch ?? false);
24
+ const setFzfSearch = useContextSelector(RecordSearchContext, ctx => ctx?.setFzfSearch);
25
25
  const beforeEditorMount = useCallback((_monaco) => {
26
26
  _monaco.languages.register({ id: 'lucene' });
27
27
  _monaco.languages.register({ id: 'eql' });
@@ -1,13 +1,13 @@
1
1
  import { useMonaco } from '@monaco-editor/react';
2
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
2
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
3
3
  import Fuse from 'fuse.js';
4
4
  import { useMemo } from 'react';
5
5
  import { useContextSelector } from 'use-context-selector';
6
6
  import { twitterShort } from '@cccsaurora/howler-ui/utils/utils';
7
7
  const useHistoryCompletionProvider = () => {
8
8
  const monaco = useMonaco();
9
- const fzfSearch = useContextSelector(HitSearchContext, ctx => ctx?.fzfSearch);
10
- const queryHistory = useContextSelector(HitSearchContext, ctx => ctx?.queryHistory ?? {});
9
+ const fzfSearch = useContextSelector(RecordSearchContext, ctx => ctx?.fzfSearch);
10
+ const queryHistory = useContextSelector(RecordSearchContext, ctx => ctx?.queryHistory ?? {});
11
11
  // Using fuse for fuzzy searching
12
12
  const fuse = useMemo(() => new Fuse(Object.keys(queryHistory), { keys: ['key'], threshold: 0.4 }), [queryHistory]);
13
13
  return {
@@ -51,7 +51,7 @@ const AnalyticDetails = () => {
51
51
  _setFilter(detection);
52
52
  }
53
53
  }, [filter]);
54
- const onOwnerChange = useCallback(async (ownerId) => {
54
+ const onOwnerChange = useCallback(async ([ownerId]) => {
55
55
  const result = await dispatchApi(api.analytic.owner.post(analytic.analytic_id, { username: ownerId }), {
56
56
  throwError: true,
57
57
  showError: true
@@ -108,7 +108,7 @@ const AnalyticDetails = () => {
108
108
  marginTop: '0 !important',
109
109
  marginLeft: `${theme.spacing(-1)} !important`,
110
110
  marginRight: `${theme.spacing(-1)} !important`
111
- }, userId: analytic?.owner, onChange: onOwnerChange, i18nLabel: "route.analytics.set.owner" })) : (_jsx(HowlerAvatar, { userId: analytic?.owner })), _jsx(Stack, { children: users[analytic?.owner] ? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body1", children: users[analytic?.owner].name }), _jsx(Typography, { component: "a", href: `mailto:${users[analytic?.owner].email}`, variant: "caption", color: "text.secondary", children: users[analytic?.owner].email })] })) : (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "text", width: "70px" }), _jsx(Skeleton, { variant: "text", width: "60px" })] })) })] })] }), filteredContributors.length > 0 && (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('route.analytics.contributors') }), _jsx(Stack, { direction: "row", alignItems: "center", spacing: 1, children: filteredContributors.map(_user => (_jsx(HowlerAvatar, { userId: _user }, _user))) })] })), analytic?.rule_crontab && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { spacing: 1, justifyContent: "space-between", children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('rule.interval') }), editingInterval ? (_jsxs(FormControl, { sx: { minWidth: '200px' }, children: [_jsx(InputLabel, { children: t('rule.interval') }), _jsx(Select, { size: "small", label: t('rule.interval'), onChange: event => setCrontab(event.target.value), value: crontab, children: RULE_INTERVALS.map(interval => (_jsx(MenuItem, { value: interval.crontab, children: t(interval.key) }, interval.key))) })] })) : (_jsx("code", { style: {
111
+ }, userIds: [analytic?.owner], onChange: onOwnerChange, i18nLabel: "route.analytics.set.owner" })) : (_jsx(HowlerAvatar, { userId: analytic?.owner })), _jsx(Stack, { children: users[analytic?.owner] ? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body1", children: users[analytic?.owner].name }), _jsx(Typography, { component: "a", href: `mailto:${users[analytic?.owner].email}`, variant: "caption", color: "text.secondary", children: users[analytic?.owner].email })] })) : (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "text", width: "70px" }), _jsx(Skeleton, { variant: "text", width: "60px" })] })) })] })] }), filteredContributors.length > 0 && (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('route.analytics.contributors') }), _jsx(Stack, { direction: "row", alignItems: "center", spacing: 1, children: filteredContributors.map(_user => (_jsx(HowlerAvatar, { userId: _user }, _user))) })] })), analytic?.rule_crontab && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { spacing: 1, justifyContent: "space-between", children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('rule.interval') }), editingInterval ? (_jsxs(FormControl, { sx: { minWidth: '200px' }, children: [_jsx(InputLabel, { children: t('rule.interval') }), _jsx(Select, { size: "small", label: t('rule.interval'), onChange: event => setCrontab(event.target.value), value: crontab, children: RULE_INTERVALS.map(interval => (_jsx(MenuItem, { value: interval.crontab, children: t(interval.key) }, interval.key))) })] })) : (_jsx("code", { style: {
112
112
  backgroundColor: theme.palette.background.paper,
113
113
  padding: theme.spacing(0.5),
114
114
  alignSelf: 'start',
@@ -128,7 +128,7 @@ const AnalyticSearchBase = () => {
128
128
  padding: theme.spacing(0.5),
129
129
  borderRadius: theme.shape.borderRadius,
130
130
  border: `thin solid ${theme.palette.divider}`
131
- }, children: item.item.rule_type })] })), _jsx(FlexOne, {}), _jsxs(Stack, { direction: "row", spacing: 1, sx: { mt: 1 }, children: [item.item.owner && _jsx(HowlerAvatar, { sx: { width: 24, height: 24 }, userId: item.item.owner }), filteredContributors.length > 0 && _jsx(Divider, { orientation: "vertical", flexItem: true }), _jsx(AvatarGroup, { children: filteredContributors.map(contributor => (_jsx(HowlerAvatar, { sx: { width: 24, height: 24 }, userId: contributor }, contributor))) })] }), _jsx(Tooltip, { title: t('button.pin'), children: _jsx(IconButton, { size: "small", onClick: e => onFavourite(e, item.item), children: appUser.user?.favourite_analytics?.includes(item.item.analytic_id) ? _jsx(Star, {}) : _jsx(StarBorder, {}) }) })] }) }), item.item.detections?.length > 0 && (_jsx(CardContent, { sx: { paddingTop: 0 }, children: _jsxs(Grid, { container: true, spacing: 0.5, sx: { marginTop: `${theme.spacing(-0.5)} !important` }, children: [item.item.detections.slice(0, 5).map(d => (_jsx(Grid, { item: true, children: _jsx(Chip, { size: "small", variant: "outlined", label: d }) }, d))), item.item.detections.length > 5 && (_jsx(Grid, { item: true, children: _jsx(Tooltip, { title: _jsx(Stack, { children: item.item.detections.slice(5).map(d => (_jsx("span", { children: d }, d))) }), children: _jsx(Chip, { size: "small", variant: "outlined", label: `+ ${item.item.detections.length - 5}` }) }) }))] }) }))] }, item.item.name));
131
+ }, children: item.item.rule_type })] })), _jsx(FlexOne, {}), _jsxs(Stack, { direction: "row", spacing: 1, sx: { mt: 1 }, children: [item.item.owner && _jsx(HowlerAvatar, { sx: { width: 24, height: 24 }, userId: item.item.owner }), filteredContributors.length > 0 && _jsx(Divider, { orientation: "vertical", flexItem: true }), _jsx(AvatarGroup, { children: filteredContributors.map(contributor => (_jsx(HowlerAvatar, { sx: { width: 24, height: 24 }, userId: contributor }, contributor))) })] }), _jsx(Tooltip, { title: t('button.pin'), children: _jsx(IconButton, { size: "small", onClick: e => onFavourite(e, item.item), children: appUser.user?.favourite_analytics?.includes(item.item.analytic_id) ? _jsx(Star, {}) : _jsx(StarBorder, {}) }) })] }) }), item.item.detections?.length > 0 && (_jsx(CardContent, { sx: { paddingTop: 0 }, children: _jsxs(Grid, { container: true, spacing: 0.5, sx: { marginTop: `${theme.spacing(-0.5)} !important` }, children: [item.item.detections.slice(0, 5).map(d => (_jsx(Grid, { item: true, children: _jsx(Chip, { variant: "outlined", label: d }) }, d))), item.item.detections.length > 5 && (_jsx(Grid, { item: true, children: _jsx(Tooltip, { title: _jsx(Stack, { children: item.item.detections.slice(5).map(d => (_jsx("span", { children: d }, d))) }), children: _jsx(Chip, { variant: "outlined", label: `+ ${item.item.detections.length - 5}` }) }) }))] }) }))] }, item.item.name));
132
132
  }, [appUser.user?.favourite_analytics, navigate, onFavourite, t, theme]);
133
133
  return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, searchAdornment: _jsx(InputAdornment, { position: "end", children: _jsx(Tooltip, { title: t(`route.analytics.search.filter.rules.${onlyRules < 0 ? 'hide' : onlyRules > 0 ? 'show' : 'toggle'}`), children: _jsx(IconButton, { onClick: () => setOnlyRules((((onlyRules + 2) % 3) - 1)), children: _jsx(SsidChart, { color: onlyRules < 0 ? 'error' : onlyRules > 0 ? 'info' : 'inherit', sx: { transition: theme.transitions.create(['color']) } }) }) }) }), aboveSearch: _jsx(Typography, { sx: { fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }, variant: "body2", children: t('route.analytics.search.prompt') }), renderer: renderer, response: response, searchPrompt: "route.analytics.manager.search" }));
134
134
  };
@@ -0,0 +1,2 @@
1
+ declare const _default: import("react").NamedExoticComponent<{}>;
2
+ export default _default;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Stack } from '@mui/material';
3
+ import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
4
+ import { memo, useContext, useEffect } from 'react';
5
+ import { Outlet, useParams } from 'react-router-dom';
6
+ import NotFoundPage from '../404';
7
+ import ErrorBoundary from '../ErrorBoundary';
8
+ import CaseDetails from './detail/CaseDetails';
9
+ import CaseSidebar from './detail/CaseSidebar';
10
+ import useCase from './hooks/useCase';
11
+ const CaseViewer = () => {
12
+ const params = useParams();
13
+ const { case: _case, missing, update } = useCase({ caseId: params.id });
14
+ const { emit, open, fetchViewers } = useContext(SocketContext);
15
+ useEffect(() => {
16
+ if (!params.id) {
17
+ return;
18
+ }
19
+ fetchViewers(params.id);
20
+ if (open) {
21
+ emit({
22
+ broadcast: false,
23
+ action: 'viewing',
24
+ id: params.id
25
+ });
26
+ return () => {
27
+ emit({
28
+ broadcast: false,
29
+ action: 'stop_viewing',
30
+ id: params.id
31
+ });
32
+ };
33
+ }
34
+ }, [emit, params.id, open, fetchViewers]);
35
+ if (missing) {
36
+ return _jsx(NotFoundPage, {});
37
+ }
38
+ return (_jsx(ErrorBoundary, { children: _jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case, update: updatedCase => update(updatedCase, false) }), _jsx(Box, { sx: {
39
+ maxHeight: 'calc(100vh - 64px)',
40
+ flex: 1,
41
+ overflow: 'auto'
42
+ }, children: _jsx(ErrorBoundary, { children: _jsx(Outlet, { context: _case }) }) }), _jsx(CaseDetails, { case: _case })] }) }));
43
+ };
44
+ export default memo(CaseViewer);
@@ -0,0 +1 @@
1
+ export {};