@cccsaurora/howler-ui 2.19.0-dev.999 → 2.19.0

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 (653) hide show
  1. package/api/action/index.d.ts +5 -4
  2. package/api/action/index.js +8 -8
  3. package/api/analytic/index.d.ts +5 -5
  4. package/api/analytic/index.js +5 -6
  5. package/api/analytic/owner.d.ts +2 -1
  6. package/api/analytic/owner.js +2 -2
  7. package/api/dossier/index.d.ts +4 -3
  8. package/api/dossier/index.js +6 -6
  9. package/api/hit/index.d.ts +3 -2
  10. package/api/hit/index.js +4 -4
  11. package/api/hit/labels.d.ts +3 -2
  12. package/api/hit/labels.js +4 -4
  13. package/api/hit/overwrite.d.ts +2 -1
  14. package/api/hit/overwrite.js +2 -2
  15. package/api/hit/transition.d.ts +2 -1
  16. package/api/hit/transition.js +2 -2
  17. package/api/index.d.ts +10 -5
  18. package/api/index.js +18 -10
  19. package/api/overview/index.d.ts +4 -3
  20. package/api/overview/index.js +6 -6
  21. package/api/search/action.js +1 -1
  22. package/api/search/analytic.js +1 -1
  23. package/api/search/case.d.ts +4 -0
  24. package/api/search/case.js +8 -0
  25. package/api/search/count/hit.js +1 -1
  26. package/api/search/dossier.js +1 -1
  27. package/api/search/eql/hit.js +1 -1
  28. package/api/search/explain/hit.js +1 -1
  29. package/api/search/facet/hit.d.ts +1 -3
  30. package/api/search/facet/hit.js +1 -1
  31. package/api/search/facet/index.d.ts +3 -1
  32. package/api/search/grouped/hit.js +1 -1
  33. package/api/search/grouped/user.js +1 -1
  34. package/api/search/histogram/hit.js +1 -1
  35. package/api/search/hit.js +1 -1
  36. package/api/search/index.d.ts +3 -1
  37. package/api/search/index.js +2 -1
  38. package/api/search/overview.d.ts +2 -2
  39. package/api/search/overview.js +1 -1
  40. package/api/search/sigma/hit.js +1 -1
  41. package/api/search/template.js +1 -1
  42. package/api/search/user.js +1 -1
  43. package/api/search/view.js +1 -1
  44. package/api/socket/index.d.ts +3 -0
  45. package/api/socket/index.js +6 -0
  46. package/api/socket/viewers.d.ts +2 -0
  47. package/api/socket/viewers.js +8 -0
  48. package/api/socket/viewers.test.js +44 -0
  49. package/api/template/index.d.ts +4 -3
  50. package/api/template/index.js +6 -6
  51. package/api/user/index.d.ts +2 -1
  52. package/api/user/index.js +2 -2
  53. package/api/v2/case/index.d.ts +9 -0
  54. package/api/v2/case/index.js +21 -0
  55. package/api/v2/case/items.d.ts +9 -0
  56. package/api/v2/case/items.js +19 -0
  57. package/api/v2/case/rules.d.ts +6 -0
  58. package/api/v2/case/rules.js +18 -0
  59. package/api/v2/fuzzy.d.ts +17 -0
  60. package/api/v2/fuzzy.js +11 -0
  61. package/api/v2/index.d.ts +5 -0
  62. package/api/v2/index.js +7 -0
  63. package/api/v2/search/facet.d.ts +3 -0
  64. package/api/v2/search/facet.js +12 -0
  65. package/api/v2/search/index.d.ts +6 -0
  66. package/api/v2/search/index.js +24 -0
  67. package/api/view/index.d.ts +4 -3
  68. package/api/view/index.js +6 -6
  69. package/branding/AppBrand.d.ts +2 -2
  70. package/commons/components/app/AppSkeleton.js +1 -1
  71. package/commons/components/app/hooks/useAppConfigs.d.ts +2 -2
  72. package/commons/components/app/hooks/useAppSitemap.d.ts +5 -2
  73. package/commons/components/breadcrumbs/BreadcrumbIcon.js +1 -1
  74. package/commons/components/breadcrumbs/Breadcrumbs.d.ts +3 -2
  75. package/commons/components/display/AppToc.d.ts +2 -1
  76. package/commons/components/display/AppToc.js +1 -1
  77. package/commons/components/display/hooks/useAppColor.d.ts +1 -1
  78. package/commons/components/leftnav/LeftNavDrawer.d.ts +2 -1
  79. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  80. package/commons/components/leftnav/LeftNavGroup.d.ts +2 -1
  81. package/commons/components/leftnav/LeftNavItem.d.ts +2 -1
  82. package/commons/components/leftnav/LeftNavItem.js +1 -1
  83. package/commons/components/notification/Notification.js +1 -1
  84. package/commons/components/notification/elements/NotificationHeader.js +1 -1
  85. package/commons/components/notification/elements/NotificationItems.js +1 -1
  86. package/commons/components/notification/elements/item/NotificationItemAuthor.js +1 -1
  87. package/commons/components/pages/PageCenter.d.ts +2 -1
  88. package/commons/components/pages/PageContent.d.ts +2 -1
  89. package/commons/components/pages/PageFullScreen.d.ts +2 -1
  90. package/commons/components/search/AppSearch.js +1 -1
  91. package/commons/components/search/AppSearchInput.d.ts +2 -1
  92. package/commons/components/search/AppSearchResult.d.ts +2 -1
  93. package/commons/components/topnav/AppBar.d.ts +2 -1
  94. package/commons/components/topnav/AppName.d.ts +3 -2
  95. package/commons/components/topnav/AppSwitcher.d.ts +2 -1
  96. package/commons/components/topnav/ThemeSelection.d.ts +2 -1
  97. package/commons/components/topnav/ThemeSelection.js +1 -1
  98. package/commons/components/topnav/UserProfile.d.ts +2 -1
  99. package/commons/components/topnav/UserProfile.js +1 -1
  100. package/commons/components/utils/hooks/useThemeBuilder.d.ts +1 -1
  101. package/components/app/App.js +52 -12
  102. package/components/app/drawers/ApiKeyDrawer.js +1 -1
  103. package/components/app/hooks/useMatchers.d.ts +1 -1
  104. package/components/app/hooks/useMatchers.js +27 -15
  105. package/components/app/hooks/useMatchers.test.js +22 -22
  106. package/components/app/hooks/useTitle.js +6 -6
  107. package/components/app/providers/AnalyticProvider.js +1 -1
  108. package/components/app/providers/AvatarProvider.js +1 -1
  109. package/components/app/providers/FavouritesProvider.js +3 -3
  110. package/components/app/providers/GridColumnsProvider.d.ts +52 -0
  111. package/components/app/providers/GridColumnsProvider.js +177 -0
  112. package/components/app/providers/GridColumnsProvider.test.js +255 -0
  113. package/components/app/providers/ModalProvider.d.ts +1 -0
  114. package/components/app/providers/OverviewProvider.js +1 -1
  115. package/components/app/providers/ParameterProvider.d.ts +25 -3
  116. package/components/app/providers/ParameterProvider.js +163 -226
  117. package/components/app/providers/ParameterProvider.test.js +358 -94
  118. package/components/app/providers/RecordProvider.d.ts +23 -0
  119. package/components/app/providers/{HitProvider.js → RecordProvider.js} +47 -43
  120. package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
  121. package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +24 -37
  122. package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +52 -71
  123. package/components/app/providers/SearchResponseProvider.d.ts +21 -0
  124. package/components/app/providers/SearchResponseProvider.js +81 -0
  125. package/components/app/providers/SearchResponseProvider.test.d.ts +1 -0
  126. package/components/app/providers/SearchResponseProvider.test.js +406 -0
  127. package/components/app/providers/SocketProvider.d.ts +11 -2
  128. package/components/app/providers/SocketProvider.js +21 -8
  129. package/components/app/providers/UserListProvider.js +28 -8
  130. package/components/app/providers/UserListProvider.test.d.ts +1 -0
  131. package/components/app/providers/UserListProvider.test.js +150 -0
  132. package/components/app/providers/ViewProvider.js +3 -3
  133. package/components/app/providers/ViewProvider.test.js +4 -1
  134. package/components/elements/Comment.js +2 -2
  135. package/components/elements/ContextMenu.d.ts +56 -0
  136. package/components/elements/ContextMenu.js +113 -0
  137. package/components/elements/ContextMenu.test.d.ts +1 -0
  138. package/components/elements/ContextMenu.test.js +215 -0
  139. package/components/elements/EditRow.js +4 -4
  140. package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
  141. package/components/elements/ObjectDetails.d.ts +6 -0
  142. package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
  143. package/components/elements/PluginChip.d.ts +1 -1
  144. package/components/elements/PluginChip.js +0 -1
  145. package/components/elements/PluginTypography.d.ts +2 -1
  146. package/components/elements/PluginTypography.js +3 -2
  147. package/components/elements/UserList.d.ts +5 -2
  148. package/components/elements/UserList.js +34 -17
  149. package/components/elements/UserList.test.d.ts +1 -0
  150. package/components/elements/UserList.test.js +122 -0
  151. package/components/elements/addons/buttons/CustomIconButton.test.d.ts +1 -0
  152. package/components/elements/addons/buttons/CustomIconButton.test.js +81 -0
  153. package/components/elements/addons/buttons/index.d.ts +1 -1
  154. package/components/elements/addons/layout/FlexOne.d.ts +3 -2
  155. package/components/elements/addons/layout/FlexPort.d.ts +2 -1
  156. package/components/elements/addons/layout/FlexVertical.d.ts +2 -1
  157. package/components/elements/addons/layout/FlexVertical.test.d.ts +1 -0
  158. package/components/elements/addons/layout/FlexVertical.test.js +32 -0
  159. package/components/elements/addons/layout/vsbox/VSBoxContent.test.d.ts +1 -0
  160. package/components/elements/addons/layout/vsbox/VSBoxContent.test.js +26 -0
  161. package/components/elements/addons/layout/vsbox/VSBoxElement.d.ts +2 -1
  162. package/components/elements/addons/lists/TuiList.d.ts +2 -1
  163. package/components/elements/addons/lists/TuiListBase.d.ts +2 -1
  164. package/components/elements/addons/lists/TuiListElement.d.ts +3 -2
  165. package/components/elements/addons/lists/TuiListElement.js +0 -1
  166. package/components/elements/addons/lists/TuiListMenu.d.ts +2 -1
  167. package/components/elements/addons/lists/TuiListMenu.js +1 -1
  168. package/components/elements/addons/lists/table/TuiTableHeader.d.ts +3 -2
  169. package/components/elements/addons/lists/table/index.d.ts +1 -1
  170. package/components/elements/addons/search/SearchPagination.d.ts +2 -1
  171. package/components/elements/addons/search/SearchPagination.js +4 -4
  172. package/components/elements/addons/search/SearchTotal.js +1 -1
  173. package/components/elements/addons/search/SearchTotal.test.js +5 -3
  174. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  175. package/components/elements/addons/search/phrase/WordLexer.test.d.ts +1 -0
  176. package/components/elements/addons/search/phrase/WordLexer.test.js +55 -0
  177. package/components/elements/case/CaseCard.d.ts +12 -0
  178. package/components/elements/case/CaseCard.js +43 -0
  179. package/components/elements/case/CasePreview.d.ts +6 -0
  180. package/components/elements/case/CasePreview.js +17 -0
  181. package/components/elements/case/StatusIcon.d.ts +5 -0
  182. package/components/elements/case/StatusIcon.js +13 -0
  183. package/components/elements/display/ActionButton.js +1 -1
  184. package/components/elements/display/ChipPopper.d.ts +1 -1
  185. package/components/elements/display/ChipPopper.js +7 -6
  186. package/components/elements/display/Classification.js +1 -1
  187. package/components/elements/display/ClassificationChip.d.ts +14 -0
  188. package/components/elements/display/ClassificationChip.js +36 -0
  189. package/components/elements/display/HandlebarsMarkdown.js +38 -3
  190. package/components/elements/display/HowlerAvatar.js +2 -2
  191. package/components/elements/display/HowlerCard.js +1 -1
  192. package/components/elements/display/HowlerCard.test.js +5 -9
  193. package/components/elements/display/Image.js +3 -3
  194. package/components/elements/display/ItemManager.d.ts +2 -2
  195. package/components/elements/display/ItemManager.js +3 -3
  196. package/components/elements/display/Markdown.js +2 -2
  197. package/components/elements/display/Modal.js +2 -0
  198. package/components/elements/display/QueryResultText.d.ts +1 -0
  199. package/components/elements/display/QueryResultText.js +2 -2
  200. package/components/elements/display/handlebars/helpers.d.ts +4 -0
  201. package/components/elements/display/handlebars/helpers.js +134 -35
  202. package/components/elements/display/json/JSONViewer.js +2 -2
  203. package/components/elements/display/modals/RationaleModal.js +2 -2
  204. package/components/elements/display/modals/RationaleModal.test.js +1 -1
  205. package/components/elements/event/EventCard.d.ts +6 -0
  206. package/components/elements/event/EventCard.js +22 -0
  207. package/components/elements/event/EventPreview.d.ts +6 -0
  208. package/components/elements/event/EventPreview.js +12 -0
  209. package/components/elements/hit/HitActions.d.ts +1 -1
  210. package/components/elements/hit/HitActions.js +5 -5
  211. package/components/elements/hit/HitBanner.d.ts +1 -0
  212. package/components/elements/hit/HitBanner.js +32 -50
  213. package/components/elements/hit/HitCard.d.ts +4 -0
  214. package/components/elements/hit/HitCard.js +7 -7
  215. package/components/elements/hit/HitLabels.js +3 -3
  216. package/components/elements/hit/HitLabels.test.d.ts +1 -0
  217. package/components/elements/hit/HitLabels.test.js +77 -0
  218. package/components/elements/hit/HitLinks.d.ts +5 -4
  219. package/components/elements/hit/HitLinks.js +28 -10
  220. package/components/elements/hit/HitNotebooks.js +32 -31
  221. package/components/elements/hit/HitOutline.d.ts +1 -0
  222. package/components/elements/hit/HitOutline.js +4 -4
  223. package/components/elements/hit/HitOverview.js +2 -2
  224. package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
  225. package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +11 -5
  226. package/components/elements/hit/HitSummary.d.ts +2 -1
  227. package/components/elements/hit/HitSummary.js +8 -7
  228. package/components/elements/hit/NotebookTooltip.d.ts +3 -0
  229. package/components/elements/hit/NotebookTooltip.js +8 -0
  230. package/components/elements/hit/PivotTooltip.d.ts +7 -0
  231. package/components/elements/hit/PivotTooltip.js +9 -0
  232. package/components/elements/hit/RelatedLinkTooltip.d.ts +6 -0
  233. package/components/elements/hit/RelatedLinkTooltip.js +8 -0
  234. package/components/elements/hit/ResolvePivotUrl.d.ts +4 -0
  235. package/components/elements/hit/ResolvePivotUrl.js +22 -0
  236. package/components/elements/hit/actions/ButtonActions.js +1 -3
  237. package/components/elements/hit/aggregate/HitGraph.js +9 -9
  238. package/components/elements/hit/elements/AnalyticLink.d.ts +9 -0
  239. package/components/elements/hit/elements/AnalyticLink.js +22 -0
  240. package/components/elements/hit/elements/{HitUsers.d.ts → Assigned.d.ts} +2 -4
  241. package/components/elements/hit/elements/Assigned.js +35 -0
  242. package/components/elements/hit/elements/Assigned.test.d.ts +1 -0
  243. package/components/elements/hit/elements/Assigned.test.js +65 -0
  244. package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.js +6 -5
  245. package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.d.ts +2 -1
  246. package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.js +3 -3
  247. package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.d.ts +2 -1
  248. package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.js +2 -2
  249. package/components/{routes/hits/search/grid/HitRow.d.ts → elements/hit/grid/RecordRow.d.ts} +4 -3
  250. package/components/{routes/hits/search/grid/HitRow.js → elements/hit/grid/RecordRow.js} +12 -10
  251. package/components/elements/hit/grid/RecordTable.d.ts +13 -0
  252. package/components/elements/hit/grid/RecordTable.js +84 -0
  253. package/components/elements/hit/outlines/DefaultOutline.js +2 -4
  254. package/components/elements/hit/related/PivotLink.d.ts +3 -0
  255. package/components/elements/hit/related/PivotLink.js +6 -3
  256. package/components/elements/hit/related/RelatedLink.d.ts +1 -0
  257. package/components/elements/hit/related/RelatedLink.js +18 -15
  258. package/components/elements/hit/related/RelatedRecords.js +63 -0
  259. package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
  260. package/components/elements/{hit/HitComments.js → record/RecordComments.js} +33 -34
  261. package/components/{routes/hits/search/HitContextMenu.d.ts → elements/record/RecordContextMenu.d.ts} +3 -3
  262. package/components/elements/record/RecordContextMenu.js +268 -0
  263. package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
  264. package/components/{routes/hits/search/HitContextMenu.test.js → elements/record/RecordContextMenu.test.js} +143 -49
  265. package/components/elements/record/RecordRelated.d.ts +7 -0
  266. package/components/elements/record/RecordRelated.js +34 -0
  267. package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
  268. package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +16 -14
  269. package/components/elements/search/FuzzySearchBar.d.ts +7 -0
  270. package/components/elements/search/FuzzySearchBar.js +33 -0
  271. package/components/elements/search/FuzzySearchBar.test.d.ts +1 -0
  272. package/components/elements/search/FuzzySearchBar.test.js +87 -0
  273. package/components/elements/view/LayoutToggle.d.ts +8 -0
  274. package/components/elements/view/LayoutToggle.js +12 -0
  275. package/components/elements/view/ViewTitle.d.ts +4 -0
  276. package/components/elements/view/ViewTitle.js +13 -3
  277. package/components/hooks/useHitActions.d.ts +1 -1
  278. package/components/hooks/useHitActions.js +5 -5
  279. package/components/hooks/useLocalStorage.js +1 -1
  280. package/components/hooks/useLocalStorageItem.test.js +2 -2
  281. package/components/hooks/useMyApi.d.ts +1 -1
  282. package/components/hooks/useMyChart.d.ts +23 -23
  283. package/components/hooks/useMyPreferences.js +10 -1
  284. package/components/hooks/useMySearch.js +2 -2
  285. package/components/hooks/useMySitemap.js +3 -1
  286. package/components/hooks/useMySnackbar.js +1 -1
  287. package/components/hooks/useMyTheme.js +9 -2
  288. package/components/hooks/useMyTheme.test.d.ts +1 -0
  289. package/components/hooks/useMyTheme.test.js +35 -0
  290. package/components/hooks/useMyUserFunctions.d.ts +21 -21
  291. package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
  292. package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
  293. package/components/hooks/useRelatedRecords.d.ts +13 -0
  294. package/components/hooks/useRelatedRecords.js +32 -0
  295. package/components/logins/Login.js +1 -1
  296. package/components/logins/auth/OAuthLogin.js +1 -1
  297. package/components/logins/hooks/useLogin.js +2 -2
  298. package/components/routes/ErrorBoundary.d.ts +2 -13
  299. package/components/routes/ErrorBoundary.js +12 -1
  300. package/components/routes/ErrorBoundary.test.js +27 -4
  301. package/components/routes/action/edit/ActionEditor.js +6 -6
  302. package/components/routes/action/shared/OperationEntry.js +1 -1
  303. package/components/routes/action/shared/OperationStep.js +1 -1
  304. package/components/routes/action/useMyActionFunctions.js +6 -3
  305. package/components/routes/action/view/ActionDetails.js +9 -3
  306. package/components/routes/action/view/ActionSearch.js +25 -19
  307. package/components/routes/admin/users/UserEditor.js +1 -1
  308. package/components/routes/admin/users/UserSearch.js +5 -5
  309. package/components/routes/advanced/QueryBuilder.js +6 -29
  310. package/components/routes/advanced/QueryEditor.js +3 -3
  311. package/components/routes/advanced/eqlTokenProvider.test.d.ts +1 -0
  312. package/components/routes/advanced/eqlTokenProvider.test.js +34 -0
  313. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  314. package/components/routes/advanced/luceneCompletionProvider.js +1 -1
  315. package/components/routes/advanced/luceneTokenProvider.test.d.ts +1 -0
  316. package/components/routes/advanced/luceneTokenProvider.test.js +34 -0
  317. package/components/routes/analytics/AnalyticComments.js +3 -5
  318. package/components/routes/analytics/AnalyticDetails.js +8 -56
  319. package/components/routes/analytics/AnalyticHitComments.js +1 -1
  320. package/components/routes/analytics/AnalyticHitComments.test.js +1 -1
  321. package/components/routes/analytics/AnalyticOverview.js +2 -2
  322. package/components/routes/analytics/AnalyticOverviews.js +1 -1
  323. package/components/routes/analytics/AnalyticSearch.js +16 -25
  324. package/components/routes/analytics/AnalyticTemplates.js +1 -1
  325. package/components/routes/analytics/TriageSettings.js +2 -2
  326. package/components/routes/analytics/widgets/Assessment.js +1 -1
  327. package/components/routes/analytics/widgets/Created.js +1 -1
  328. package/components/routes/analytics/widgets/Escalation.js +1 -1
  329. package/components/routes/analytics/widgets/Stacked.js +1 -1
  330. package/components/routes/cases/CaseViewer.d.ts +2 -0
  331. package/components/routes/cases/CaseViewer.js +44 -0
  332. package/components/routes/cases/CaseViewer.test.d.ts +1 -0
  333. package/components/routes/cases/CaseViewer.test.js +156 -0
  334. package/components/routes/cases/Cases.d.ts +2 -0
  335. package/components/routes/cases/Cases.js +140 -0
  336. package/components/routes/cases/constants.d.ts +6 -0
  337. package/components/routes/cases/constants.js +6 -0
  338. package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
  339. package/components/routes/cases/detail/AlertPanel.js +34 -0
  340. package/components/routes/cases/detail/AlertPanel.test.d.ts +1 -0
  341. package/components/routes/cases/detail/AlertPanel.test.js +107 -0
  342. package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
  343. package/components/routes/cases/detail/CaseDashboard.js +66 -0
  344. package/components/routes/cases/detail/CaseDashboard.test.d.ts +1 -0
  345. package/components/routes/cases/detail/CaseDashboard.test.js +218 -0
  346. package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
  347. package/components/routes/cases/detail/CaseDetails.js +79 -0
  348. package/components/routes/cases/detail/CaseDetails.test.d.ts +1 -0
  349. package/components/routes/cases/detail/CaseDetails.test.js +145 -0
  350. package/components/routes/cases/detail/CaseObservables.d.ts +6 -0
  351. package/components/routes/cases/detail/CaseObservables.js +127 -0
  352. package/components/routes/cases/detail/CaseObservables.test.d.ts +1 -0
  353. package/components/routes/cases/detail/CaseObservables.test.js +167 -0
  354. package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
  355. package/components/routes/cases/detail/CaseOverview.js +40 -0
  356. package/components/routes/cases/detail/CaseRules.d.ts +7 -0
  357. package/components/routes/cases/detail/CaseRules.js +55 -0
  358. package/components/routes/cases/detail/CaseRules.test.d.ts +1 -0
  359. package/components/routes/cases/detail/CaseRules.test.js +238 -0
  360. package/components/routes/cases/detail/CaseSearch.d.ts +3 -0
  361. package/components/routes/cases/detail/CaseSearch.js +78 -0
  362. package/components/routes/cases/detail/CaseSearch.test.d.ts +1 -0
  363. package/components/routes/cases/detail/CaseSearch.test.js +154 -0
  364. package/components/routes/cases/detail/CaseSidebar.d.ts +8 -0
  365. package/components/routes/cases/detail/CaseSidebar.js +147 -0
  366. package/components/routes/cases/detail/CaseSidebar.test.d.ts +1 -0
  367. package/components/routes/cases/detail/CaseSidebar.test.js +155 -0
  368. package/components/routes/cases/detail/CaseTask.d.ts +14 -0
  369. package/components/routes/cases/detail/CaseTask.js +71 -0
  370. package/components/routes/cases/detail/CaseTask.test.d.ts +1 -0
  371. package/components/routes/cases/detail/CaseTask.test.js +143 -0
  372. package/components/routes/cases/detail/CaseTimeline.d.ts +12 -0
  373. package/components/routes/cases/detail/CaseTimeline.js +120 -0
  374. package/components/routes/cases/detail/CaseTimeline.test.d.ts +1 -0
  375. package/components/routes/cases/detail/CaseTimeline.test.js +319 -0
  376. package/components/routes/cases/detail/CreateRuleDialog.d.ts +9 -0
  377. package/components/routes/cases/detail/CreateRuleDialog.js +145 -0
  378. package/components/routes/cases/detail/CreateRuleDialog.test.d.ts +1 -0
  379. package/components/routes/cases/detail/CreateRuleDialog.test.js +218 -0
  380. package/components/routes/cases/detail/ItemPage.d.ts +6 -0
  381. package/components/routes/cases/detail/ItemPage.js +100 -0
  382. package/components/routes/cases/detail/ItemPage.test.d.ts +1 -0
  383. package/components/routes/cases/detail/ItemPage.test.js +238 -0
  384. package/components/routes/cases/detail/MarkdownPage.d.ts +8 -0
  385. package/components/routes/cases/detail/MarkdownPage.js +63 -0
  386. package/components/routes/cases/detail/MarkdownPage.test.d.ts +1 -0
  387. package/components/routes/cases/detail/MarkdownPage.test.js +116 -0
  388. package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
  389. package/components/routes/cases/detail/RelatedCasePanel.js +34 -0
  390. package/components/routes/cases/detail/RelatedCasePanel.test.d.ts +1 -0
  391. package/components/routes/cases/detail/RelatedCasePanel.test.js +115 -0
  392. package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
  393. package/components/routes/cases/detail/TaskPanel.js +95 -0
  394. package/components/routes/cases/detail/TaskPanel.test.d.ts +1 -0
  395. package/components/routes/cases/detail/TaskPanel.test.js +145 -0
  396. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +11 -0
  397. package/components/routes/cases/detail/aggregates/CaseAggregate.js +24 -0
  398. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
  399. package/components/routes/cases/detail/aggregates/SourceAggregate.js +26 -0
  400. package/components/routes/cases/detail/observables/Observable.d.ts +8 -0
  401. package/components/routes/cases/detail/observables/Observable.js +14 -0
  402. package/components/routes/cases/detail/observables/Observable.test.d.ts +1 -0
  403. package/components/routes/cases/detail/observables/Observable.test.js +80 -0
  404. package/components/routes/cases/detail/observables/ObservableTable.d.ts +7 -0
  405. package/components/routes/cases/detail/observables/ObservableTable.js +61 -0
  406. package/components/routes/cases/detail/observables/ObservableTable.test.d.ts +1 -0
  407. package/components/routes/cases/detail/observables/ObservableTable.test.js +97 -0
  408. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +22 -0
  409. package/components/routes/cases/detail/sidebar/CaseFolder.js +105 -0
  410. package/components/routes/cases/detail/sidebar/CaseFolder.test.d.ts +1 -0
  411. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +98 -0
  412. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +32 -0
  413. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +99 -0
  414. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +1 -0
  415. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +117 -0
  416. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +25 -0
  417. package/components/routes/cases/detail/sidebar/FolderEntry.js +89 -0
  418. package/components/routes/cases/detail/sidebar/FolderEntry.test.d.ts +1 -0
  419. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +81 -0
  420. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +5 -0
  421. package/components/routes/cases/detail/sidebar/RootDropZone.js +33 -0
  422. package/components/routes/cases/detail/sidebar/types.d.ts +9 -0
  423. package/components/routes/cases/detail/sidebar/utils.d.ts +10 -0
  424. package/components/routes/cases/detail/sidebar/utils.js +51 -0
  425. package/components/routes/cases/detail/sidebar/utils.test.d.ts +1 -0
  426. package/components/routes/cases/detail/sidebar/utils.test.js +82 -0
  427. package/components/routes/cases/detail/types.d.ts +22 -0
  428. package/components/routes/cases/detail/utils.d.ts +28 -0
  429. package/components/routes/cases/detail/utils.js +117 -0
  430. package/components/routes/cases/detail/utils.test.d.ts +1 -0
  431. package/components/routes/cases/detail/utils.test.js +166 -0
  432. package/components/routes/cases/hooks/useCase.d.ts +13 -0
  433. package/components/routes/cases/hooks/useCase.js +67 -0
  434. package/components/routes/cases/hooks/useCase.test.d.ts +1 -0
  435. package/components/routes/cases/hooks/useCase.test.js +170 -0
  436. package/components/routes/cases/modals/AddRecordToCaseModal.d.ts +7 -0
  437. package/components/routes/cases/modals/AddRecordToCaseModal.js +57 -0
  438. package/components/routes/cases/modals/AddRecordToCaseModal.test.d.ts +1 -0
  439. package/components/routes/cases/modals/AddRecordToCaseModal.test.js +309 -0
  440. package/components/routes/cases/modals/AddToCaseModal.d.ts +9 -0
  441. package/components/routes/cases/modals/AddToCaseModal.js +72 -0
  442. package/components/routes/cases/modals/CaseRecordRow.d.ts +9 -0
  443. package/components/routes/cases/modals/CaseRecordRow.js +14 -0
  444. package/components/routes/cases/modals/CreateCaseModal.d.ts +7 -0
  445. package/components/routes/cases/modals/CreateCaseModal.js +53 -0
  446. package/components/routes/cases/modals/CreateCaseModal.test.d.ts +1 -0
  447. package/components/routes/cases/modals/CreateCaseModal.test.js +319 -0
  448. package/components/routes/cases/modals/RenameItemModal.d.ts +9 -0
  449. package/components/routes/cases/modals/RenameItemModal.js +44 -0
  450. package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
  451. package/components/routes/cases/modals/ResolveModal.js +120 -0
  452. package/components/routes/cases/modals/ResolveModal.test.d.ts +1 -0
  453. package/components/routes/cases/modals/ResolveModal.test.js +520 -0
  454. package/components/routes/cases/modals/hooks.d.ts +7 -0
  455. package/components/routes/cases/modals/hooks.js +40 -0
  456. package/components/routes/cases/modals/types.d.ts +13 -0
  457. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +6 -0
  458. package/components/routes/cases/search/CaseAssigneeFilter.js +33 -0
  459. package/components/routes/cases/search/CaseAssigneeFilter.test.d.ts +1 -0
  460. package/components/routes/cases/search/CaseAssigneeFilter.test.js +127 -0
  461. package/components/routes/cases/search/CaseDateFilter.d.ts +13 -0
  462. package/components/routes/cases/search/CaseDateFilter.js +26 -0
  463. package/components/routes/cases/search/CaseDateFilter.test.d.ts +1 -0
  464. package/components/routes/cases/search/CaseDateFilter.test.js +115 -0
  465. package/components/routes/cases/search/CaseStatusFilter.d.ts +6 -0
  466. package/components/routes/cases/search/CaseStatusFilter.js +13 -0
  467. package/components/routes/cases/search/CaseStatusFilter.test.d.ts +1 -0
  468. package/components/routes/cases/search/CaseStatusFilter.test.js +86 -0
  469. package/components/routes/cases/utils.d.ts +3 -0
  470. package/components/routes/cases/utils.js +21 -0
  471. package/components/routes/dossiers/DossierEditor.js +4 -4
  472. package/components/routes/dossiers/DossierEditor.test.js +2 -2
  473. package/components/routes/dossiers/Dossiers.js +16 -14
  474. package/components/routes/dossiers/PivotForm.js +2 -4
  475. package/components/routes/events/EventViewer.d.ts +7 -0
  476. package/components/routes/events/EventViewer.js +24 -0
  477. package/components/routes/help/ApiDocumentation.js +2 -2
  478. package/components/routes/help/HitBannerDocumentation.js +2 -1
  479. package/components/routes/help/HitDocumentation.js +1 -3
  480. package/components/routes/help/HitSchemaDocumentation.js +1 -1
  481. package/components/routes/help/OverviewDocumentation.d.ts +1 -1
  482. package/components/routes/help/OverviewDocumentation.js +0 -1
  483. package/components/routes/help/RetentionDocumentation.js +2 -2
  484. package/components/routes/help/SearchDocumentation.js +1 -1
  485. package/components/routes/help/markdown/en/client.md.js +1 -1
  486. package/components/routes/help/markdown/fr/client.md.js +1 -1
  487. package/components/routes/hits/search/InformationPane.d.ts +1 -0
  488. package/components/routes/hits/search/InformationPane.js +52 -65
  489. package/components/routes/hits/search/QuerySettings.js +2 -1
  490. package/components/routes/hits/search/QuerySettings.test.js +19 -17
  491. package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +13 -12
  492. package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
  493. package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
  494. package/components/routes/hits/search/SearchPane.js +28 -50
  495. package/components/routes/hits/search/ViewLink.js +7 -6
  496. package/components/routes/hits/search/ViewLink.test.js +8 -9
  497. package/components/routes/hits/search/grid/RecordGrid.d.ts +3 -0
  498. package/components/routes/hits/search/grid/RecordGrid.js +64 -0
  499. package/components/routes/hits/search/shared/CustomSort.js +1 -1
  500. package/components/routes/hits/search/shared/CustomSpan.test.js +3 -4
  501. package/components/routes/hits/search/shared/HitFilter.d.ts +1 -1
  502. package/components/routes/hits/search/shared/HitSort.d.ts +1 -1
  503. package/components/routes/hits/search/shared/HitSort.js +2 -1
  504. package/components/routes/hits/search/shared/IndexPicker.d.ts +8 -0
  505. package/components/routes/hits/search/shared/IndexPicker.js +21 -0
  506. package/components/routes/hits/search/shared/LayoutSettings.js +25 -0
  507. package/components/routes/hits/search/shared/SearchActionMenu.d.ts +4 -0
  508. package/components/routes/hits/search/shared/SearchActionMenu.js +11 -0
  509. package/components/routes/hits/search/shared/SearchSpan.d.ts +1 -1
  510. package/components/routes/hits/search/shared/SearchSpan.js +1 -1
  511. package/components/routes/hits/view/HitViewer.js +16 -17
  512. package/components/routes/home/AddNewCard.js +2 -2
  513. package/components/routes/home/AddNewCard.test.js +20 -2
  514. package/components/routes/home/AnalyticCard.js +1 -1
  515. package/components/routes/home/EntryWrapper.js +1 -1
  516. package/components/routes/home/HomeSettings.js +1 -1
  517. package/components/routes/home/HomeSettings.test.d.ts +1 -0
  518. package/components/routes/home/HomeSettings.test.js +44 -0
  519. package/components/routes/home/ViewCard.js +52 -46
  520. package/components/routes/home/ViewRefresh.test.js +1 -1
  521. package/components/routes/home/index.js +2 -2
  522. package/components/routes/overviews/OverviewCard.js +1 -1
  523. package/components/routes/overviews/OverviewViewer.js +13 -10
  524. package/components/routes/overviews/Overviews.js +16 -14
  525. package/components/routes/overviews/markdownExtendedTokenProvider.js +15 -15
  526. package/components/routes/overviews/markdownExtendedTokenProvider.test.d.ts +1 -0
  527. package/components/routes/overviews/markdownExtendedTokenProvider.test.js +50 -0
  528. package/components/routes/settings/ProfileSection.js +2 -2
  529. package/components/routes/templates/TemplateEditor.d.ts +3 -2
  530. package/components/routes/templates/TemplateEditor.js +1 -1
  531. package/components/routes/templates/TemplateViewer.js +10 -5
  532. package/components/routes/templates/Templates.js +19 -14
  533. package/components/routes/views/ViewComposer.js +78 -26
  534. package/components/routes/views/Views.js +17 -14
  535. package/i18n.js +1 -1
  536. package/index.css +4 -0
  537. package/index.d.ts +1 -1
  538. package/index.js +1 -1
  539. package/locales/en/translation.json +188 -34
  540. package/locales/fr/translation.json +186 -32
  541. package/models/WithMetadata.d.ts +2 -1
  542. package/models/entities/generated/Analytic.d.ts +0 -3
  543. package/models/entities/generated/ApiType.d.ts +3 -1
  544. package/models/entities/generated/AttachmentsFile.d.ts +12 -0
  545. package/models/entities/generated/Case.d.ts +29 -0
  546. package/models/entities/generated/Column.d.ts +7 -0
  547. package/models/entities/generated/DestinationOriginal.d.ts +19 -0
  548. package/models/entities/generated/ECSEvent.d.ts +31 -0
  549. package/models/entities/generated/EmailAttachment.d.ts +8 -0
  550. package/models/entities/generated/EmailParent.d.ts +19 -0
  551. package/models/entities/generated/Enrichments.d.ts +7 -0
  552. package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
  553. package/models/entities/generated/Event.d.ts +80 -26
  554. package/models/entities/generated/EventCloud.d.ts +20 -0
  555. package/models/entities/generated/EventDestination.d.ts +23 -0
  556. package/models/entities/generated/EventEmail.d.ts +30 -0
  557. package/models/entities/generated/EventFile.d.ts +36 -0
  558. package/models/entities/generated/EventHowler.d.ts +42 -0
  559. package/models/entities/generated/EventHttp.d.ts +11 -0
  560. package/models/entities/generated/EventObserver.d.ts +21 -0
  561. package/models/entities/generated/EventOrganization.d.ts +7 -0
  562. package/models/entities/generated/EventProcess.d.ts +34 -0
  563. package/models/entities/generated/EventSource.d.ts +23 -0
  564. package/models/entities/generated/EventThreat.d.ts +21 -0
  565. package/models/entities/generated/EventTls.d.ts +12 -0
  566. package/models/entities/generated/Hit.d.ts +3 -2
  567. package/models/entities/generated/Host.d.ts +1 -0
  568. package/models/entities/generated/Howler.d.ts +1 -6
  569. package/models/entities/generated/HttpResponse.d.ts +11 -0
  570. package/models/entities/generated/Item.d.ts +12 -0
  571. package/models/entities/generated/ObserverIngress.d.ts +9 -0
  572. package/models/entities/generated/Rule.d.ts +8 -9
  573. package/models/entities/generated/Settings.d.ts +4 -0
  574. package/models/entities/generated/Task.d.ts +10 -0
  575. package/models/entities/generated/Threat.d.ts +2 -2
  576. package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
  577. package/models/entities/generated/View.d.ts +1 -0
  578. package/models/socket/CaseUpdate.d.ts +5 -0
  579. package/models/socket/ViewersUpdate.d.ts +4 -0
  580. package/package.json +156 -137
  581. package/plugins/clue/components/ClueLeadForm.js +1 -1
  582. package/plugins/clue/components/CluePivotForm.js +2 -2
  583. package/plugins/clue/components/ClueTypography.js +2 -2
  584. package/plugins/clue/helpers.js +4 -4
  585. package/plugins/clue/index.d.ts +3 -2
  586. package/plugins/clue/index.js +0 -1
  587. package/plugins/clue/utils.d.ts +2 -1
  588. package/rest/AxiosClient.test.d.ts +1 -0
  589. package/rest/AxiosClient.test.js +185 -0
  590. package/tests/mocks.d.ts +11 -1
  591. package/tests/mocks.js +12 -7
  592. package/tests/server-handlers.js +11 -1
  593. package/tests/utils.d.ts +4 -0
  594. package/tests/utils.js +21 -0
  595. package/utils/Throttler.test.js +2 -2
  596. package/utils/actionUtils.d.ts +3 -3
  597. package/utils/classificationParser.d.ts +195 -0
  598. package/utils/classificationParser.js +680 -0
  599. package/utils/constants.d.ts +4 -3
  600. package/utils/constants.js +6 -0
  601. package/utils/localStorage.js +1 -1
  602. package/utils/menuUtils.js +0 -4
  603. package/utils/recordFunctions.d.ts +3 -0
  604. package/utils/recordFunctions.js +8 -0
  605. package/utils/recordFunctions.test.d.ts +1 -0
  606. package/utils/{hitFunctions.test.js → recordFunctions.test.js} +1 -1
  607. package/utils/socketUtils.d.ts +14 -0
  608. package/utils/socketUtils.js +17 -1
  609. package/utils/socketUtils.test.js +49 -16
  610. package/utils/stringUtils.d.ts +1 -0
  611. package/utils/stringUtils.js +7 -0
  612. package/utils/stringUtils.test.js +22 -2
  613. package/utils/typeUtils.d.ts +7 -0
  614. package/utils/typeUtils.js +27 -0
  615. package/utils/utils.d.ts +1 -1
  616. package/utils/utils.js +2 -2
  617. package/utils/utils.test.js +119 -2
  618. package/utils/viewUtils.js +3 -0
  619. package/utils/xsrf.js +1 -1
  620. package/api/analytic/rules.d.ts +0 -3
  621. package/api/analytic/rules.js +0 -8
  622. package/components/app/providers/HitProvider.d.ts +0 -22
  623. package/components/elements/display/icons/BundleButton.d.ts +0 -6
  624. package/components/elements/display/icons/BundleButton.js +0 -32
  625. package/components/elements/hit/HitRelated.d.ts +0 -6
  626. package/components/elements/hit/HitRelated.js +0 -7
  627. package/components/elements/hit/elements/HitUsers.js +0 -33
  628. package/components/routes/advanced/RuleModal.d.ts +0 -7
  629. package/components/routes/advanced/RuleModal.js +0 -83
  630. package/components/routes/analytics/RuleView.d.ts +0 -8
  631. package/components/routes/analytics/RuleView.js +0 -48
  632. package/components/routes/help/BundleDocumentation.d.ts +0 -3
  633. package/components/routes/help/BundleDocumentation.js +0 -12
  634. package/components/routes/help/markdown/en/bundles.md.js +0 -1
  635. package/components/routes/help/markdown/fr/bundles.md.js +0 -1
  636. package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
  637. package/components/routes/hits/search/BundleParentMenu.js +0 -32
  638. package/components/routes/hits/search/BundleScroller.d.ts +0 -2
  639. package/components/routes/hits/search/BundleScroller.js +0 -6
  640. package/components/routes/hits/search/HitContextMenu.js +0 -239
  641. package/components/routes/hits/search/LayoutSettings.js +0 -24
  642. package/components/routes/hits/search/grid/HitGrid.d.ts +0 -3
  643. package/components/routes/hits/search/grid/HitGrid.js +0 -132
  644. package/utils/hitFunctions.d.ts +0 -2
  645. package/utils/hitFunctions.js +0 -8
  646. /package/{components/app/providers/HitSearchProvider.test.d.ts → api/socket/viewers.test.d.ts} +0 -0
  647. /package/components/{routes/hits/search/HitContextMenu.test.d.ts → app/providers/GridColumnsProvider.test.d.ts} +0 -0
  648. /package/{utils/hitFunctions.test.d.ts → components/app/providers/RecordSearchProvider.test.d.ts} +0 -0
  649. /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
  650. /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.d.ts +0 -0
  651. /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
  652. /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
  653. /package/components/routes/hits/search/{LayoutSettings.d.ts → shared/LayoutSettings.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
3
  import userEvent, {} from '@testing-library/user-event';
4
4
  import { omit } from 'lodash-es';
@@ -57,6 +57,7 @@ vi.mock('components/app/hooks/useMatchers', () => ({
57
57
  getMatchingTemplate: mockGetMatchingTemplate
58
58
  }))
59
59
  }));
