@cccsaurora/howler-ui 2.19.0-dev.1181 → 2.19.0-dev.1264

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 (419) hide show
  1. package/api/analytic/index.d.ts +2 -3
  2. package/api/analytic/index.js +1 -2
  3. package/api/index.d.ts +4 -0
  4. package/api/index.js +10 -2
  5. package/api/search/case.d.ts +4 -0
  6. package/api/search/case.js +8 -0
  7. package/api/search/facet/hit.d.ts +1 -3
  8. package/api/search/facet/index.d.ts +3 -1
  9. package/api/search/index.d.ts +3 -1
  10. package/api/search/index.js +2 -1
  11. package/api/socket/index.d.ts +3 -0
  12. package/api/socket/index.js +6 -0
  13. package/api/socket/viewers.d.ts +2 -0
  14. package/api/socket/viewers.js +8 -0
  15. package/api/socket/viewers.test.js +44 -0
  16. package/api/v2/case/index.d.ts +9 -0
  17. package/api/v2/case/index.js +21 -0
  18. package/api/v2/case/items.d.ts +9 -0
  19. package/api/v2/case/items.js +19 -0
  20. package/api/v2/case/rules.d.ts +6 -0
  21. package/api/v2/case/rules.js +18 -0
  22. package/api/v2/fuzzy.d.ts +17 -0
  23. package/api/v2/fuzzy.js +11 -0
  24. package/api/v2/index.d.ts +5 -0
  25. package/api/v2/index.js +7 -0
  26. package/api/v2/search/facet.d.ts +3 -0
  27. package/api/v2/search/facet.js +12 -0
  28. package/api/v2/search/index.d.ts +6 -0
  29. package/api/v2/search/index.js +24 -0
  30. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  31. package/components/app/App.js +49 -11
  32. package/components/app/hooks/useMatchers.d.ts +1 -1
  33. package/components/app/hooks/useMatchers.js +23 -11
  34. package/components/app/hooks/useMatchers.test.js +22 -22
  35. package/components/app/hooks/useTitle.js +5 -5
  36. package/components/app/providers/FavouritesProvider.js +2 -2
  37. package/components/app/providers/GridColumnsProvider.js +2 -2
  38. package/components/app/providers/GridColumnsProvider.test.js +3 -3
  39. package/components/app/providers/ModalProvider.d.ts +1 -0
  40. package/components/app/providers/ParameterProvider.d.ts +25 -3
  41. package/components/app/providers/ParameterProvider.js +163 -226
  42. package/components/app/providers/ParameterProvider.test.js +358 -94
  43. package/components/app/providers/RecordProvider.d.ts +23 -0
  44. package/components/app/providers/{HitProvider.js → RecordProvider.js} +46 -42
  45. package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
  46. package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +13 -19
  47. package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +52 -71
  48. package/components/app/providers/SearchResponseProvider.d.ts +4 -4
  49. package/components/app/providers/SearchResponseProvider.js +1 -1
  50. package/components/app/providers/SocketProvider.d.ts +11 -2
  51. package/components/app/providers/SocketProvider.js +18 -5
  52. package/components/app/providers/UserListProvider.js +28 -8
  53. package/components/app/providers/UserListProvider.test.js +150 -0
  54. package/components/elements/ContextMenu.d.ts +56 -0
  55. package/components/elements/ContextMenu.js +109 -0
  56. package/components/elements/ContextMenu.test.d.ts +1 -0
  57. package/components/elements/ContextMenu.test.js +215 -0
  58. package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
  59. package/components/elements/ObjectDetails.d.ts +6 -0
  60. package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
  61. package/components/elements/PluginChip.d.ts +1 -1
  62. package/components/elements/PluginChip.js +0 -1
  63. package/components/elements/PluginTypography.d.ts +2 -1
  64. package/components/elements/PluginTypography.js +3 -2
  65. package/components/elements/UserList.d.ts +5 -2
  66. package/components/elements/UserList.js +33 -16
  67. package/components/elements/UserList.test.d.ts +1 -0
  68. package/components/elements/UserList.test.js +122 -0
  69. package/components/elements/addons/lists/TuiListElement.d.ts +1 -1
  70. package/components/elements/addons/lists/TuiListElement.js +0 -1
  71. package/components/elements/addons/search/SearchTotal.js +1 -1
  72. package/components/elements/addons/search/SearchTotal.test.js +2 -2
  73. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  74. package/components/elements/case/CaseCard.d.ts +12 -0
  75. package/components/elements/case/CaseCard.js +43 -0
  76. package/components/elements/case/CasePreview.d.ts +6 -0
  77. package/components/elements/case/CasePreview.js +17 -0
  78. package/components/elements/case/StatusIcon.d.ts +5 -0
  79. package/components/elements/case/StatusIcon.js +13 -0
  80. package/components/elements/display/ChipPopper.d.ts +1 -1
  81. package/components/elements/display/ChipPopper.js +6 -5
  82. package/components/elements/display/Classification.js +1 -1
  83. package/components/elements/display/ClassificationChip.d.ts +14 -0
  84. package/components/elements/display/ClassificationChip.js +36 -0
  85. package/components/elements/display/HowlerCard.js +1 -1
  86. package/components/elements/display/HowlerCard.test.js +5 -9
  87. package/components/elements/display/ItemManager.js +2 -2
  88. package/components/elements/display/Modal.js +2 -0
  89. package/components/elements/display/QueryResultText.d.ts +1 -0
  90. package/components/elements/display/QueryResultText.js +2 -2
  91. package/components/elements/event/EventCard.d.ts +6 -0
  92. package/components/elements/event/EventCard.js +22 -0
  93. package/components/elements/event/EventPreview.d.ts +6 -0
  94. package/components/elements/event/EventPreview.js +12 -0
  95. package/components/elements/hit/HitActions.js +4 -4
  96. package/components/elements/hit/HitBanner.d.ts +1 -0
  97. package/components/elements/hit/HitBanner.js +31 -49
  98. package/components/elements/hit/HitCard.d.ts +4 -0
  99. package/components/elements/hit/HitCard.js +7 -7
  100. package/components/elements/hit/HitLabels.js +2 -2
  101. package/components/elements/hit/HitLabels.test.js +2 -2
  102. package/components/elements/hit/HitOutline.d.ts +1 -0
  103. package/components/elements/hit/HitOutline.js +3 -3
  104. package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
  105. package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +10 -4
  106. package/components/elements/hit/HitSummary.d.ts +2 -1
  107. package/components/elements/hit/HitSummary.js +6 -5
  108. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  109. package/components/elements/hit/elements/AnalyticLink.d.ts +9 -0
  110. package/components/elements/hit/elements/AnalyticLink.js +22 -0
  111. package/components/elements/hit/elements/{HitUsers.d.ts → Assigned.d.ts} +3 -4
  112. package/components/elements/hit/elements/Assigned.js +36 -0
  113. package/components/elements/hit/elements/Assigned.test.d.ts +1 -0
  114. package/components/elements/hit/elements/Assigned.test.js +65 -0
  115. package/components/elements/hit/grid/AddColumnModal.js +5 -4
  116. package/components/elements/hit/grid/EnhancedCell.d.ts +2 -1
  117. package/components/elements/hit/grid/EnhancedCell.js +2 -2
  118. package/components/elements/hit/grid/{HitRow.d.ts → RecordRow.d.ts} +3 -2
  119. package/components/elements/hit/grid/{HitRow.js → RecordRow.js} +11 -9
  120. package/components/elements/hit/grid/{HitTable.d.ts → RecordTable.d.ts} +5 -4
  121. package/components/elements/hit/grid/{HitTable.js → RecordTable.js} +13 -11
  122. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  123. package/components/elements/hit/related/RelatedRecords.js +63 -0
  124. package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
  125. package/components/elements/{hit/HitComments.js → record/RecordComments.js} +29 -28
  126. package/components/elements/{hit/HitContextMenu.d.ts → record/RecordContextMenu.d.ts} +3 -3
  127. package/components/elements/record/RecordContextMenu.js +268 -0
  128. package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
  129. package/components/elements/{hit/HitContextMenu.test.js → record/RecordContextMenu.test.js} +98 -43
  130. package/components/elements/record/RecordRelated.d.ts +7 -0
  131. package/components/elements/record/RecordRelated.js +34 -0
  132. package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
  133. package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +15 -13
  134. package/components/elements/search/FuzzySearchBar.d.ts +7 -0
  135. package/components/elements/search/FuzzySearchBar.js +33 -0
  136. package/components/elements/search/FuzzySearchBar.test.d.ts +1 -0
  137. package/components/elements/search/FuzzySearchBar.test.js +87 -0
  138. package/components/elements/view/ViewTitle.d.ts +1 -0
  139. package/components/elements/view/ViewTitle.js +9 -2
  140. package/components/hooks/useHitActions.d.ts +1 -1
  141. package/components/hooks/useHitActions.js +4 -4
  142. package/components/hooks/useMyPreferences.js +10 -1
  143. package/components/hooks/useMySearch.js +2 -2
  144. package/components/hooks/useMySitemap.js +4 -1
  145. package/components/hooks/useMyTheme.js +9 -2
  146. package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
  147. package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
  148. package/components/hooks/useRelatedRecords.d.ts +13 -0
  149. package/components/hooks/useRelatedRecords.js +32 -0
  150. package/components/routes/action/edit/ActionEditor.js +2 -2
  151. package/components/routes/action/view/ActionSearch.js +1 -1
  152. package/components/routes/advanced/QueryBuilder.js +4 -27
  153. package/components/routes/advanced/QueryEditor.js +3 -3
  154. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  155. package/components/routes/analytics/AnalyticDetails.js +6 -54
  156. package/components/routes/analytics/AnalyticSearch.js +9 -18
  157. package/components/routes/cases/CaseViewer.d.ts +2 -0
  158. package/components/routes/cases/CaseViewer.js +44 -0
  159. package/components/routes/cases/CaseViewer.test.d.ts +1 -0
  160. package/components/routes/cases/CaseViewer.test.js +133 -0
  161. package/components/routes/cases/Cases.d.ts +2 -0
  162. package/components/routes/cases/Cases.js +140 -0
  163. package/components/routes/cases/constants.d.ts +6 -0
  164. package/components/routes/cases/constants.js +6 -0
  165. package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
  166. package/components/routes/cases/detail/AlertPanel.js +33 -0
  167. package/components/routes/cases/detail/AlertPanel.test.d.ts +1 -0
  168. package/components/routes/cases/detail/AlertPanel.test.js +107 -0
  169. package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
  170. package/components/routes/cases/detail/CaseDashboard.js +66 -0
  171. package/components/routes/cases/detail/CaseDashboard.test.d.ts +1 -0
  172. package/components/routes/cases/detail/CaseDashboard.test.js +218 -0
  173. package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
  174. package/components/routes/cases/detail/CaseDetails.js +79 -0
  175. package/components/routes/cases/detail/CaseDetails.test.d.ts +1 -0
  176. package/components/routes/cases/detail/CaseDetails.test.js +145 -0
  177. package/components/routes/cases/detail/CaseObservables.d.ts +6 -0
  178. package/components/routes/cases/detail/CaseObservables.js +142 -0
  179. package/components/routes/cases/detail/CaseObservables.test.d.ts +1 -0
  180. package/components/routes/cases/detail/CaseObservables.test.js +169 -0
  181. package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
  182. package/components/routes/cases/detail/CaseOverview.js +43 -0
  183. package/components/routes/cases/detail/CaseRules.d.ts +7 -0
  184. package/components/routes/cases/detail/CaseRules.js +55 -0
  185. package/components/routes/cases/detail/CaseRules.test.d.ts +1 -0
  186. package/components/routes/cases/detail/CaseRules.test.js +238 -0
  187. package/components/routes/cases/detail/CaseSearch.d.ts +3 -0
  188. package/components/routes/cases/detail/CaseSearch.js +78 -0
  189. package/components/routes/cases/detail/CaseSearch.test.d.ts +1 -0
  190. package/components/routes/cases/detail/CaseSearch.test.js +154 -0
  191. package/components/routes/cases/detail/CaseSidebar.d.ts +8 -0
  192. package/components/routes/cases/detail/CaseSidebar.js +147 -0
  193. package/components/routes/cases/detail/CaseSidebar.test.d.ts +1 -0
  194. package/components/routes/cases/detail/CaseSidebar.test.js +155 -0
  195. package/components/routes/cases/detail/CaseTask.d.ts +14 -0
  196. package/components/routes/cases/detail/CaseTask.js +71 -0
  197. package/components/routes/cases/detail/CaseTask.test.d.ts +1 -0
  198. package/components/routes/cases/detail/CaseTask.test.js +143 -0
  199. package/components/routes/cases/detail/CaseTimeline.d.ts +12 -0
  200. package/components/routes/cases/detail/CaseTimeline.js +106 -0
  201. package/components/routes/cases/detail/CaseTimeline.test.d.ts +1 -0
  202. package/components/routes/cases/detail/CaseTimeline.test.js +320 -0
  203. package/components/routes/cases/detail/CreateRuleDialog.d.ts +9 -0
  204. package/components/routes/cases/detail/CreateRuleDialog.js +145 -0
  205. package/components/routes/cases/detail/CreateRuleDialog.test.d.ts +1 -0
  206. package/components/routes/cases/detail/CreateRuleDialog.test.js +284 -0
  207. package/components/routes/cases/detail/ItemPage.d.ts +6 -0
  208. package/components/routes/cases/detail/ItemPage.js +100 -0
  209. package/components/routes/cases/detail/ItemPage.test.d.ts +1 -0
  210. package/components/routes/cases/detail/ItemPage.test.js +238 -0
  211. package/components/routes/cases/detail/MarkdownPage.d.ts +8 -0
  212. package/components/routes/cases/detail/MarkdownPage.js +63 -0
  213. package/components/routes/cases/detail/MarkdownPage.test.d.ts +1 -0
  214. package/components/routes/cases/detail/MarkdownPage.test.js +116 -0
  215. package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
  216. package/components/routes/cases/detail/RelatedCasePanel.js +34 -0
  217. package/components/routes/cases/detail/RelatedCasePanel.test.d.ts +1 -0
  218. package/components/routes/cases/detail/RelatedCasePanel.test.js +115 -0
  219. package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
  220. package/components/routes/cases/detail/TaskPanel.js +95 -0
  221. package/components/routes/cases/detail/TaskPanel.test.d.ts +1 -0
  222. package/components/routes/cases/detail/TaskPanel.test.js +145 -0
  223. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +11 -0
  224. package/components/routes/cases/detail/aggregates/CaseAggregate.js +24 -0
  225. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
  226. package/components/routes/cases/detail/aggregates/SourceAggregate.js +26 -0
  227. package/components/routes/cases/detail/observables/Observable.d.ts +8 -0
  228. package/components/routes/cases/detail/observables/Observable.js +13 -0
  229. package/components/routes/cases/detail/observables/Observable.test.d.ts +1 -0
  230. package/components/routes/cases/detail/observables/Observable.test.js +72 -0
  231. package/components/routes/cases/detail/observables/ObservableTable.d.ts +7 -0
  232. package/components/routes/cases/detail/observables/ObservableTable.js +61 -0
  233. package/components/routes/cases/detail/observables/ObservableTable.test.d.ts +1 -0
  234. package/components/routes/cases/detail/observables/ObservableTable.test.js +85 -0
  235. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +22 -0
  236. package/components/routes/cases/detail/sidebar/CaseFolder.js +105 -0
  237. package/components/routes/cases/detail/sidebar/CaseFolder.test.d.ts +1 -0
  238. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +98 -0
  239. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +32 -0
  240. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +99 -0
  241. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +1 -0
  242. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +117 -0
  243. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +25 -0
  244. package/components/routes/cases/detail/sidebar/FolderEntry.js +89 -0
  245. package/components/routes/cases/detail/sidebar/FolderEntry.test.d.ts +1 -0
  246. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +81 -0
  247. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +5 -0
  248. package/components/routes/cases/detail/sidebar/RootDropZone.js +33 -0
  249. package/components/routes/cases/detail/sidebar/types.d.ts +9 -0
  250. package/components/routes/cases/detail/sidebar/utils.d.ts +10 -0
  251. package/components/routes/cases/detail/sidebar/utils.js +51 -0
  252. package/components/routes/cases/detail/sidebar/utils.test.d.ts +1 -0
  253. package/components/routes/cases/detail/sidebar/utils.test.js +82 -0
  254. package/components/routes/cases/detail/types.d.ts +24 -0
  255. package/components/routes/cases/detail/utils.d.ts +29 -0
  256. package/components/routes/cases/detail/utils.js +114 -0
  257. package/components/routes/cases/detail/utils.test.d.ts +1 -0
  258. package/components/routes/cases/detail/utils.test.js +144 -0
  259. package/components/routes/cases/hooks/useCase.d.ts +13 -0
  260. package/components/routes/cases/hooks/useCase.js +69 -0
  261. package/components/routes/cases/hooks/useCase.test.d.ts +1 -0
  262. package/components/routes/cases/hooks/useCase.test.js +141 -0
  263. package/components/routes/cases/modals/AddRecordToCaseModal.d.ts +7 -0
  264. package/components/routes/cases/modals/AddRecordToCaseModal.js +57 -0
  265. package/components/routes/cases/modals/AddRecordToCaseModal.test.d.ts +1 -0
  266. package/components/routes/cases/modals/AddRecordToCaseModal.test.js +309 -0
  267. package/components/routes/cases/modals/AddToCaseModal.d.ts +9 -0
  268. package/components/routes/cases/modals/AddToCaseModal.js +72 -0
  269. package/components/routes/cases/modals/CaseRecordRow.d.ts +9 -0
  270. package/components/routes/cases/modals/CaseRecordRow.js +14 -0
  271. package/components/routes/cases/modals/CreateCaseModal.d.ts +7 -0
  272. package/components/routes/cases/modals/CreateCaseModal.js +53 -0
  273. package/components/routes/cases/modals/CreateCaseModal.test.d.ts +1 -0
  274. package/components/routes/cases/modals/CreateCaseModal.test.js +319 -0
  275. package/components/routes/cases/modals/RenameItemModal.d.ts +9 -0
  276. package/components/routes/cases/modals/RenameItemModal.js +44 -0
  277. package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
  278. package/components/routes/cases/modals/ResolveModal.js +120 -0
  279. package/components/routes/cases/modals/ResolveModal.test.d.ts +1 -0
  280. package/components/routes/cases/modals/ResolveModal.test.js +520 -0
  281. package/components/routes/cases/modals/hooks.d.ts +7 -0
  282. package/components/routes/cases/modals/hooks.js +40 -0
  283. package/components/routes/cases/modals/types.d.ts +13 -0
  284. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +6 -0
  285. package/components/routes/cases/search/CaseAssigneeFilter.js +33 -0
  286. package/components/routes/cases/search/CaseAssigneeFilter.test.d.ts +1 -0
  287. package/components/routes/cases/search/CaseAssigneeFilter.test.js +127 -0
  288. package/components/routes/cases/search/CaseDateFilter.d.ts +13 -0
  289. package/components/routes/cases/search/CaseDateFilter.js +26 -0
  290. package/components/routes/cases/search/CaseDateFilter.test.d.ts +1 -0
  291. package/components/routes/cases/search/CaseDateFilter.test.js +115 -0
  292. package/components/routes/cases/search/CaseStatusFilter.d.ts +6 -0
  293. package/components/routes/cases/search/CaseStatusFilter.js +13 -0
  294. package/components/routes/cases/search/CaseStatusFilter.test.d.ts +1 -0
  295. package/components/routes/cases/search/CaseStatusFilter.test.js +86 -0
  296. package/components/routes/cases/utils.d.ts +3 -0
  297. package/components/routes/cases/utils.js +21 -0
  298. package/components/routes/dossiers/DossierEditor.js +2 -2
  299. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  300. package/components/routes/events/EventViewer.d.ts +7 -0
  301. package/components/routes/events/EventViewer.js +24 -0
  302. package/components/routes/help/ApiDocumentation.js +1 -1
  303. package/components/routes/help/HitBannerDocumentation.js +1 -0
  304. package/components/routes/help/HitDocumentation.js +1 -3
  305. package/components/routes/help/OverviewDocumentation.d.ts +1 -1
  306. package/components/routes/help/OverviewDocumentation.js +0 -1
  307. package/components/routes/hits/search/InformationPane.d.ts +1 -0
  308. package/components/routes/hits/search/InformationPane.js +51 -64
  309. package/components/routes/hits/search/QuerySettings.js +2 -1
  310. package/components/routes/hits/search/QuerySettings.test.js +14 -9
  311. package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +11 -11
  312. package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
  313. package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
  314. package/components/routes/hits/search/SearchPane.js +24 -46
  315. package/components/routes/hits/search/ViewLink.js +4 -4
  316. package/components/routes/hits/search/ViewLink.test.js +8 -9
  317. package/components/routes/hits/search/grid/RecordGrid.d.ts +3 -0
  318. package/components/routes/hits/search/grid/{HitGrid.js → RecordGrid.js} +15 -15
  319. package/components/routes/hits/search/shared/CustomSpan.test.js +3 -4
  320. package/components/routes/hits/search/shared/IndexPicker.d.ts +8 -0
  321. package/components/routes/hits/search/shared/IndexPicker.js +21 -0
  322. package/components/routes/hits/search/shared/LayoutSettings.js +3 -3
  323. package/components/routes/hits/search/shared/SearchActionMenu.js +3 -9
  324. package/components/routes/hits/view/HitViewer.js +13 -14
  325. package/components/routes/home/ViewCard.js +47 -41
  326. package/components/routes/overviews/OverviewViewer.js +4 -4
  327. package/components/routes/views/ViewComposer.js +49 -23
  328. package/locales/en/translation.json +171 -27
  329. package/locales/fr/translation.json +169 -25
  330. package/models/WithMetadata.d.ts +2 -1
  331. package/models/entities/generated/Analytic.d.ts +0 -3
  332. package/models/entities/generated/ApiType.d.ts +3 -1
  333. package/models/entities/generated/AttachmentsFile.d.ts +12 -0
  334. package/models/entities/generated/Case.d.ts +29 -0
  335. package/models/entities/generated/DestinationOriginal.d.ts +19 -0
  336. package/models/entities/generated/ECSEvent.d.ts +31 -0
  337. package/models/entities/generated/EmailAttachment.d.ts +8 -0
  338. package/models/entities/generated/EmailParent.d.ts +19 -0
  339. package/models/entities/generated/Enrichments.d.ts +7 -0
  340. package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
  341. package/models/entities/generated/Event.d.ts +80 -26
  342. package/models/entities/generated/EventCloud.d.ts +20 -0
  343. package/models/entities/generated/EventDestination.d.ts +23 -0
  344. package/models/entities/generated/EventEmail.d.ts +30 -0
  345. package/models/entities/generated/EventFile.d.ts +36 -0
  346. package/models/entities/generated/EventHowler.d.ts +42 -0
  347. package/models/entities/generated/EventHttp.d.ts +11 -0
  348. package/models/entities/generated/EventObserver.d.ts +21 -0
  349. package/models/entities/generated/EventOrganization.d.ts +7 -0
  350. package/models/entities/generated/EventProcess.d.ts +34 -0
  351. package/models/entities/generated/EventSource.d.ts +23 -0
  352. package/models/entities/generated/EventThreat.d.ts +21 -0
  353. package/models/entities/generated/EventTls.d.ts +12 -0
  354. package/models/entities/generated/Hit.d.ts +3 -2
  355. package/models/entities/generated/Howler.d.ts +0 -5
  356. package/models/entities/generated/HttpResponse.d.ts +11 -0
  357. package/models/entities/generated/Item.d.ts +12 -0
  358. package/models/entities/generated/ObserverIngress.d.ts +9 -0
  359. package/models/entities/generated/Rule.d.ts +8 -9
  360. package/models/entities/generated/Task.d.ts +10 -0
  361. package/models/entities/generated/Threat.d.ts +2 -2
  362. package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
  363. package/models/entities/generated/View.d.ts +1 -0
  364. package/models/socket/CaseUpdate.d.ts +5 -0
  365. package/models/socket/ViewersUpdate.d.ts +4 -0
  366. package/package.json +115 -94
  367. package/plugins/clue/components/ClueTypography.js +2 -2
  368. package/plugins/clue/index.d.ts +1 -1
  369. package/plugins/clue/index.js +0 -1
  370. package/plugins/clue/utils.d.ts +2 -1
  371. package/tests/mocks.d.ts +11 -1
  372. package/tests/mocks.js +12 -7
  373. package/tests/server-handlers.js +11 -1
  374. package/tests/utils.d.ts +4 -0
  375. package/tests/utils.js +21 -0
  376. package/utils/classificationParser.d.ts +195 -0
  377. package/utils/classificationParser.js +680 -0
  378. package/utils/constants.d.ts +4 -3
  379. package/utils/constants.js +6 -0
  380. package/utils/recordFunctions.d.ts +3 -0
  381. package/utils/recordFunctions.js +8 -0
  382. package/utils/recordFunctions.test.d.ts +1 -0
  383. package/utils/{hitFunctions.test.js → recordFunctions.test.js} +1 -1
  384. package/utils/socketUtils.d.ts +14 -0
  385. package/utils/socketUtils.js +17 -1
  386. package/utils/socketUtils.test.js +49 -16
  387. package/utils/typeUtils.d.ts +7 -0
  388. package/utils/typeUtils.js +27 -0
  389. package/utils/viewUtils.js +3 -0
  390. package/api/analytic/rules.d.ts +0 -4
  391. package/api/analytic/rules.js +0 -8
  392. package/components/app/providers/HitProvider.d.ts +0 -22
  393. package/components/elements/display/icons/BundleButton.d.ts +0 -6
  394. package/components/elements/display/icons/BundleButton.js +0 -32
  395. package/components/elements/hit/HitContextMenu.js +0 -239
  396. package/components/elements/hit/HitRelated.d.ts +0 -6
  397. package/components/elements/hit/HitRelated.js +0 -7
  398. package/components/elements/hit/elements/HitUsers.js +0 -33
  399. package/components/routes/advanced/RuleModal.d.ts +0 -7
  400. package/components/routes/advanced/RuleModal.js +0 -83
  401. package/components/routes/analytics/RuleView.d.ts +0 -8
  402. package/components/routes/analytics/RuleView.js +0 -48
  403. package/components/routes/help/BundleDocumentation.d.ts +0 -3
  404. package/components/routes/help/BundleDocumentation.js +0 -12
  405. package/components/routes/help/markdown/en/bundles.md.js +0 -1
  406. package/components/routes/help/markdown/fr/bundles.md.js +0 -1
  407. package/components/routes/hits/search/BundleScroller.d.ts +0 -2
  408. package/components/routes/hits/search/BundleScroller.js +0 -6
  409. package/components/routes/hits/search/grid/HitGrid.d.ts +0 -3
  410. package/components/routes/hits/search/shared/BundleParentMenu.d.ts +0 -6
  411. package/components/routes/hits/search/shared/BundleParentMenu.js +0 -32
  412. package/utils/hitFunctions.d.ts +0 -2
  413. package/utils/hitFunctions.js +0 -8
  414. /package/{components/app/providers/HitSearchProvider.test.d.ts → api/socket/viewers.test.d.ts} +0 -0
  415. /package/components/{elements/hit/HitContextMenu.test.d.ts → app/providers/RecordSearchProvider.test.d.ts} +0 -0
  416. /package/{utils/hitFunctions.test.d.ts → components/app/providers/UserListProvider.test.d.ts} +0 -0
  417. /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
  418. /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
  419. /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