60
+ const mockShowModal = vi.fn();
60
61
  const mockDispatchApi = vi.fn();
61
62
  vi.mock('components/hooks/useMyApi', () => ({
62
63
  default: vi.fn(() => ({
@@ -82,15 +83,18 @@ vi.mock('plugins/store', () => ({
82
83
  plugins: ['plugin1']
83
84
  }
84
85
  }));
86
+ vi.mock('components/routes/cases/modals/AddToCaseModal', () => ({
87
+ default: () => null
88
+ }));
85
89
  // Mock MUI components
86
90
  vi.mock('@mui/material', async () => {
87
91
  const actual = await vi.importActual('@mui/material');
88
92
  return {
89
93
  ...actual,
90
- Menu: ({ children, open, onClose, ...props }) => open ? (_jsx("div", { role: "menu", onClick: onClose, ...omit(props, ['sx', 'slotProps', 'MenuListProps', 'anchorOrigin', 'anchorEl']), children: children })) : null,
94
+ Menu: ({ children, open, onClose, ...props }) => open ? (_jsx("div", { tabIndex: 0, role: "menu", onClick: onClose, ...omit(props, ['sx', 'slotProps', 'MenuListProps', 'anchorOrigin', 'anchorEl']), children: children })) : null,
91
95
  MenuItem: ({ children, onClick, disabled, component, to, ...props }) => {
92
96
  const Component = component || 'div';
93
- return (_jsx(Component, { role: "menuitem", onClick: onClick, "aria-disabled": disabled, href: to, ...omit(props, ['sx', 'component']), children: children }));
97
+ return (_jsx(Component, { tabIndex: 0, role: "menuitem", onClick: onClick, "aria-disabled": disabled, href: to, ...omit(props, ['sx', 'component']), children: children }));
94
98
  },
95
99
  Fade: ({ children, in: inProp }) => (inProp ? _jsx(_Fragment, { children: children }) : null),
96
100
  ListItemIcon: ({ children }) => _jsx("div", { children: children }),
@@ -101,13 +105,14 @@ vi.mock('@mui/material', async () => {
101
105
  });
102
106
  // Import component after mocks
103
107
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
104
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
108
+ import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
105
109
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
110
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
106
111
  import i18n from '@cccsaurora/howler-ui/i18n';
107
112
  import { I18nextProvider } from 'react-i18next';
108
113
  import { createMockAction, createMockAnalytic, createMockHit, createMockTemplate } from '@cccsaurora/howler-ui/tests/utils';
109
114
  import { DEFAULT_QUERY } from '@cccsaurora/howler-ui/utils/constants';
110
- import HitContextMenu from './HitContextMenu';
115
+ import RecordContextMenu from './RecordContextMenu';
111
116
  const mockGetSelectedId = vi.fn(() => 'test-hit-1');
112
117
  const mockConfig = {
113
118
  lookups: {
@@ -115,16 +120,16 @@ const mockConfig = {
115
120
  }
116
121
  };
117
122
  const mockApiContext = { config: mockConfig };
118
- const mockHitContext = {
119
- hits: {
123
+ const mockRecordContext = {
124
+ records: {
120
125
  'test-hit-1': createMockHit()
121
126
  },
122
- selectedHits: []
127
+ selectedRecords: []
123
128
  };
124
129
  const mockParameterContext = { query: DEFAULT_QUERY, setQuery: vi.fn() };
125
130
  // Test wrapper
126
131
  const Wrapper = ({ children }) => {
127
- return (_jsx(I18nextProvider, { i18n: i18n, children: _jsx(ApiConfigContext.Provider, { value: mockApiContext, children: _jsx(HitContext.Provider, { value: mockHitContext, children: _jsx(ParameterContext.Provider, { value: mockParameterContext, children: children }) }) }) }));
132
+ return (_jsx(I18nextProvider, { i18n: i18n, children: _jsx(ApiConfigContext.Provider, { value: mockApiContext, children: _jsx(ModalContext.Provider, { value: { showModal: mockShowModal }, children: _jsx(RecordContext.Provider, { value: mockRecordContext, children: _jsx(ParameterContext.Provider, { value: mockParameterContext, children: children }) }) }) }) }));
128
133
  };
129
134
  describe('HitContextMenu', () => {
130
135
  let user;
@@ -132,11 +137,15 @@ describe('HitContextMenu', () => {
132
137
  beforeEach(() => {
133
138
  user = userEvent.setup();
134
139
  vi.clearAllMocks();
135
- mockHitContext.selectedHits.length = 0;
136
- mockHitContext.hits['test-hit-1'] = createMockHit();
140
+ // Reset context to clean state
141
+ mockRecordContext.selectedRecords.length = 0;
142
+ mockRecordContext.records = {
143
+ 'test-hit-1': createMockHit()
144
+ };
145
+ mockGetSelectedId.mockReturnValue('test-hit-1');
137
146
  mockGetMatchingAnalytic.mockResolvedValue(createMockAnalytic());
138
147
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate());
139
- rerender = render(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) })).rerender;
148
+ rerender = render(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) })).rerender;
140
149
  });
141
150
  describe('Context Menu Initialization', () => {
142
151
  it('should open menu on right-click', async () => {
@@ -166,7 +175,7 @@ describe('HitContextMenu', () => {
166
175
  });
167
176
  act(() => {
168
177
  const menu = screen.getByRole('menu');
169
- user.click(menu);
178
+ void user.click(menu);
170
179
  });
171
180
  await waitFor(() => {
172
181
  expect(screen.queryByRole('menu')).not.toBeInTheDocument();
@@ -200,13 +209,13 @@ describe('HitContextMenu', () => {
200
209
  });
201
210
  it('should disable "Open Hit" when hit is null', async () => {
202
211
  act(() => {
203
- mockHitContext.hits['test-hit-1'] = null;
212
+ mockRecordContext.records['test-hit-1'] = null;
204
213
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
205
214
  fireEvent.contextMenu(contextMenuWrapper);
206
215
  });
207
216
  await waitFor(() => {
208
217
  const menuItems = screen.getAllByRole('menuitem');
209
- const openHitItem = menuItems.find(item => item.textContent?.toLowerCase().includes('open hit viewer'));
218
+ const openHitItem = menuItems.find(item => item.textContent?.toLowerCase().includes('open hit'));
210
219
  expect(openHitItem).toHaveAttribute('aria-disabled', 'true');
211
220
  });
212
221
  });
@@ -247,7 +256,7 @@ describe('HitContextMenu', () => {
247
256
  skip_rationale: false
248
257
  }
249
258
  }));
250
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
259
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
251
260
  act(() => {
252
261
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
253
262
  fireEvent.contextMenu(contextMenuWrapper);
@@ -305,7 +314,7 @@ describe('HitContextMenu', () => {
305
314
  createMockAction({ action_id: 'action-2', name: 'Custom Action 2' })
306
315
  ];
307
316
  mockDispatchApi.mockResolvedValue({ items: mockActions });
308
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
317
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
309
318
  act(() => {
310
319
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
311
320
  fireEvent.contextMenu(contextMenuWrapper);
@@ -349,7 +358,7 @@ describe('HitContextMenu', () => {
349
358
  });
350
359
  it('should disable custom actions menu when no actions are available', async () => {
351
360
  mockDispatchApi.mockResolvedValueOnce({ items: [] });
352
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
361
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
353
362
  act(() => {
354
363
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
355
364
  fireEvent.contextMenu(contextMenuWrapper);
@@ -391,7 +400,7 @@ describe('HitContextMenu', () => {
391
400
  skip_rationale: true
392
401
  }
393
402
  }));
394
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
403
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
395
404
  act(() => {
396
405
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
397
406
  fireEvent.contextMenu(contextMenuWrapper);
@@ -456,10 +465,45 @@ describe('HitContextMenu', () => {
456
465
  expect(mockExecuteAction).toHaveBeenCalled();
457
466
  });
458
467
  });
459
- it('should call executeAction with action_id and hit query', async () => {
468
+ it('should call executeAction with action_id and hit query for a single hit', async () => {
469
+ const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action' })];
470
+ mockDispatchApi.mockResolvedValue({ items: mockActions });
471
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
472
+ act(() => {
473
+ const contextMenuWrapper = screen.getByText('Test Content').parentElement;
474
+ fireEvent.contextMenu(contextMenuWrapper);
475
+ });
476
+ let actionsMenuItem;
477
+ await waitFor(() => {
478
+ actionsMenuItem = screen.getByText('Run Action');
479
+ expect(actionsMenuItem).toBeInTheDocument();
480
+ });
481
+ act(() => {
482
+ fireEvent.mouseEnter(actionsMenuItem);
483
+ });
484
+ await waitFor(() => {
485
+ expect(screen.getByTestId('actions-submenu')).toBeInTheDocument();
486
+ });
487
+ await act(async () => {
488
+ const customActionOption = screen.getByText('Custom Action');
489
+ await user.click(customActionOption);
490
+ });
491
+ await waitFor(() => {
492
+ expect(mockExecuteAction).toHaveBeenCalledWith('action-1', 'howler.id:(test-hit-1)');
493
+ });
494
+ });
495
+ it('should call executeAction with combined hit query when multiple hits are selected', async () => {
460
496
  const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action' })];
461
497
  mockDispatchApi.mockResolvedValue({ items: mockActions });
462
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
498
+ const hit1 = createMockHit({ howler: { id: 'hit-1' } });
499
+ const hit2 = createMockHit({ howler: { id: 'hit-2' } });
500
+ const hit3 = createMockHit({ howler: { id: 'hit-3' } });
501
+ mockRecordContext.records['hit-1'] = hit1;
502
+ mockRecordContext.records['hit-2'] = hit2;
503
+ mockRecordContext.records['hit-3'] = hit3;
504
+ mockRecordContext.selectedRecords = [hit1, hit2, hit3];
505
+ mockGetSelectedId.mockReturnValue('hit-1');
506
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
463
507
  act(() => {
464
508
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
465
509
  fireEvent.contextMenu(contextMenuWrapper);
@@ -480,7 +524,7 @@ describe('HitContextMenu', () => {
480
524
  await user.click(customActionOption);
481
525
  });
482
526
  await waitFor(() => {
483
- expect(mockExecuteAction).toHaveBeenCalledWith('action-1', 'howler.id:test-hit-1');
527
+ expect(mockExecuteAction).toHaveBeenCalledWith('action-1', 'howler.id:(hit-1 OR hit-2 OR hit-3)');
484
528
  });
485
529
  });
486
530
  it('should close menu after action execution', async () => {
@@ -512,7 +556,7 @@ describe('HitContextMenu', () => {
512
556
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
513
557
  keys: ['howler.detection', 'event.id']
514
558
  }));
515
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
559
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
516
560
  });
517
561
  it('should render exclusion submenu with template keys', async () => {
518
562
  act(() => {
@@ -560,7 +604,7 @@ describe('HitContextMenu', () => {
560
604
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
561
605
  keys: ['howler.outline.indicators']
562
606
  }));
563
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
607
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
564
608
  act(() => {
565
609
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
566
610
  fireEvent.contextMenu(contextMenuWrapper);
@@ -603,7 +647,7 @@ describe('HitContextMenu', () => {
603
647
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
604
648
  keys: []
605
649
  }));
606
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
650
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
607
651
  act(() => {
608
652
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
609
653
  fireEvent.contextMenu(contextMenuWrapper);
@@ -615,7 +659,7 @@ describe('HitContextMenu', () => {
615
659
  });
616
660
  it('should skip null field values in exclusion menu', async () => {
617
661
  act(() => {
618
- mockHitContext.hits['test-hit-1'].event = {};
662
+ mockRecordContext.records['test-hit-1'].event = {};
619
663
  });
620
664
  act(() => {
621
665
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -638,7 +682,7 @@ describe('HitContextMenu', () => {
638
682
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
639
683
  keys: ['howler.detection', 'event.id']
640
684
  }));
641
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
685
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
642
686
  });
643
687
  it('should render inclusion submenu with template keys', async () => {
644
688
  act(() => {
@@ -686,7 +730,7 @@ describe('HitContextMenu', () => {
686
730
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
687
731
  keys: ['howler.outline.indicators']
688
732
  }));
689
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
733
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
690
734
  act(() => {
691
735
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
692
736
  fireEvent.contextMenu(contextMenuWrapper);
@@ -729,7 +773,7 @@ describe('HitContextMenu', () => {
729
773
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
730
774
  keys: []
731
775
  }));
732
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
776
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
733
777
  act(() => {
734
778
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
735
779
  fireEvent.contextMenu(contextMenuWrapper);
@@ -741,7 +785,7 @@ describe('HitContextMenu', () => {
741
785
  });
742
786
  it('should skip null field values in inclusion menu', async () => {
743
787
  act(() => {
744
- mockHitContext.hits['test-hit-1'].event = {};
788
+ mockRecordContext.records['test-hit-1'].event = {};
745
789
  });
746
790
  act(() => {
747
791
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -760,24 +804,24 @@ describe('HitContextMenu', () => {
760
804
  });
761
805
  });
762
806
  describe('Multiple Hit Selection', () => {
763
- it('should use selectedHits when current hit is included', async () => {
807
+ it('should use selectedRecords when current hit is included', async () => {
764
808
  act(() => {
765
- mockHitContext.hits['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
766
- mockHitContext.hits['hit-2'] = createMockHit({ howler: { id: 'hit-2' } });
767
- mockHitContext.selectedHits.push(mockHitContext.hits['hit-1'], mockHitContext.hits['hit-2']);
809
+ mockRecordContext.records['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
810
+ mockRecordContext.records['hit-2'] = createMockHit({ howler: { id: 'hit-2' } });
811
+ mockRecordContext.selectedRecords.push(mockRecordContext.records['hit-1'], mockRecordContext.records['hit-2']);
768
812
  mockGetSelectedId.mockReturnValue('hit-1');
769
813
  });
770
814
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
771
815
  await user.pointer({ keys: '[MouseRight]', target: contextMenuWrapper });
772
- // The component should use selectedHits for actions
816
+ // The component should use selectedRecords for actions
773
817
  // We can verify this indirectly through the useHitActions hook receiving the right data
774
818
  expect(screen.getByRole('menu')).toBeInTheDocument();
775
819
  expect(mockGetSelectedId).toHaveBeenCalled();
776
820
  });
777
- it('should use only current hit when not in selectedHits', async () => {
821
+ it('should use only current hit when not in selectedRecords', async () => {
778
822
  act(() => {
779
- mockHitContext.hits['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
780
- mockHitContext.selectedHits.push(mockHitContext.hits['hit-1']);
823
+ mockRecordContext.records['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
824
+ mockRecordContext.selectedRecords.push(mockRecordContext.records['hit-1']);
781
825
  mockGetSelectedId.mockReturnValue('test-hit-1');
782
826
  });
783
827
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -796,12 +840,12 @@ describe('HitContextMenu', () => {
796
840
  });
797
841
  it('should call getMatchingAnalytic when hit has analytic', async () => {
798
842
  await waitFor(() => {
799
- expect(mockGetMatchingAnalytic).toHaveBeenCalledWith(mockHitContext.hits['test-hit-1']);
843
+ expect(mockGetMatchingAnalytic).toHaveBeenCalledWith(mockRecordContext.records['test-hit-1']);
800
844
  });
801
845
  });
802
846
  it('should call getMatchingTemplate when menu opens', async () => {
803
847
  await waitFor(() => {
804
- expect(mockGetMatchingTemplate).toHaveBeenCalledWith(mockHitContext.hits['test-hit-1']);
848
+ expect(mockGetMatchingTemplate).toHaveBeenCalledWith(mockRecordContext.records['test-hit-1']);
805
849
  });
806
850
  });
807
851
  it('should reset state when menu closes', async () => {
@@ -834,7 +878,7 @@ describe('HitContextMenu', () => {
834
878
  describe('Edge Cases and Error Handling', () => {
835
879
  it('should not crash when hit is null', async () => {
836
880
  act(() => {
837
- mockHitContext.hits = {};
881
+ mockRecordContext.records = {};
838
882
  });
839
883
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
840
884
  fireEvent.contextMenu(contextMenuWrapper);
@@ -845,7 +889,7 @@ describe('HitContextMenu', () => {
845
889
  });
846
890
  it('should not render exclusion menu when template is null', async () => {
847
891
  mockGetMatchingTemplate.mockResolvedValue(null);
848
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
892
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
849
893
  act(() => {
850
894
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
851
895
  fireEvent.contextMenu(contextMenuWrapper);
@@ -859,7 +903,7 @@ describe('HitContextMenu', () => {
859
903
  });
860
904
  it('should not render inclusion menu when template is null', async () => {
861
905
  mockGetMatchingTemplate.mockResolvedValue(null);
862
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
906
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
863
907
  act(() => {
864
908
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
865
909
  fireEvent.contextMenu(contextMenuWrapper);
@@ -873,7 +917,7 @@ describe('HitContextMenu', () => {
873
917
  });
874
918
  it('should handle API failure gracefully', async () => {
875
919
  mockDispatchApi.mockResolvedValue(null);
876
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
920
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
877
921
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
878
922
  fireEvent.contextMenu(contextMenuWrapper);
879
923
  await waitFor(() => {
@@ -884,7 +928,7 @@ describe('HitContextMenu', () => {
884
928
  });
885
929
  it('should not call getMatchingAnalytic or getMatchingTemplate when hit has no analytic', async () => {
886
930
  act(() => {
887
- mockHitContext.hits['test-hit-1'].howler.analytic = null;
931
+ mockRecordContext.records['test-hit-1'].howler.analytic = null;
888
932
  });
889
933
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
890
934
  fireEvent.contextMenu(contextMenuWrapper);
@@ -903,6 +947,56 @@ describe('HitContextMenu', () => {
903
947
  expect(mockPluginStoreExecuteFunction).toHaveBeenCalled();
904
948
  });
905
949
  });
950
+ describe('Add to Case Menu Item', () => {
951
+ it('should render "Add to Case" item in the menu', async () => {
952
+ act(() => {
953
+ const contextMenuWrapper = screen.getByText('Test Content').parentElement;
954
+ fireEvent.contextMenu(contextMenuWrapper);
955
+ });
956
+ await waitFor(() => {
957
+ expect(screen.getByText('Add to Case')).toBeInTheDocument();
958
+ });
959
+ });
960
+ it('should enable "Add to Case" when a record is present', async () => {
961
+ act(() => {
962
+ const contextMenuWrapper = screen.getByText('Test Content').parentElement;
963
+ fireEvent.contextMenu(contextMenuWrapper);
964
+ });
965
+ await waitFor(() => {
966
+ const menuItems = screen.getAllByRole('menuitem');
967
+ const addToCaseItem = menuItems.find(item => item.textContent?.includes('Add to Case'));
968
+ expect(addToCaseItem).toHaveAttribute('aria-disabled', 'false');
969
+ });
970
+ });
971
+ it('should disable "Add to Case" when record is null', async () => {
972
+ act(() => {
973
+ mockRecordContext.records['test-hit-1'] = null;
974
+ const contextMenuWrapper = screen.getByText('Test Content').parentElement;
975
+ fireEvent.contextMenu(contextMenuWrapper);
976
+ });
977
+ await waitFor(() => {
978
+ const menuItems = screen.getAllByRole('menuitem');
979
+ const addToCaseItem = menuItems.find(item => item.textContent?.includes('Add to Case'));
980
+ expect(addToCaseItem).toHaveAttribute('aria-disabled', 'true');
981
+ });
982
+ });
983
+ it('should call showModal with an AddToCaseModal element when clicked', async () => {
984
+ act(() => {
985
+ const contextMenuWrapper = screen.getByText('Test Content').parentElement;
986
+ fireEvent.contextMenu(contextMenuWrapper);
987
+ });
988
+ await waitFor(() => {
989
+ expect(screen.getByText('Add to Case')).toBeInTheDocument();
990
+ });
991
+ await act(async () => {
992
+ await user.click(screen.getByText('Add to Case'));
993
+ });
994
+ await waitFor(() => {
995
+ expect(mockShowModal).toHaveBeenCalledOnce();
996
+ expect(mockShowModal).toHaveBeenCalledWith(expect.objectContaining({ type: expect.any(Function) }), expect.objectContaining({ maxHeight: expect.any(String) }));
997
+ });
998
+ });
999
+ });
906
1000
  describe('Role-Based Action Permissions', () => {
907
1001
  afterEach(() => {
908
1002
  // Reset to default user with required roles
@@ -922,7 +1016,7 @@ describe('HitContextMenu', () => {
922
1016
  });
923
1017
  const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action 1' })];
924
1018
  mockDispatchApi.mockResolvedValue({ items: mockActions });
925
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
1019
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
926
1020
  act(() => {
927
1021
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
928
1022
  fireEvent.contextMenu(contextMenuWrapper);
@@ -941,7 +1035,7 @@ describe('HitContextMenu', () => {
941
1035
  });
942
1036
  const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action 1' })];
943
1037
  mockDispatchApi.mockResolvedValue({ items: mockActions });
944
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
1038
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
945
1039
  act(() => {
946
1040
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
947
1041
  fireEvent.contextMenu(contextMenuWrapper);
@@ -960,7 +1054,7 @@ describe('HitContextMenu', () => {
960
1054
  });
961
1055
  const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action 1' })];
962
1056
  mockDispatchApi.mockResolvedValue({ items: mockActions });
963
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
1057
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
964
1058
  act(() => {
965
1059
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
966
1060
  fireEvent.contextMenu(contextMenuWrapper);
@@ -978,7 +1072,7 @@ describe('HitContextMenu', () => {
978
1072
  }
979
1073
  });
980
1074
  mockDispatchApi.mockResolvedValue({ items: [] });
981
- rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
1075
+ rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
982
1076
  act(() => {
983
1077
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
984
1078
  fireEvent.contextMenu(contextMenuWrapper);
@@ -0,0 +1,7 @@
1
+ import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
2
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
+ import { type FC } from 'react';
4
+ declare const RecordRelated: FC<{
5
+ record: Hit | Event;
6
+ }>;
7
+ export default RecordRelated;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Stack, Tab, Tabs, useTheme } from '@mui/material';
3
+ import EventCard from '@cccsaurora/howler-ui/components/elements/event/EventCard';
4
+ import useRelatedRecords from '@cccsaurora/howler-ui/components/hooks/useRelatedRecords';
5
+ import { groupBy } from 'lodash-es';
6
+ import { useMemo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Link } from 'react-router-dom';
9
+ import { isCase, isEvent, isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
10
+ import CaseCard from '../case/CaseCard';
11
+ import HitCard from '../hit/HitCard';
12
+ import { HitLayout } from '../hit/HitLayout';
13
+ import RelatedLink from '../hit/related/RelatedLink';
14
+ const RecordRelated = ({ record }) => {
15
+ const theme = useTheme();
16
+ const { t } = useTranslation();
17
+ const related = useMemo(() => record?.howler.related ?? [], [record?.howler.related]);
18
+ const records = useRelatedRecords(related, related.length > 0);
19
+ const groups = groupBy(records, '__index');
20
+ const hasLinks = (record?.howler.links?.length ?? 0) > 0;
21
+ const tabs = [
22
+ hasLinks && 'links',
23
+ groups.hit?.length > 0 && 'hit',
24
+ groups.case?.length > 0 && 'case',
25
+ groups.event?.length > 0 && 'event'
26
+ ].filter(Boolean);
27
+ const [activeTab, setActiveTab] = useState(false);
28
+ const currentTab = activeTab !== false && tabs.includes(activeTab) ? activeTab : (tabs[0] ?? false);
29
+ if (!record) {
30
+ return null;
31
+ }
32
+ return (_jsxs(Box, { sx: { borderTop: `thin solid ${theme.palette.divider}`, height: '100%', flex: 1, mr: 2, pb: 2 }, children: [_jsxs(Tabs, { value: currentTab, onChange: (_, v) => setActiveTab(v), variant: "scrollable", scrollButtons: "auto", children: [hasLinks && _jsx(Tab, { value: "links", label: t('hit.related.tab.links') }), groups.hit?.length > 0 && _jsx(Tab, { value: "hit", label: t('hit.related.tab.hit') }), groups.case?.length > 0 && _jsx(Tab, { value: "case", label: t('hit.related.tab.case') }), groups.event?.length > 0 && _jsx(Tab, { value: "event", label: t('hit.related.tab.event') })] }), currentTab === 'links' && (_jsx(Box, { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 1, pt: 1, children: record.howler.links.map(l => (_jsx(RelatedLink, { ...l }, l.title + l.href))) })), currentTab === 'hit' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isHit).map(h => (_jsx(Link, { to: `/hits/${h.howler.id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(HitCard, { id: h.howler.id, layout: HitLayout.NORMAL }) }, h.howler.id))) })), currentTab === 'case' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isCase).map(c => (_jsx(Link, { to: `/cases/${c.case_id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(CaseCard, { case: c }) }, c.case_id))) })), currentTab === 'event' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isEvent).map(o => (_jsx(Link, { to: `/events/${o.howler.id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(EventCard, { event: o }) }, o.howler.id))) }))] }));
33
+ };
34
+ export default RecordRelated;
@@ -1,10 +1,11 @@
1
1
  import type { HowlerUser } from '@cccsaurora/howler-ui/models/entities/HowlerUser';
2
+ import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
2
3
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
4
  import type { FC } from 'react';
4
- declare const HitWorklog: FC<{
5
- hit: Hit;
5
+ declare const RecordWorklog: FC<{
6
+ record: Hit | Event;
6
7
  users: {
7
8
  [id: string]: HowlerUser;
8
9
  };
9
10
  }>;
10
- export default HitWorklog;
11
+ export default RecordWorklog;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { KeyboardArrowUp } from '@mui/icons-material';
3
3
  import { CardContent, CardHeader, Divider, Skeleton, Stack, Tooltip, Typography, useTheme } from '@mui/material';
4
4
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
@@ -10,7 +10,7 @@ import { compareTimestamp, twitterShort } from '@cccsaurora/howler-ui/utils/util
10
10
  import HowlerAvatar from '../display/HowlerAvatar';
11
11
  import HowlerCard from '../display/HowlerCard';
12
12
  import Markdown from '../display/Markdown';
13
- const HitWorklog = ({ hit, users }) => {
13
+ const RecordWorklog = ({ record, users }) => {
14
14
  const theme = useTheme();
15
15
  const { shiftColor } = useMyUtils();
16
16
  const { t } = useTranslation();
@@ -23,15 +23,15 @@ const HitWorklog = ({ hit, users }) => {
23
23
  */
24
24
  const worklogGroups = useMemo(() => {
25
25
  let setInitialVersion = false;
26
- return (hit?.howler?.log || [])
26
+ return (record?.howler?.log || [])
27
27
  .slice()
28
28
  .sort((a, b) => compareTimestamp(b.timestamp, a.timestamp))
29
29
  .reduce((acc, l) => {
30
- if (!initialVersions[hit.howler.id] && !setInitialVersion) {
30
+ if (!initialVersions[record.howler.id] && !setInitialVersion) {
31
31
  setInitialVersion = true;
32
32
  setInitialVersions({
33
33
  ...initialVersions,
34
- [hit.howler.id]: l.previous_version
34
+ [record.howler.id]: l.previous_version
35
35
  });
36
36
  }
37
37
  // Initialize the worklog card groups
@@ -42,9 +42,9 @@ const HitWorklog = ({ hit, users }) => {
42
42
  const currArr = acc[acc.length - 1];
43
43
  if (
44
44
  // Does this log version match the saved version?
45
- l.previous_version === initialVersions[hit.howler.id] &&
45
+ l.previous_version === initialVersions[record.howler.id] &&
46
46
  // Does the previous entry not match?
47
- currArr[currArr.length - 1].previous_version !== initialVersions[hit.howler.id]) {
47
+ currArr[currArr.length - 1].previous_version !== initialVersions[record.howler.id]) {
48
48
  // If so, we've figured out where the new logs should start, so we start a new card.
49
49
  acc.push([l]);
50
50
  return acc;
@@ -59,14 +59,14 @@ const HitWorklog = ({ hit, users }) => {
59
59
  }, []);
60
60
  },
61
61
  // eslint-disable-next-line react-hooks/exhaustive-deps
62
- [hit?.howler?.log]);
62
+ [record?.howler?.log]);
63
63
  useEffect(() => {
64
64
  // On unmount, mark the latest entry version as the last seen version.
65
65
  return () => {
66
- if (hit?.howler.id) {
66
+ if (record?.howler.id) {
67
67
  setInitialVersions({
68
68
  ...initialVersions,
69
- [hit.howler.id]: worklogGroups[0][0]?.previous_version ?? initialVersions[hit.howler.id]
69
+ [record.howler.id]: worklogGroups[0][0]?.previous_version ?? initialVersions[record.howler.id]
70
70
  });
71
71
  }
72
72
  };
@@ -77,7 +77,9 @@ const HitWorklog = ({ hit, users }) => {
77
77
  if (worklogGroups.length > 0) {
78
78
  return worklogGroups.flatMap((ls, index) => {
79
79
  const result = [];
80
- if (index > 0 && initialVersions[hit.howler.id] && ls[0].previous_version === initialVersions[hit.howler.id]) {
80
+ if (index > 0 &&
81
+ initialVersions[record.howler.id] &&
82
+ ls[0].previous_version === initialVersions[record.howler.id]) {
81
83
  result.push(_jsx(Divider, { children: _jsxs(Stack, { direction: "row", children: [_jsx(KeyboardArrowUp, { sx: { color: 'text.secondary' }, fontSize: "small" }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('hit.worklog.new') }), _jsx(KeyboardArrowUp, { sx: { color: 'text.secondary' }, fontSize: "small" })] }) }, "new"));
82
84
  }
83
85
  result.push(_jsxs(HowlerCard, { elevation: 4, children: [_jsx(CardHeader, { avatar: _jsx(HowlerAvatar, { userId: ls[0].user }), title: users[ls[0].user]?.name ?? ls[0].user, subheader: _jsx(Tooltip, { title: new Date(ls[0].timestamp).toLocaleString(), children: _jsx(Typography, { variant: "caption", children: twitterShort(ls[0].timestamp) }) }) }), _jsx(CardContent, { children: _jsx(Stack, { spacing: 1, divider: _jsx(Divider, { orientation: "horizontal" }), children: ls.map(l => (_jsxs(Typography, { variant: "body2", color: "text.secondary", component: "div", position: "relative", children: [l.explanation ? (_jsx(Markdown, { md: l.explanation.trim() })) : (_jsxs(_Fragment, { children: [_jsxs("span", { children: [t('hit.worklog.updated'), "\u00A0"] }), _jsx("code", { children: l.key }), _jsx("span", { children: ":\u00A0" }), {
@@ -88,10 +90,10 @@ const HitWorklog = ({ hit, users }) => {
88
90
  return result;
89
91
  });
90
92
  }
91
- else if (!hit?.howler) {
93
+ else if (!record?.howler) {
92
94
  return (_jsxs(_Fragment, { children: [_jsx(Skeleton, { width: "100%", height: 200, variant: "rounded" }), _jsx(Skeleton, { width: "100%", height: 220, variant: "rounded" }), _jsx(Skeleton, { width: "100%", height: 150, variant: "rounded" })] }));
93
95
  }
94
- }, [worklogGroups, hit.howler, initialVersions, users, t, shiftColor, theme.palette.text.primary]);
96
+ }, [worklogGroups, record.howler, initialVersions, users, t, shiftColor, theme.palette.text.primary]);
95
97
  return (_jsx(Stack, { sx: { p: 2 }, spacing: 1, children: worklogEls }));
96
98
  };
97
- export default HitWorklog;
99
+ export default RecordWorklog;
@@ -0,0 +1,7 @@
1
+ import type { FC } from 'react';
2
+ export type FuzzySearchBarProps = {
3
+ onSearch: (query: string, indexes: string[]) => void;
4
+ loading?: boolean;
5
+ };
6
+ declare const FuzzySearchBar: FC<FuzzySearchBarProps>;
7
+ export default FuzzySearchBar;