@@ -2,13 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Typography } from '@mui/material';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Link } from 'react-router-dom';
5
- const QueryResultText = ({ count, query }) => {
5
+ const QueryResultText = ({ count, query, mb = 0.5 }) => {
6
6
  const { t } = useTranslation();
7
7
  return (_jsxs(Typography, { sx: theme => ({
8
8
  color: theme.palette.text.secondary,
9
9
  fontSize: '0.9em',
10
10
  fontStyle: 'italic',
11
- mb: 0.5,
11
+ mb,
12
12
  '& a': { color: theme.palette.text.secondary }
13
13
  }), variant: "body2", children: [t('search.total', { count }), " ", _jsx(Link, { to: `/hits?query=${encodeURIComponent(query)}`, children: t('search.open') })] }));
14
14
  };
@@ -0,0 +1,6 @@
1
+ import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
2
+ declare const _default: import("react").NamedExoticComponent<{
3
+ id?: string;
4
+ event?: Event;
5
+ }>;
6
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { CardContent, Skeleton } from '@mui/material';
3
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
4
+ import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
5
+ import { memo, useEffect } from 'react';
6
+ import { useContextSelector } from 'use-context-selector';
7
+ import EventPreview from './EventPreview';
8
+ const EventCard = ({ id, event: _event }) => {
9
+ const getRecord = useContextSelector(RecordContext, ctx => ctx.getRecord);
10
+ const event = useContextSelector(RecordContext, ctx => _event ?? ctx.records[id]);
11
+ useEffect(() => {
12
+ if (!event) {
13
+ getRecord(id);
14
+ }
15
+ // eslint-disable-next-line react-hooks/exhaustive-deps
16
+ }, [id]);
17
+ if (!event) {
18
+ return _jsx(Skeleton, { variant: "rounded", height: "200px" });
19
+ }
20
+ return (_jsx(HowlerCard, { sx: { position: 'relative' }, children: _jsx(CardContent, { children: _jsx(EventPreview, { event: event }) }) }));
21
+ };
22
+ export default memo(EventCard);
@@ -0,0 +1,6 @@
1
+ import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
2
+ type PreviewProps = {
3
+ event: Event;
4
+ };
5
+ declare const _default: import("react").NamedExoticComponent<PreviewProps>;
6
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { OpenInNew } from '@mui/icons-material';
3
+ import { Chip, IconButton, Stack, Typography, useTheme } from '@mui/material';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Link } from 'react-router-dom';
7
+ const EventPreview = ({ event }) => {
8
+ const { t } = useTranslation();
9
+ const theme = useTheme();
10
+ return (_jsx(Stack, { flex: 1, spacing: 1, sx: { overflow: 'hidden', borderBottom: `thin solid ${theme.palette.divider}`, pb: 1, mb: 0 }, children: _jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "body1", fontWeight: "bold", children: event.event.provider }), _jsx("div", { style: { flex: 1 } }), _jsx(Chip, { label: event.event.kind }), event.event.reference && (_jsx(IconButton, { size: "small", component: Link, to: event.event.reference, sx: { opacity: 1 }, target: "_blank", rel: "noopener noreferrer", children: _jsx(OpenInNew, { fontSize: "small" }) }))] }), event.event.type && (_jsxs(Typography, { variant: "caption", children: [t('event.type'), " - ", event.event.type.join(', ')] })), event.event.module && (_jsxs(Typography, { variant: "caption", children: [t('event.module'), " - ", event.event.module] }))] }) }));
11
+ };
12
+ export default memo(EventPreview);
@@ -3,9 +3,9 @@ import { MoreHoriz } from '@mui/icons-material';
3
3
  import { Box, CircularProgress, Divider, FormControl, FormControlLabel, FormLabel, IconButton, Menu, Radio, RadioGroup, Stack, Switch, useMediaQuery } from '@mui/material';
4
4
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
5
5
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
7
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
8
6
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
7
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
8
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
9
9
  import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
10
10
  import useHitActions from '@cccsaurora/howler-ui/components/hooks/useHitActions';
11
11
  import { useMyLocalStorageProvider } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
@@ -30,8 +30,8 @@ const HitActions = ({ hit, orientation = 'horizontal' }) => {
30
30
  const getCurrentViews = useContextSelector(ViewContext, ctx => ctx.getCurrentViews);
31
31
  const selected = useContextSelector(ParameterContext, ctx => ctx?.selected);
32
32
  const setSelected = useContextSelector(ParameterContext, ctx => ctx?.setSelected);
33
- const clearSelectedHits = useContextSelector(HitContext, ctx => ctx.clearSelectedHits);
34
- const nextHit = useContextSelector(HitSearchContext, ctx => {
33
+ const clearSelectedHits = useContextSelector(RecordContext, ctx => ctx.clearSelectedRecords);
34
+ const nextHit = useContextSelector(RecordSearchContext, ctx => {
35
35
  if (!ctx?.response || !selected) {
36
36
  return null;
37
37
  }
@@ -3,6 +3,7 @@ import { type FC } from 'react';
3
3
  import { HitLayout } from './HitLayout';
4
4
  type HitBannerProps = {
5
5
  hit: Hit;
6
+ lazy?: boolean;
6
7
  layout?: HitLayout;
7
8
  showAssigned?: boolean;
8
9
  useListener?: boolean;
@@ -1,39 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { OpenInNew } from '@mui/icons-material';
3
3
  import { Box, Chip, Divider, Grid, Stack, Tooltip, Typography, avatarClasses, chipClasses, iconButtonClasses, useTheme } from '@mui/material';
4
- import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
5
4
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
5
  import { uniq } from 'lodash-es';
7
6
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
8
- import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
7
+ import { useCallback, useContext, useMemo } from 'react';
9
8
  import { Trans, useTranslation } from 'react-i18next';
10
9
  import { usePluginStore } from 'react-pluggable';
11
- import { Link } from 'react-router-dom';
12
10
  import { ESCALATION_COLORS, PROVIDER_COLORS } from '@cccsaurora/howler-ui/utils/constants';
13
11
  import { stringToColor } from '@cccsaurora/howler-ui/utils/utils';
14
12
  import PluginTypography from '../PluginTypography';
13
+ import AnalyticLink from './elements/AnalyticLink';
14
+ import Assigned from './elements/Assigned';
15
15
  import EscalationChip from './elements/EscalationChip';
16
16
  import HitTimestamp from './elements/HitTimestamp';
17
- import HitUsers from './elements/HitUsers';
18
17
  import HitBannerTooltip from './HitBannerTooltip';
19
18
  import { HitLayout } from './HitLayout';
20
- const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
19
+ import RelatedRecords from './related/RelatedRecords';
20
+ const HitBanner = ({ hit, lazy = false, layout = HitLayout.NORMAL, showAssigned = true }) => {
21
21
  const { t } = useTranslation();
22
22
  const { config } = useContext(ApiConfigContext);
23
23
  const theme = useTheme();
24
24
  const pluginStore = usePluginStore();
25
- const { getMatchingAnalytic } = useMatchers();
26
- const [analyticId, setAnalyticId] = useState();
27
25
  const compressed = useMemo(() => layout === HitLayout.DENSE, [layout]);
28
26
  const textVariant = useMemo(() => (layout === HitLayout.COMFY ? 'body1' : 'caption'), [layout]);
29
- useEffect(() => {
30
- if (!hit?.howler.analytic) {
31
- return;
27
+ const providerColor = useMemo(() => {
28
+ if (!hit?.event.provider) {
29
+ return PROVIDER_COLORS.unknown;
32
30
  }
33
- getMatchingAnalytic(hit).then(analytic => setAnalyticId(analytic?.analytic_id));
34
- // eslint-disable-next-line react-hooks/exhaustive-deps
35
- }, [hit?.howler.analytic]);
36
- const providerColor = useMemo(() => PROVIDER_COLORS[hit.event?.provider ?? 'unknown'] ?? stringToColor(hit.event.provider), [hit.event?.provider]);
31
+ return PROVIDER_COLORS[hit?.event.provider] ?? stringToColor(hit?.event.provider);
32
+ }, [hit?.event.provider]);
37
33
  const mitreId = useMemo(() => {
38
34
  if (hit.threat?.framework?.toLowerCase().startsWith('mitre')) {
39
35
  return;
@@ -53,35 +49,23 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
53
49
  }
54
50
  return `/api/static/mitre/${mitreId}.svg`;
55
51
  }, [mitreId]);
56
- const leftBox = useMemo(() => {
57
- if (hit.howler.is_bundle) {
58
- return (_jsx(Box, { sx: {
59
- alignSelf: 'stretch',
60
- backgroundColor: providerColor,
61
- borderRadius: theme.shape.borderRadius,
62
- minWidth: '15px'
63
- } }));
64
- }
65
- else {
66
- return (_jsx(HitBannerTooltip, { hit: hit, children: _jsxs(Box, { sx: {
67
- gridColumn: { xs: 'span 3', sm: 'span 1' },
68
- minWidth: '90px',
69
- backgroundColor: providerColor,
70
- color: theme.palette.getContrastText(providerColor),
71
- alignSelf: 'start',
72
- borderRadius: theme.shape.borderRadius,
73
- p: compressed ? 0.5 : 1,
74
- pt: 2,
75
- pl: 1
76
- }, display: "flex", flexDirection: "column", children: [_jsx(Typography, { variant: compressed ? 'caption' : 'body1', style: { wordBreak: 'break-all' }, children: hit.organization?.name ?? _jsx(Trans, { i18nKey: "unknown" }) }), iconUrl && (_jsx(Box, { sx: {
77
- width: '40px',
78
- height: '40px',
79
- mask: `url("${iconUrl}")`,
80
- maskSize: 'cover',
81
- background: theme.palette.getContrastText(providerColor)
82
- } }))] }) }));
83
- }
84
- }, [compressed, hit, iconUrl, providerColor, theme.palette, theme.shape.borderRadius]);
52
+ const leftBox = useMemo(() => (_jsx(HitBannerTooltip, { hit: hit, children: _jsxs(Box, { sx: {
53
+ gridColumn: { xs: 'span 3', sm: 'span 1' },
54
+ minWidth: '90px',
55
+ backgroundColor: providerColor,
56
+ color: theme.palette.getContrastText(providerColor),
57
+ alignSelf: 'start',
58
+ borderRadius: theme.shape.borderRadius,
59
+ p: compressed ? 0.5 : 1,
60
+ pt: 2,
61
+ pl: 1
62
+ }, display: "flex", flexDirection: "column", children: [_jsx(Typography, { variant: compressed ? 'caption' : 'body1', style: { wordBreak: 'break-all' }, children: hit.organization?.name ?? _jsx(Trans, { i18nKey: "unknown" }) }), iconUrl && (_jsx(Box, { sx: {
63
+ width: '40px',
64
+ height: '40px',
65
+ mask: `url("${iconUrl}")`,
66
+ maskSize: 'cover',
67
+ background: theme.palette.getContrastText(providerColor)
68
+ } }))] }) })), [compressed, hit, iconUrl, providerColor, theme.palette, theme.shape.borderRadius]);
85
69
  /**
86
70
  * The tooltips are necessary only when in the most compressed format
87
71
  */
@@ -89,7 +73,7 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
89
73
  const _children = (_jsxs(Stack, { direction: "row", spacing: 1, flex: 1, children: [_jsxs(Typography, { variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, sx: [
90
74
  { display: 'flex', flexDirection: 'row' },
91
75
  ...(Array.isArray(typographyProps?.sx) ? typographyProps?.sx : [typographyProps?.sx])
92
- ], children: [t(i18nKey), ":"] }), (Array.isArray(value) ? value : [value]).map(val => (_jsx(PluginTypography, { component: "span", context: "banner", variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, value: val, field: field, hit: hit }, val)))] }));
76
+ ], children: [t(i18nKey), ":"] }), (Array.isArray(value) ? value : [value]).map(val => (_jsx(PluginTypography, { component: "span", context: "banner", variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, value: val, field: field, obj: hit }, val)))] }));
93
77
  return compressed ? (_jsx(Tooltip, { title: Array.isArray(value) ? (_jsx("div", { children: value.map(_indicator => (_jsx("p", { style: { margin: 0, padding: 0 }, children: _indicator }, _indicator))) })) : (value), children: _children })) : (_children);
94
78
  }, [compressed, hit, t, textVariant]);
95
79
  return (_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(0, auto) minmax(0, 1fr) minmax(0, auto)", alignItems: "stretch", sx: { width: '100%', ml: 0, overflow: 'hidden', textDecoration: 'none', color: 'text.primary' }, component: "a", href: `/hits/${hit?.howler.id}`, onClick: e => e.preventDefault(), children: [leftBox, _jsxs(Stack, { sx: {
@@ -99,9 +83,7 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
99
83
  }, spacing: layout !== HitLayout.COMFY ? 1 : 2, divider: _jsx(Divider, { orientation: "horizontal", sx: [
100
84
  layout !== HitLayout.COMFY && { marginTop: '4px !important' },
101
85
  { mr: `${theme.spacing(-1)} !important` }
102
- ] }), children: [_jsxs(Typography, { variant: compressed ? 'body1' : 'h6', fontWeight: compressed && 'bold', sx: { alignSelf: 'start', '& a': { color: 'text.primary' } }, children: [analyticId ? (_jsx(Link, { to: `/analytics/${analyticId}`, target: "_blank", rel: "noopener noreferrer", onClick: e => {
103
- e.stopPropagation();
104
- }, children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }), hit.howler?.rationale && (_jsxs(Typography, { flex: 1, variant: textVariant, color: ESCALATION_COLORS[hit.howler.escalation] + '.main', sx: { fontWeight: 'bold' }, children: [t('hit.header.rationale'), ": ", hit.howler.rationale] })), hit.howler?.outline && (_jsxs(_Fragment, { children: [_jsxs(Grid, { container: true, spacing: layout !== HitLayout.COMFY ? 1 : 2, sx: { ml: `${theme.spacing(-1)} !important` }, children: [hit.howler.outline.threat && (_jsx(Grid, { item: true, children: _jsx(Wrapper, { i18nKey: "hit.header.threat", value: hit.howler.outline.threat, field: "howler.outline.threat" }) })), hit.howler.outline.target && (_jsx(Grid, { item: true, children: _jsx(Wrapper, { i18nKey: "hit.header.target", value: hit.howler.outline.target, field: "howler.outline.target" }) }))] }), hit.howler.outline.indicators?.length > 0 && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Typography, { component: "span", variant: textVariant, children: [t('hit.header.indicators'), ":"] }), _jsx(Grid, { container: true, spacing: 0.5, sx: { mt: `${theme.spacing(-0.5)} !important`, ml: `${theme.spacing(0.25)} !important` }, children: uniq(hit.howler.outline.indicators).map((_indicator, index) => {
86
+ ] }), children: [_jsx(AnalyticLink, { lazy: lazy, hit: hit }), hit.howler?.rationale && (_jsxs(Typography, { flex: 1, variant: textVariant, color: ESCALATION_COLORS[hit.howler.escalation] + '.main', sx: { fontWeight: 'bold' }, children: [t('hit.header.rationale'), ": ", hit.howler.rationale] })), hit.howler?.outline && (_jsxs(_Fragment, { children: [_jsxs(Grid, { container: true, spacing: layout !== HitLayout.COMFY ? 1 : 2, sx: { ml: `${theme.spacing(-1)} !important` }, children: [hit.howler.outline.threat && (_jsx(Grid, { item: true, children: _jsx(Wrapper, { i18nKey: "hit.header.threat", value: hit.howler.outline.threat, field: "howler.outline.threat" }) })), hit.howler.outline.target && (_jsx(Grid, { item: true, children: _jsx(Wrapper, { i18nKey: "hit.header.target", value: hit.howler.outline.target, field: "howler.outline.target" }) }))] }), hit.howler.outline.indicators?.length > 0 && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Typography, { component: "span", variant: textVariant, children: [t('hit.header.indicators'), ":"] }), _jsx(Grid, { container: true, spacing: 0.5, sx: { mt: `${theme.spacing(-0.5)} !important`, ml: `${theme.spacing(0.25)} !important` }, children: uniq(hit.howler.outline.indicators).map((_indicator, index) => {
105
87
  return (_jsx(Grid, { item: true, children: _jsxs(Stack, { direction: "row", children: [_jsx(PluginTypography, { context: "indicators", variant: textVariant, value: _indicator, children: _indicator }), index < hit.howler.outline.indicators.length - 1 && (_jsx(Typography, { variant: textVariant, children: ',' }))] }) }, _indicator));
106
88
  }) })] })), hit.howler.outline.summary && (_jsx(Wrapper, { i18nKey: "hit.header.summary", value: hit.howler.outline.summary, paragraph: true, textOverflow: "wrap", sx: [compressed && { marginTop: `0 !important` }], field: "howler.outline.summary" }))] }))] }), _jsxs(Stack, { direction: "column", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, alignSelf: "stretch", sx: [
107
89
  { minWidth: 0, alignItems: { sm: 'end', md: 'start' }, flex: 1, pl: 1 },
@@ -115,8 +97,8 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
115
97
  width: theme.spacing(3)
116
98
  }
117
99
  }
118
- ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), _jsx(HitUsers, { hit: hit, layout: layout, showAssigned: showAssigned }), hit.howler.links?.[0]?.href && (_jsx(Chip, { icon: _jsx(OpenInNew, {}), label: hit.howler.links[0].title || t('hit.header.link'), size: layout !== HitLayout.COMFY ? 'small' : 'medium', component: "a", href: hit.howler.links[0].href, target: "_blank", rel: "noopener noreferrer", sx: { [`.${chipClasses.label}`]: { cursor: 'pointer !important' } }, onClick: e => {
100
+ ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), _jsx(Assigned, { hit: hit, layout: layout, showAssigned: showAssigned }), hit.howler.links?.[0]?.href && (_jsx(Chip, { icon: _jsx(OpenInNew, {}), label: hit.howler.links[0].title || t('hit.header.link'), size: layout !== HitLayout.COMFY ? 'small' : 'medium', component: "a", href: hit.howler.links[0].href, target: "_blank", rel: "noopener noreferrer", sx: { [`.${chipClasses.label}`]: { cursor: 'pointer !important' } }, onClick: e => {
119
101
  e.stopPropagation();
120
- } })), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: layout !== HitLayout.COMFY ? 'small' : 'medium', color: "primary" })), hit.howler.is_bundle && (_jsx(Chip, { size: layout !== HitLayout.COMFY ? 'small' : 'medium', label: t('hit.header.bundlesize', { hits: hit.howler.hits.length }) }))] }), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
102
+ } })), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, color: "primary" }))] }), hit.howler.related && _jsx(RelatedRecords, { hit: hit }), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
121
103
  };
122
104
  export default HitBanner;
@@ -1,7 +1,11 @@
1
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
1
2
  import type { HitLayout } from './HitLayout';
2
3
  declare const _default: import("react").NamedExoticComponent<{
3
4
  id?: string;
5
+ hit?: Hit;
6
+ lazy?: boolean;
4
7
  layout: HitLayout;
5
8
  readOnly?: boolean;
9
+ elevation?: number;
6
10
  }>;
7
11
  export default _default;
@@ -1,24 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CardContent, Skeleton } from '@mui/material';
3
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
3
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
4
4
  import { memo, useEffect } from 'react';
5
5
  import { useContextSelector } from 'use-context-selector';
6
6
  import HowlerCard from '../display/HowlerCard';
7
7
  import HitBanner from './HitBanner';
8
8
  import HitLabels from './HitLabels';
9
9
  import HitOutline from './HitOutline';
10
- const HitCard = ({ id, layout, readOnly = true }) => {
11
- const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
12
- const hit = useContextSelector(HitContext, ctx => ctx.hits[id]);
10
+ const HitCard = ({ id, layout, readOnly = true, lazy = false, elevation, hit: _hit }) => {
11
+ const getRecord = useContextSelector(RecordContext, ctx => ctx.getRecord);
12
+ const hit = useContextSelector(RecordContext, ctx => _hit ?? ctx.records[id]);
13
13
  useEffect(() => {
14
- if (!hit) {
15
- getHit(id);
14
+ if (!hit && !lazy) {
15
+ getRecord(id);
16
16
  }
17
17
  // eslint-disable-next-line react-hooks/exhaustive-deps
18
18
  }, [id]);
19
19
  if (!hit) {
20
20
  return _jsx(Skeleton, { variant: "rounded", height: "200px" });
21
21
  }
22
- return (_jsx(HowlerCard, { tabIndex: 0, sx: { position: 'relative' }, children: _jsxs(CardContent, { children: [_jsx(HitBanner, { hit: hit, layout: layout }), _jsx(HitOutline, { hit: hit, layout: layout }), _jsx(HitLabels, { hit: hit, readOnly: readOnly })] }) }));
22
+ return (_jsx(HowlerCard, { id: hit?.howler.id, tabIndex: 0, sx: { position: 'relative' }, elevation: elevation, children: _jsxs(CardContent, { children: [_jsx(HitBanner, { hit: hit, layout: layout, lazy: lazy }), _jsx(HitOutline, { hit: hit, layout: layout, lazy: lazy }), _jsx(HitLabels, { hit: hit, readOnly: readOnly })] }) }));
23
23
  };
24
24
  export default memo(HitCard);
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Add, Check, Edit } from '@mui/icons-material';
3
3
  import { Backdrop, Box, Button, Chip, CircularProgress, Drawer, FormControl, FormHelperText, IconButton, InputAdornment, InputLabel, ListItemIcon, ListItemText, MenuItem, Select, Stack, TextField, Tooltip, Typography } from '@mui/material';
4
4
  import api from '@cccsaurora/howler-ui/api';
5
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
5
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
6
6
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
7
7
  import { memo, useCallback, useEffect, useState } from 'react';
8
8
  import { Trans, useTranslation } from 'react-i18next';
@@ -44,7 +44,7 @@ const NewLabelForm = ({ handleSubmit }) => {
44
44
  const HitLabels = ({ hit, readOnly = false }) => {
45
45
  const { dispatchApi } = useMyApi();
46
46
  const { t } = useTranslation();
47
- const updateHit = useContextSelector(HitContext, ctx => ctx.updateHit);
47
+ const updateHit = useContextSelector(RecordContext, ctx => ctx.updateRecord);
48
48
  const [openDrawer, setOpenDrawer] = useState(false);
49
49
  const [loading, setLoading] = useState(false);
50
50
  const [labels, setLabels] = useState(Object.entries(hit.howler.labels).flatMap(([key, category]) => {
@@ -17,8 +17,8 @@ vi.mock('components/hooks/useMyApi', () => ({
17
17
  dispatchApi: vi.fn()
18
18
  })
19
19
  }));
20
- vi.mock('components/app/providers/HitProvider', () => ({
21
- HitContext: {}
20
+ vi.mock('components/app/providers/RecordProvider', () => ({
21
+ RecordContext: {}
22
22
  }));
23
23
  vi.mock('use-context-selector', () => ({
24
24
  useContextSelector: () => vi.fn()
@@ -4,6 +4,7 @@ import { HitLayout } from './HitLayout';
4
4
  export declare const DEFAULT_FIELDS: string[];
5
5
  declare const _default: import("react").NamedExoticComponent<{
6
6
  hit: WithMetadata<Hit>;
7
+ lazy?: boolean;
7
8
  layout: HitLayout;
8
9
  forceAllFields?: boolean;
9
10
  }>;
@@ -9,9 +9,9 @@ import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
9
9
  import { HitLayout } from './HitLayout';
10
10
  import DefaultOutline from './outlines/DefaultOutline';
11
11
  export const DEFAULT_FIELDS = ['event.created', 'howler.id', 'howler.hash'];
12
- const HitOutline = ({ hit, layout, forceAllFields = false }) => {
12
+ const HitOutline = ({ hit, layout, lazy = false, forceAllFields = false }) => {
13
13
  const { t } = useTranslation();
14
- const { getMatchingTemplate } = useMatchers();
14
+ const { getMatchingTemplate } = useMatchers(lazy);
15
15
  const [templateFieldCount] = useMyLocalStorageItem(StorageKey.TEMPLATE_FIELD_COUNT, null);
16
16
  const [template, setTemplate] = useState(null);
17
17
  useEffect(() => {
@@ -36,7 +36,7 @@ const HitOutline = ({ hit, layout, forceAllFields = false }) => {
36
36
  fields: DEFAULT_FIELDS
37
37
  });
38
38
  }
39
- }, [hit, layout, template, templateFieldCount]);
39
+ }, [forceAllFields, hit, layout, template, templateFieldCount]);
40
40
  return (_jsxs(Box, { sx: { py: 1, width: '100%', pr: 2 }, children: [layout === HitLayout.COMFY && (_jsx(Typography, { variant: "body1", fontWeight: "bold", sx: { mb: 1 }, children: t('hit.details.title') })), layout !== HitLayout.DENSE && _jsx(Divider, { orientation: "horizontal", sx: { mb: 1 } }), outline] }));
41
41
  };
42
42
  export default memo(HitOutline);
@@ -1,8 +1,8 @@
1
1
  import type { AppSearchItemRendererOption } from '@cccsaurora/howler-ui/commons/components/app/AppSearchService';
2
2
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
- type QuickSearchProps = {
3
+ type PreviewProps = {
4
4
  hit: Hit;
5
- options: AppSearchItemRendererOption<Hit>;
5
+ options?: AppSearchItemRendererOption<Hit>;
6
6
  };
7
- declare const _default: import("react").NamedExoticComponent<QuickSearchProps>;
7
+ declare const _default: import("react").NamedExoticComponent<PreviewProps>;
8
8
  export default _default;
@@ -4,12 +4,18 @@ import { memo, useMemo } from 'react';
4
4
  import { Trans, useTranslation } from 'react-i18next';
5
5
  import { ESCALATION_COLORS, PROVIDER_COLORS, STATUS_COLORS } from '@cccsaurora/howler-ui/utils/constants';
6
6
  import { formatDate, stringToColor } from '@cccsaurora/howler-ui/utils/utils';
7
- const HitQuickSearch = ({ hit, options }) => {
7
+ const HitPreview = ({ hit, options }) => {
8
8
  const { t } = useTranslation();
9
9
  const theme = useTheme();
10
10
  const isUnderLg = useMediaQuery(theme.breakpoints.down('lg'));
11
11
  const providerColor = useMemo(() => PROVIDER_COLORS[hit.event?.provider ?? 'unknown'] ?? stringToColor(hit.event.provider), [hit.event?.provider]);
12
- return (_jsxs(Box, { sx: { overflow: 'hidden', borderBottom: `thin solid ${theme.palette.divider}`, pb: 1, mb: 0 }, display: "grid", gridTemplateColumns: "minmax(0, 1fr) minmax(0, auto)", children: [_jsxs(Stack, { flexGrow: 1, gridColumn: options.state.mode === 'inline' ? 'span 2' : '', children: [_jsxs(Typography, { variant: "body1", fontWeight: "bold", children: [hit.howler.analytic, hit.howler.detection && ': ', hit.howler.detection] }), options.state.mode !== 'inline' && hit.howler?.outline && (_jsx(Tooltip, { placement: isUnderLg ? 'bottom' : 'left', componentsProps: {
12
+ return (_jsxs(Box, { sx: {
13
+ overflow: 'hidden',
14
+ borderBottom: `thin solid ${theme.palette.divider}`,
15
+ pb: 1,
16
+ mb: 0,
17
+ gap: theme.spacing(1)
18
+ }, display: "grid", gridTemplateColumns: "minmax(0, 1fr) minmax(0, auto)", children: [_jsxs(Stack, { flexGrow: 1, gridColumn: options?.state.mode === 'inline' ? 'span 2' : '', children: [_jsxs(Typography, { variant: "body1", fontWeight: "bold", children: [hit.howler.analytic, hit.howler.detection && ': ', hit.howler.detection] }), options?.state.mode !== 'inline' && hit.howler?.outline && (_jsx(Tooltip, { placement: isUnderLg ? 'bottom' : 'left', componentsProps: {
13
19
  tooltip: {
14
20
  sx: {
15
21
  fontSize: 12,
@@ -17,7 +23,7 @@ const HitQuickSearch = ({ hit, options }) => {
17
23
  color: theme.palette.getContrastText(theme.palette.background.paper)
18
24
  }
19
25
  }
20
- }, title: _jsxs(Stack, { divider: _jsx(Divider, { sx: { my: 0.5 } }), children: [_jsxs("div", { children: [_jsx(Trans, { i18nKey: "hit.header.threat" }), ": ", hit.howler.outline.threat] }), _jsxs("div", { children: [_jsx(Trans, { i18nKey: "hit.header.target" }), ": ", hit.howler.outline.target] }), _jsx("div", { children: hit.howler.outline.indicators.join(', ') })] }), children: _jsxs(Stack, { direction: { xs: 'column', sm: 'column' }, flex: 1, children: [_jsxs(Typography, { variant: "caption", textOverflow: "ellipsis", sx: { wordBreak: 'break-all', overflow: 'hidden' }, children: [_jsx(Trans, { i18nKey: "hit.header.threat" }), ": ", hit.howler.outline.threat] }), _jsxs(Typography, { variant: "caption", textOverflow: "ellipsis", sx: { wordBreak: 'break-all', overflow: 'hidden' }, children: [_jsx(Trans, { i18nKey: "hit.header.target" }), ": ", hit.howler.outline.target] }), _jsxs(Typography, { variant: "caption", textOverflow: "ellipsis", sx: { wordBreak: 'break-all', overflow: 'hidden' }, children: [_jsx(Trans, { i18nKey: "hit.header.indicators" }), ": ", hit.howler.outline.indicators.map(i => i).join(', ')] })] }) }))] }), _jsxs(Stack, { alignItems: options.state.mode === 'fullscreen' ? 'end' : 'start', spacing: 0.5, children: [options.state.mode === 'fullscreen' && _jsx(Chip, { label: formatDate(hit.timestamp), size: "small" }), _jsxs(Stack, { direction: "row", spacing: 0.5, children: [options.state.mode === 'inline' && _jsx(Chip, { label: formatDate(hit.timestamp), size: "small" }), _jsx(Chip, { sx: {
26
+ }, title: _jsxs(Stack, { divider: _jsx(Divider, { sx: { my: 0.5 } }), children: [_jsxs("div", { children: [_jsx(Trans, { i18nKey: "hit.header.threat" }), ": ", hit.howler.outline.threat] }), _jsxs("div", { children: [_jsx(Trans, { i18nKey: "hit.header.target" }), ": ", hit.howler.outline.target] }), _jsx("div", { children: hit.howler.outline.indicators.join(', ') })] }), children: _jsxs(Stack, { direction: { xs: 'column', sm: 'column' }, flex: 1, children: [_jsxs(Typography, { variant: "caption", textOverflow: "ellipsis", sx: { wordBreak: 'break-all', overflow: 'hidden' }, children: [_jsx(Trans, { i18nKey: "hit.header.threat" }), ": ", hit.howler.outline.threat] }), _jsxs(Typography, { variant: "caption", textOverflow: "ellipsis", sx: { wordBreak: 'break-all', overflow: 'hidden' }, children: [_jsx(Trans, { i18nKey: "hit.header.target" }), ": ", hit.howler.outline.target] })] }) }))] }), _jsxs(Stack, { alignItems: options?.state.mode === 'fullscreen' ? 'end' : 'start', spacing: 0.5, children: [options?.state.mode === 'fullscreen' && _jsx(Chip, { label: formatDate(hit.timestamp), size: "small" }), _jsxs(Stack, { direction: "row", spacing: 0.5, children: [options?.state.mode === 'inline' && _jsx(Chip, { label: formatDate(hit.timestamp), size: "small" }), _jsx(Chip, { sx: {
21
27
  backgroundColor: providerColor,
22
28
  color: theme.palette.getContrastText(providerColor)
23
29
  }, label: hit.organization?.name ?? _jsx(Trans, { i18nKey: "unknown" }), size: "small" }), _jsx(Chip, { label: hit.howler.escalation, size: "small", color: ESCALATION_COLORS[hit.howler.escalation] })] }), _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(Chip, { sx: {
@@ -28,4 +34,4 @@ const HitQuickSearch = ({ hit, options }) => {
28
34
  ? hit?.howler.assignment
29
35
  : t('app.drawer.hit.assignment.unassigned.name'), size: "small" }), _jsx(Chip, { label: hit.howler.status, size: "small", color: STATUS_COLORS[hit.howler.status] })] })] })] }));
30
36
  };
31
- export default memo(HitQuickSearch);
37
+ export default memo(HitPreview);
@@ -1,8 +1,9 @@
1
1
  import type { HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
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 { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
4
5
  declare const _default: import("react").NamedExoticComponent<{
5
- response?: HowlerSearchResponse<WithMetadata<Hit>>;
6
+ response?: HowlerSearchResponse<WithMetadata<Hit | Event>>;
6
7
  execute?: boolean;
7
8
  onStart?: () => void;
8
9
  onComplete?: () => void;
@@ -4,8 +4,8 @@ import { Alert, AlertTitle, Autocomplete, Box, Button, Chip, CircularProgress, D
4
4
  import api from '@cccsaurora/howler-ui/api';
5
5
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
6
6
  import { FieldContext } from '@cccsaurora/howler-ui/components/app/providers/FieldProvider';
7
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
8
7
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
8
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
9
9
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
10
10
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
11
11
  import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
@@ -14,6 +14,7 @@ import { memo, useCallback, useContext, useEffect, useState } from 'react';
14
14
  import { useTranslation } from 'react-i18next';
15
15
  import { useContextSelector } from 'use-context-selector';
16
16
  import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
17
+ import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
17
18
  import { getTimeRange } from '@cccsaurora/howler-ui/utils/utils';
18
19
  import PluginChip from '../PluginChip';
19
20
  import HitGraph from './aggregate/HitGraph';
@@ -25,9 +26,9 @@ const HitSummary = ({ response, onStart, onComplete }) => {
25
26
  const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
26
27
  const [showHitSummaryGraph] = useMyLocalStorageItem(StorageKey.SHOW_HIT_SUMMARY_GRAPH, true);
27
28
  const { getMatchingTemplate } = useMatchers();
28
- const searching = useContextSelector(HitSearchContext, ctx => ctx.searching);
29
- const error = useContextSelector(HitSearchContext, ctx => ctx.error);
30
- const getFilters = useContextSelector(HitSearchContext, ctx => ctx.getFilters);
29
+ const searching = useContextSelector(RecordSearchContext, ctx => ctx.searching);
30
+ const error = useContextSelector(RecordSearchContext, ctx => ctx.error);
31
+ const getFilters = useContextSelector(RecordSearchContext, ctx => ctx.getFilters);
31
32
  const query = useContextSelector(ParameterContext, ctx => ctx.query);
32
33
  const setQuery = useContextSelector(ParameterContext, ctx => ctx.setQuery);
33
34
  const views = useContextSelector(ParameterContext, ctx => ctx.views);
@@ -41,7 +42,7 @@ const HitSummary = ({ response, onStart, onComplete }) => {
41
42
  }
42
43
  try {
43
44
  // Get a list of every key in every template of the hits we're searching
44
- const rawCounts = await Promise.all((response?.items ?? []).map(async (h) => {
45
+ const rawCounts = await Promise.all((response?.items ?? []).filter(isHit).map(async (h) => {
45
46
  const matchingTemplate = await getMatchingTemplate(h);
46
47
  return (matchingTemplate?.keys ?? [])
47
48
  .filter(key => !['howler.id', 'howler.hash'].includes(key))
@@ -4,9 +4,9 @@ import { Alert, AlertTitle, Autocomplete, Box, Button, CircularProgress, IconBut
4
4
  import api from '@cccsaurora/howler-ui/api';
5
5
  import 'chartjs-adapter-dayjs-4';
6
6
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
7
- import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
- import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
9
7
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
8
+ import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
9
+ import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
10
10
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
11
11
  import useMyChart from '@cccsaurora/howler-ui/components/hooks/useMyChart';
12
12
  import dayjs from 'dayjs';
@@ -38,12 +38,12 @@ const HitGraph = () => {
38
38
  const setQuery = useContextSelector(ParameterContext, ctx => ctx.setQuery);
39
39
  const span = useContextSelector(ParameterContext, ctx => ctx.span);
40
40
  const views = useContextSelector(ParameterContext, ctx => ctx.views);
41
- const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
42
- const addHitToSelection = useContextSelector(HitContext, ctx => ctx.addHitToSelection);
43
- const removeHitFromSelection = useContextSelector(HitContext, ctx => ctx.removeHitFromSelection);
44
- const error = useContextSelector(HitSearchContext, ctx => ctx.error);
45
- const response = useContextSelector(HitSearchContext, ctx => ctx.response);
46
- const getFilters = useContextSelector(HitSearchContext, ctx => ctx.getFilters);
41
+ const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
42
+ const addHitToSelection = useContextSelector(RecordContext, ctx => ctx.addRecordToSelection);
43
+ const removeHitFromSelection = useContextSelector(RecordContext, ctx => ctx.removeRecordFromSelection);
44
+ const error = useContextSelector(RecordSearchContext, ctx => ctx.error);
45
+ const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
46
+ const getFilters = useContextSelector(RecordSearchContext, ctx => ctx.getFilters);
47
47
  const chartRef = useRef();
48
48
  const [loading, setLoading] = useState(false);
49
49
  const [filterField, setFilterField] = useState(FILTER_FIELDS[0]);
@@ -0,0 +1,9 @@
1
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
+ import { type FC } from 'react';
3
+ declare const AnalyticLink: FC<{
4
+ hit: Hit;
5
+ lazy?: boolean;
6
+ compressed?: boolean;
7
+ alignSelf?: string;
8
+ }>;
9
+ export default AnalyticLink;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Typography } from '@mui/material';
3
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
4
+ import { useEffect, useState } from 'react';
5
+ import { Link } from 'react-router-dom';
6
+ const AnalyticLink = ({ hit, lazy = false, compressed, alignSelf = 'start' }) => {
7
+ const { getMatchingAnalytic } = useMatchers(lazy);
8
+ const [analyticId, setAnalyticId] = useState();
9
+ useEffect(() => {
10
+ if (!hit?.howler.analytic) {
11
+ return;
12
+ }
13
+ getMatchingAnalytic(hit).then(analytic => setAnalyticId(analytic?.analytic_id));
14
+ // eslint-disable-next-line react-hooks/exhaustive-deps
15
+ }, [hit?.howler.analytic]);
16
+ return (_jsxs(Typography, { variant: compressed ? 'body1' : 'h6', fontWeight: compressed && 'bold', sx: { alignSelf, '& a': { color: 'text.primary' } }, children: [analyticId ? (_jsx(Link, { to: `/analytics/${analyticId}`, target: "_blank", rel: "noopener noreferrer", onAuxClick: e => {
17
+ e.stopPropagation();
18
+ }, onClick: e => {
19
+ e.stopPropagation();
20
+ }, children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }));
21
+ };
22
+ export default AnalyticLink;
@@ -1,12 +1,11 @@
1
1
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
2
  import type { FC } from 'react';
3
3
  import { HitLayout } from '../HitLayout';
4
- type AssignedProps = FC<{
4
+ declare const Assigned: FC<{
5
5
  hit: Hit;
6
6
  layout: HitLayout;
7
7
  hideLabel?: boolean;
8
- showAssigned?: boolean;
9
8
  showAssessor?: boolean;
9
+ showAssigned?: boolean;
10
10
  }>;
11
- declare const HitUsers: AssignedProps;
12
- export default HitUsers;
11
+ export default Assigned;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { avatarClasses, AvatarGroup, Chip, Divider, Stack, Typography } from '@mui/material';
3
+ import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
4
+ import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
5
+ import HowlerAvatar from '@cccsaurora/howler-ui/components/elements/display/HowlerAvatar';
6
+ import { uniq } from 'lodash-es';
7
+ import { useContext } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { HitLayout } from '../HitLayout';
10
+ const AvatarChip = ({ userId, noUser, placeholder, layout, hideLabel }) => {
11
+ const userAvatar = (_jsx(HowlerAvatar, { userId: userId, sx: { height: layout !== HitLayout.COMFY ? 24 : 32, width: layout !== HitLayout.COMFY ? 24 : 32 } }));
12
+ return hideLabel ? (userAvatar) : (_jsx(Chip, { variant: "outlined", sx: {
13
+ width: 'fit-content',
14
+ '& .MuiChip-icon': {
15
+ marginLeft: 0
16
+ }
17
+ }, icon: userAvatar, label: userId && userId !== noUser ? userId : placeholder, size: layout !== HitLayout.COMFY ? 'small' : 'medium' }));
18
+ };
19
+ const Assigned = ({ hit, layout, hideLabel = false, showAssessor = false, showAssigned = false }) => {
20
+ const { t } = useTranslation();
21
+ const { user } = useAppUser();
22
+ const { viewers } = useContext(SocketContext);
23
+ const hitViewers = uniq(viewers[hit?.howler?.id] ?? []).filter(viewer => viewer !== user.username);
24
+ const assessorVisible = showAssessor || hit.howler.assessment != null;
25
+ const assigneeVisible = !hit.howler.assessor && (showAssigned || hit.howler.assignment !== 'unassigned');
26
+ return (_jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsxs(Stack, { display: "grid", gridTemplateColumns: "repeat(2, 1fr)", alignItems: "center", columnGap: 0.5, rowGap: 0.25, children: [assigneeVisible && (_jsxs(_Fragment, { children: [!hideLabel && _jsxs(Typography, { variant: "caption", children: [t('app.drawer.hit.assignment.assignee'), ":"] }), _jsx(AvatarChip, { userId: hit?.howler.assignment, noUser: "unassigned", placeholder: t('app.drawer.hit.assignment.unassigned.name'), layout: layout, hideLabel: hideLabel })] })), assessorVisible && (_jsxs(_Fragment, { children: [!hideLabel && _jsxs(Typography, { variant: "caption", children: [t('app.drawer.hit.assessment.assessor'), ":"] }), _jsx(AvatarChip, { userId: hit.howler.assessor, noUser: "unknown", placeholder: t('app.drawer.hit.assessment.unknown.name'), layout: layout, hideLabel: hideLabel })] }))] }), hitViewers.length > 0 && hideLabel && _jsx(Divider, { orientation: "vertical", flexItem: true, variant: "middle" }), _jsx(AvatarGroup, { max: 3, sx: { [`.${avatarClasses.root}`]: { border: 0, marginLeft: 0.5 } }, componentsProps: {
27
+ additionalAvatar: {
28
+ sx: {
29
+ height: layout !== HitLayout.COMFY ? 24 : 32,
30
+ width: layout !== HitLayout.COMFY ? 24 : 32,
31
+ fontSize: '12px'
32
+ }
33
+ }
34
+ }, children: hitViewers.map(viewer => (_jsx(HowlerAvatar, { userId: viewer, sx: { height: layout !== HitLayout.COMFY ? 24 : 32, width: layout !== HitLayout.COMFY ? 24 : 32 } }, viewer))) })] }));
35
+ };
36
+ export default Assigned;