@cccsaurora/howler-ui 2.19.0-cases.863 → 2.19.0-dev.1000

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 (397) hide show
  1. package/api/index.d.ts +0 -4
  2. package/api/index.js +2 -10
  3. package/api/search/facet/hit.d.ts +3 -1
  4. package/api/search/facet/index.d.ts +1 -3
  5. package/api/search/index.d.ts +1 -2
  6. package/api/search/index.js +1 -2
  7. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  8. package/components/app/App.d.ts +1 -0
  9. package/components/app/App.js +20 -42
  10. package/components/app/hooks/useMatchers.d.ts +1 -1
  11. package/components/app/hooks/useMatchers.js +11 -23
  12. package/components/app/hooks/useMatchers.test.js +22 -22
  13. package/components/app/hooks/useTitle.js +5 -5
  14. package/components/app/providers/FavouritesProvider.js +2 -6
  15. package/components/app/providers/HitProvider.d.ts +22 -0
  16. package/components/app/providers/{RecordProvider.js → HitProvider.js} +42 -42
  17. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  18. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  19. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +71 -52
  20. package/components/app/providers/ModalProvider.d.ts +1 -1
  21. package/components/app/providers/ModalProvider.js +5 -1
  22. package/components/app/providers/ParameterProvider.d.ts +2 -9
  23. package/components/app/providers/ParameterProvider.js +240 -165
  24. package/components/app/providers/ParameterProvider.test.js +94 -346
  25. package/components/app/providers/SocketProvider.d.ts +2 -11
  26. package/components/app/providers/SocketProvider.js +5 -18
  27. package/components/app/providers/UserListProvider.js +8 -28
  28. package/components/elements/PluginTypography.d.ts +1 -2
  29. package/components/elements/PluginTypography.js +2 -3
  30. package/components/elements/UserList.d.ts +2 -5
  31. package/components/elements/UserList.js +8 -18
  32. package/components/elements/addons/buttons/CustomButton.test.js +106 -0
  33. package/components/elements/addons/layout/FlexOne.test.js +30 -0
  34. package/components/elements/addons/lists/TuiListBase.js +4 -0
  35. package/components/elements/addons/lists/TuiListElement.js +6 -3
  36. package/components/elements/addons/lists/index.d.ts +1 -0
  37. package/components/elements/addons/search/SearchPagination.test.js +80 -0
  38. package/components/elements/addons/search/SearchTotal.test.js +45 -0
  39. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  40. package/components/elements/display/ChipPopper.d.ts +1 -1
  41. package/components/elements/display/ChipPopper.js +5 -5
  42. package/components/elements/display/DocumentationButton.test.js +84 -0
  43. package/components/elements/display/DynamicTabs.test.js +93 -0
  44. package/components/elements/display/HowlerAvatar.js +2 -1
  45. package/components/elements/display/HowlerCard.js +1 -1
  46. package/components/elements/display/HowlerCard.test.js +55 -0
  47. package/components/elements/display/Image.test.js +79 -0
  48. package/components/elements/display/Markdown.js +9 -2
  49. package/components/elements/display/Modal.js +0 -2
  50. package/components/elements/display/QueryResultText.test.js +48 -0
  51. package/components/elements/display/TextDivider.test.js +43 -0
  52. package/components/elements/display/TypingIndicator.test.js +33 -0
  53. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  54. package/components/elements/display/icons/BundleButton.js +32 -0
  55. package/components/elements/display/modals/ConfirmDeleteModal.d.ts +4 -0
  56. package/components/elements/display/modals/ConfirmDeleteModal.js +4 -2
  57. package/components/elements/hit/HitActions.js +4 -4
  58. package/components/elements/hit/HitBanner.d.ts +0 -1
  59. package/components/elements/hit/HitBanner.js +49 -31
  60. package/components/elements/hit/HitCard.d.ts +0 -2
  61. package/components/elements/hit/HitCard.js +7 -7
  62. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  63. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  64. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  65. package/components/elements/hit/HitLabels.js +2 -2
  66. package/components/elements/hit/HitOutline.d.ts +0 -1
  67. package/components/elements/hit/HitOutline.js +3 -3
  68. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  69. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  70. package/components/elements/hit/HitRelated.d.ts +6 -0
  71. package/components/elements/hit/HitRelated.js +7 -0
  72. package/components/elements/hit/HitSummary.d.ts +1 -2
  73. package/components/elements/hit/HitSummary.js +8 -8
  74. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  75. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  76. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  77. package/components/elements/hit/elements/{Assigned.d.ts → HitUsers.d.ts} +5 -2
  78. package/components/elements/hit/elements/HitUsers.js +33 -0
  79. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  80. package/components/elements/view/ViewTitle.d.ts +0 -1
  81. package/components/elements/view/ViewTitle.js +2 -9
  82. package/components/hooks/useHitActions.d.ts +1 -1
  83. package/components/hooks/useHitActions.js +4 -4
  84. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  85. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  86. package/components/hooks/useMyPreferences.js +3 -11
  87. package/components/hooks/useMySearch.js +2 -2
  88. package/components/hooks/useMySitemap.js +1 -4
  89. package/components/hooks/useMyTheme.js +2 -9
  90. package/components/routes/403.test.js +27 -0
  91. package/components/routes/404.test.js +26 -0
  92. package/components/routes/ErrorBoundary.test.js +43 -0
  93. package/components/routes/ErrorOccured.test.js +35 -0
  94. package/components/routes/Logout.test.js +59 -0
  95. package/components/routes/action/edit/ActionEditor.js +2 -2
  96. package/components/routes/action/view/ActionDetails.js +4 -2
  97. package/components/routes/action/view/ActionSearch.js +12 -7
  98. package/components/routes/advanced/QueryBuilder.js +1 -1
  99. package/components/routes/advanced/QueryEditor.js +3 -3
  100. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  101. package/components/routes/analytics/AnalyticDetails.js +12 -8
  102. package/components/routes/analytics/AnalyticSearch.js +1 -1
  103. package/components/routes/dossiers/DossierEditor.js +2 -2
  104. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  105. package/components/routes/dossiers/Dossiers.js +15 -11
  106. package/components/routes/help/ApiDocumentation.js +1 -1
  107. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  108. package/components/routes/help/BundleDocumentation.js +12 -0
  109. package/components/routes/help/HitBannerDocumentation.js +0 -1
  110. package/components/routes/help/HitDocumentation.js +3 -1
  111. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  112. package/components/routes/help/markdown/en/client.md.js +1 -1
  113. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  114. package/components/routes/help/markdown/fr/client.md.js +1 -1
  115. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  116. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  117. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  118. package/components/routes/hits/search/BundleScroller.js +6 -0
  119. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  120. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  121. package/components/routes/hits/search/HitContextMenu.js +239 -0
  122. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +43 -98
  123. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  124. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  125. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  126. package/components/routes/hits/search/InformationPane.js +63 -50
  127. package/components/routes/hits/search/LayoutSettings.js +3 -3
  128. package/components/routes/hits/search/QuerySettings.js +1 -2
  129. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  130. package/components/routes/hits/search/SearchPane.js +49 -26
  131. package/components/routes/hits/search/ViewLink.js +3 -3
  132. package/components/routes/hits/search/ViewLink.test.js +8 -8
  133. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  134. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  135. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  136. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  137. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  138. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +9 -11
  139. package/components/routes/hits/search/shared/CustomSpan.js +7 -7
  140. package/components/routes/hits/search/shared/CustomSpan.test.js +170 -0
  141. package/components/routes/hits/view/HitViewer.js +13 -12
  142. package/components/routes/home/AddNewCard.test.js +68 -0
  143. package/components/routes/home/ViewCard.js +41 -47
  144. package/components/routes/home/ViewRefresh.test.js +79 -0
  145. package/components/routes/overviews/OverviewCard.test.js +108 -0
  146. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  147. package/components/routes/overviews/OverviewViewer.js +14 -10
  148. package/components/routes/overviews/Overviews.js +15 -11
  149. package/components/routes/settings/LocalSection.js +2 -1
  150. package/components/routes/settings/SettingsSection.test.js +19 -0
  151. package/components/routes/templates/TemplateCard.d.ts +3 -1
  152. package/components/routes/templates/TemplateCard.js +8 -4
  153. package/components/routes/templates/TemplateCard.test.d.ts +1 -0
  154. package/components/routes/templates/TemplateCard.test.js +115 -0
  155. package/components/routes/templates/TemplateViewer.js +57 -16
  156. package/components/routes/templates/Templates.js +23 -6
  157. package/components/routes/views/ViewComposer.js +19 -46
  158. package/components/routes/views/Views.js +8 -4
  159. package/locales/en/translation.json +17 -117
  160. package/locales/fr/translation.json +19 -119
  161. package/models/WithMetadata.d.ts +1 -2
  162. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  163. package/models/entities/generated/Hit.d.ts +1 -1
  164. package/models/entities/generated/Howler.d.ts +6 -0
  165. package/models/entities/generated/Rule.d.ts +9 -6
  166. package/models/entities/generated/Threat.d.ts +2 -2
  167. package/models/entities/generated/UserUser.d.ts +2 -0
  168. package/models/entities/generated/View.d.ts +0 -1
  169. package/package.json +112 -132
  170. package/plugins/clue/components/ClueTypography.js +2 -2
  171. package/plugins/clue/utils.d.ts +1 -2
  172. package/rest/FetchClient.test.d.ts +1 -0
  173. package/rest/FetchClient.test.js +81 -0
  174. package/setupTests.js +5 -0
  175. package/tests/mocks.d.ts +1 -11
  176. package/tests/mocks.js +7 -12
  177. package/tests/server-handlers.js +1 -6
  178. package/tests/utils.d.ts +0 -4
  179. package/tests/utils.js +0 -20
  180. package/utils/Throttler.test.d.ts +1 -0
  181. package/utils/Throttler.test.js +135 -0
  182. package/utils/actionUtils.test.d.ts +1 -0
  183. package/utils/actionUtils.test.js +140 -0
  184. package/utils/constants.d.ts +4 -4
  185. package/utils/constants.js +1 -6
  186. package/utils/hitFunctions.d.ts +1 -2
  187. package/utils/hitFunctions.js +4 -4
  188. package/utils/hitFunctions.test.d.ts +1 -0
  189. package/utils/hitFunctions.test.js +52 -0
  190. package/utils/localStorage.test.d.ts +1 -0
  191. package/utils/localStorage.test.js +96 -0
  192. package/utils/menuUtils.test.d.ts +1 -0
  193. package/utils/menuUtils.test.js +198 -0
  194. package/utils/sessionStorage.test.d.ts +1 -0
  195. package/utils/sessionStorage.test.js +81 -0
  196. package/utils/socketUtils.d.ts +0 -14
  197. package/utils/socketUtils.js +1 -17
  198. package/utils/socketUtils.test.js +16 -49
  199. package/utils/stringUtils.test.d.ts +1 -0
  200. package/utils/stringUtils.test.js +159 -0
  201. package/utils/utils.test.d.ts +1 -0
  202. package/utils/utils.test.js +292 -0
  203. package/utils/viewUtils.js +0 -3
  204. package/utils/viewUtils.test.d.ts +1 -0
  205. package/utils/viewUtils.test.js +45 -0
  206. package/utils/xsrf.test.d.ts +1 -0
  207. package/utils/xsrf.test.js +41 -0
  208. package/api/search/case.d.ts +0 -4
  209. package/api/search/case.js +0 -8
  210. package/api/socket/index.d.ts +0 -3
  211. package/api/socket/index.js +0 -6
  212. package/api/socket/viewers.d.ts +0 -2
  213. package/api/socket/viewers.js +0 -8
  214. package/api/socket/viewers.test.js +0 -44
  215. package/api/v2/case/index.d.ts +0 -9
  216. package/api/v2/case/index.js +0 -21
  217. package/api/v2/case/items.d.ts +0 -6
  218. package/api/v2/case/items.js +0 -18
  219. package/api/v2/case/rules.d.ts +0 -6
  220. package/api/v2/case/rules.js +0 -18
  221. package/api/v2/index.d.ts +0 -4
  222. package/api/v2/index.js +0 -6
  223. package/api/v2/search/facet.d.ts +0 -3
  224. package/api/v2/search/facet.js +0 -12
  225. package/api/v2/search/index.d.ts +0 -5
  226. package/api/v2/search/index.js +0 -24
  227. package/components/app/providers/RecordProvider.d.ts +0 -23
  228. package/components/elements/ContextMenu.d.ts +0 -56
  229. package/components/elements/ContextMenu.js +0 -109
  230. package/components/elements/ContextMenu.test.js +0 -215
  231. package/components/elements/ObjectDetails.d.ts +0 -6
  232. package/components/elements/case/CaseCard.d.ts +0 -12
  233. package/components/elements/case/CaseCard.js +0 -42
  234. package/components/elements/case/CasePreview.d.ts +0 -6
  235. package/components/elements/case/CasePreview.js +0 -17
  236. package/components/elements/case/StatusIcon.d.ts +0 -5
  237. package/components/elements/case/StatusIcon.js +0 -13
  238. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -9
  239. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  240. package/components/elements/hit/elements/Assigned.js +0 -34
  241. package/components/elements/hit/elements/Assigned.test.js +0 -65
  242. package/components/elements/hit/related/RelatedRecords.js +0 -63
  243. package/components/elements/observable/ObservableCard.d.ts +0 -6
  244. package/components/elements/observable/ObservableCard.js +0 -22
  245. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  246. package/components/elements/observable/ObservablePreview.js +0 -12
  247. package/components/elements/record/RecordContextMenu.js +0 -268
  248. package/components/elements/record/RecordRelated.d.ts +0 -7
  249. package/components/elements/record/RecordRelated.js +0 -34
  250. package/components/hooks/useRelatedRecords.d.ts +0 -13
  251. package/components/hooks/useRelatedRecords.js +0 -32
  252. package/components/routes/cases/CaseViewer.d.ts +0 -2
  253. package/components/routes/cases/CaseViewer.js +0 -44
  254. package/components/routes/cases/CaseViewer.test.js +0 -133
  255. package/components/routes/cases/Cases.d.ts +0 -2
  256. package/components/routes/cases/Cases.js +0 -148
  257. package/components/routes/cases/constants.d.ts +0 -6
  258. package/components/routes/cases/constants.js +0 -6
  259. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  260. package/components/routes/cases/detail/AlertPanel.js +0 -33
  261. package/components/routes/cases/detail/CaseAssets.d.ts +0 -11
  262. package/components/routes/cases/detail/CaseAssets.js +0 -104
  263. package/components/routes/cases/detail/CaseAssets.test.js +0 -167
  264. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  265. package/components/routes/cases/detail/CaseDashboard.js +0 -66
  266. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  267. package/components/routes/cases/detail/CaseDetails.js +0 -70
  268. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  269. package/components/routes/cases/detail/CaseOverview.js +0 -43
  270. package/components/routes/cases/detail/CaseRules.d.ts +0 -7
  271. package/components/routes/cases/detail/CaseRules.js +0 -57
  272. package/components/routes/cases/detail/CaseRules.test.js +0 -221
  273. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
  274. package/components/routes/cases/detail/CaseSidebar.js +0 -107
  275. package/components/routes/cases/detail/CaseSidebar.test.js +0 -266
  276. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  277. package/components/routes/cases/detail/CaseTask.js +0 -66
  278. package/components/routes/cases/detail/CaseTimeline.d.ts +0 -12
  279. package/components/routes/cases/detail/CaseTimeline.js +0 -106
  280. package/components/routes/cases/detail/CaseTimeline.test.js +0 -320
  281. package/components/routes/cases/detail/CreateRuleDialog.d.ts +0 -9
  282. package/components/routes/cases/detail/CreateRuleDialog.js +0 -163
  283. package/components/routes/cases/detail/CreateRuleDialog.test.js +0 -259
  284. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  285. package/components/routes/cases/detail/ItemPage.js +0 -95
  286. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  287. package/components/routes/cases/detail/RelatedCasePanel.js +0 -34
  288. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  289. package/components/routes/cases/detail/TaskPanel.js +0 -52
  290. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -11
  291. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -24
  292. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  293. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -26
  294. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  295. package/components/routes/cases/detail/assets/Asset.js +0 -12
  296. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  297. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -20
  298. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -83
  299. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +0 -295
  300. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
  301. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -103
  302. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -363
  303. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +0 -25
  304. package/components/routes/cases/detail/sidebar/FolderEntry.js +0 -88
  305. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +0 -206
  306. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +0 -5
  307. package/components/routes/cases/detail/sidebar/RootDropZone.js +0 -33
  308. package/components/routes/cases/detail/sidebar/types.d.ts +0 -9
  309. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  310. package/components/routes/cases/detail/sidebar/utils.js +0 -29
  311. package/components/routes/cases/detail/sidebar/utils.test.js +0 -82
  312. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  313. package/components/routes/cases/hooks/useCase.js +0 -69
  314. package/components/routes/cases/hooks/useCase.test.js +0 -141
  315. package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
  316. package/components/routes/cases/modals/AddToCaseModal.js +0 -59
  317. package/components/routes/cases/modals/AddToCaseModal.test.js +0 -313
  318. package/components/routes/cases/modals/CaseRecordRow.d.ts +0 -9
  319. package/components/routes/cases/modals/CaseRecordRow.js +0 -15
  320. package/components/routes/cases/modals/CreateCaseModal.d.ts +0 -7
  321. package/components/routes/cases/modals/CreateCaseModal.js +0 -55
  322. package/components/routes/cases/modals/CreateCaseModal.test.js +0 -358
  323. package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
  324. package/components/routes/cases/modals/RenameItemModal.js +0 -48
  325. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  326. package/components/routes/cases/modals/ResolveModal.js +0 -115
  327. package/components/routes/cases/modals/ResolveModal.test.js +0 -394
  328. package/components/routes/cases/modals/hooks.d.ts +0 -7
  329. package/components/routes/cases/modals/hooks.js +0 -44
  330. package/components/routes/cases/modals/types.d.ts +0 -5
  331. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +0 -6
  332. package/components/routes/cases/search/CaseAssigneeFilter.js +0 -33
  333. package/components/routes/cases/search/CaseAssigneeFilter.test.js +0 -127
  334. package/components/routes/cases/search/CaseDateFilter.d.ts +0 -13
  335. package/components/routes/cases/search/CaseDateFilter.js +0 -26
  336. package/components/routes/cases/search/CaseDateFilter.test.js +0 -115
  337. package/components/routes/cases/search/CaseStatusFilter.d.ts +0 -6
  338. package/components/routes/cases/search/CaseStatusFilter.js +0 -13
  339. package/components/routes/cases/search/CaseStatusFilter.test.js +0 -86
  340. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  341. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  342. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  343. package/components/routes/observables/ObservableViewer.js +0 -27
  344. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  345. package/models/entities/generated/Case.d.ts +0 -28
  346. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  347. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  348. package/models/entities/generated/EmailParent.d.ts +0 -19
  349. package/models/entities/generated/Enrichments.d.ts +0 -7
  350. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  351. package/models/entities/generated/HttpResponse.d.ts +0 -11
  352. package/models/entities/generated/Item.d.ts +0 -9
  353. package/models/entities/generated/Observable.d.ts +0 -85
  354. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  355. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  356. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  357. package/models/entities/generated/ObservableFile.d.ts +0 -36
  358. package/models/entities/generated/ObservableHowler.d.ts +0 -42
  359. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  360. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  361. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  362. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  363. package/models/entities/generated/ObservableSource.d.ts +0 -23
  364. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  365. package/models/entities/generated/ObservableTls.d.ts +0 -12
  366. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  367. package/models/entities/generated/Task.d.ts +0 -10
  368. package/models/socket/CaseUpdate.d.ts +0 -5
  369. package/models/socket/ViewersUpdate.d.ts +0 -4
  370. package/utils/typeUtils.d.ts +0 -7
  371. package/utils/typeUtils.js +0 -27
  372. /package/{api/socket/viewers.test.d.ts → components/app/providers/HitSearchProvider.test.d.ts} +0 -0
  373. /package/components/{app/providers/RecordSearchProvider.test.d.ts → elements/addons/buttons/CustomButton.test.d.ts} +0 -0
  374. /package/components/elements/{ContextMenu.test.d.ts → addons/layout/FlexOne.test.d.ts} +0 -0
  375. /package/components/elements/{hit/elements/Assigned.test.d.ts → addons/search/SearchPagination.test.d.ts} +0 -0
  376. /package/components/elements/{record/RecordContextMenu.test.d.ts → addons/search/SearchTotal.test.d.ts} +0 -0
  377. /package/components/{routes/cases/CaseViewer.test.d.ts → elements/display/DocumentationButton.test.d.ts} +0 -0
  378. /package/components/{routes/cases/detail/CaseAssets.test.d.ts → elements/display/DynamicTabs.test.d.ts} +0 -0
  379. /package/components/{routes/cases/detail/CaseRules.test.d.ts → elements/display/HowlerCard.test.d.ts} +0 -0
  380. /package/components/{routes/cases/detail/CaseSidebar.test.d.ts → elements/display/Image.test.d.ts} +0 -0
  381. /package/components/{routes/cases/detail/CaseTimeline.test.d.ts → elements/display/QueryResultText.test.d.ts} +0 -0
  382. /package/components/{routes/cases/detail/CreateRuleDialog.test.d.ts → elements/display/TextDivider.test.d.ts} +0 -0
  383. /package/components/{routes/cases/detail/assets/Asset.test.d.ts → elements/display/TypingIndicator.test.d.ts} +0 -0
  384. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  385. /package/components/routes/{cases/detail/sidebar/CaseFolder.test.d.ts → 403.test.d.ts} +0 -0
  386. /package/components/routes/{cases/detail/sidebar/CaseFolderContextMenu.test.d.ts → 404.test.d.ts} +0 -0
  387. /package/components/routes/{cases/detail/sidebar/FolderEntry.test.d.ts → ErrorBoundary.test.d.ts} +0 -0
  388. /package/components/routes/{cases/detail/sidebar/utils.test.d.ts → ErrorOccured.test.d.ts} +0 -0
  389. /package/components/routes/{cases/hooks/useCase.test.d.ts → Logout.test.d.ts} +0 -0
  390. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  391. /package/components/routes/{cases/modals/AddToCaseModal.test.d.ts → hits/search/HitContextMenu.test.d.ts} +0 -0
  392. /package/components/routes/{cases/modals/CreateCaseModal.test.d.ts → hits/search/shared/CustomSpan.test.d.ts} +0 -0
  393. /package/components/routes/{cases/modals/ResolveModal.test.d.ts → home/AddNewCard.test.d.ts} +0 -0
  394. /package/components/routes/{cases/search/CaseAssigneeFilter.test.d.ts → home/ViewRefresh.test.d.ts} +0 -0
  395. /package/components/routes/{cases/search/CaseDateFilter.test.d.ts → overviews/OverviewCard.test.d.ts} +0 -0
  396. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
  397. /package/components/routes/{cases/search/CaseStatusFilter.test.d.ts → settings/SettingsSection.test.d.ts} +0 -0
@@ -1 +1 @@
1
- export default "# Documentation du client Howler\n\nCette documentation explique comment interagir avec l'API howler en utilisant le client howler dans les environnements de d\u00e9veloppement Java et Python. Nous d\u00e9crirons le processus de base de la cr\u00e9ation d'un nouveau hit dans chaque environnement ainsi que la recherche dans howler des hits correspondant \u00e0 votre requ\u00eate.\n\n## Mise en route\n\n### Installation\n\nAfin d'utiliser le client howler, vous devez le lister comme une d\u00e9pendance dans votre projet.\n\n#### **Python**\n\nIl suffit de l'installer \u00e0 l'aide de pip:\n\n```bash\npip install howler-client\n```\n\nVous pouvez \u00e9galement l'ajouter \u00e0 votre requirements.txt, ou \u00e0 tout autre syst\u00e8me de gestion des d\u00e9pendances que vous utilisez.\n\n### Authentification\n\nComme indiqu\u00e9 dans la [Documentation sur l'authentification](/help/auth), les utilisateurs peuvent s'authentifier de diff\u00e9rentes mani\u00e8res. Pour interfacer avec le client howler, cependant, le flux sugg\u00e9r\u00e9 est d'utiliser une cl\u00e9 API. Avant de commencer, g\u00e9n\u00e9rons une cl\u00e9.\n\n1. Ouvrez l'interface utilisateur Howler avec laquelle vous souhaitez vous interfacer.\n2. Connectez-vous, puis cliquez sur votre profil en haut \u00e0 droite.\n3. Dans le menu utilisateur, cliquez sur Param\u00e8tres.\n4. Sous S\u00e9curit\u00e9 de l'utilisateur, appuyez sur l'ic\u00f4ne (+) sur la ligne Cl\u00e9s API.\n5. Nommez votre cl\u00e9 et donnez-lui les autorisations n\u00e9cessaires.\n6. Appuyez sur Create (Cr\u00e9er) et copiez la cha\u00eene fournie dans un endroit s\u00fbr. \\*\\*Vous ne reverrez plus cette cha\u00eene.\n\nCette cl\u00e9 API sera fournie \u00e0 votre code par la suite.\n\n## Client Python\n\nPour se connecter \u00e0 howler en utilisant le client python, il y a un processus assez simple \u00e0 suivre:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtenez-le \u00e0 partir de la page des param\u00e8tres de l'utilisateur de l'interface utilisateur de Howler.\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\nVoil\u00e0, c'est fait ! Vous pouvez maintenant utiliser l'objet `howler` pour interagir avec le serveur. A quoi cela ressemble-t-il en r\u00e9alit\u00e9?\n\n### Cr\u00e9er des hits en Python\n\nPour le client python, vous pouvez cr\u00e9er des hits en utilisant les fonctions `howler.hit.create` ou `howler.hit.create_from_map`.\n\n#### `create`\n\nCette fonction prend un seul argument - soit un hit unique, soit une liste de hits, conforme au [Howler Schema] (/help/hit?tab=schema). Voici un exemple simple :\n\n```python\n# Quelques donn\u00e9es bidons au format Howler Schema\nexemple_hit = {\n \"howler\" : {\n \"analytic\" : \"exemple\",\n \"score\" : 10.0\n },\n \"event\" : {\n \"reason\" : \"Exemple hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nVous pouvez \u00e9galement ing\u00e9rer des donn\u00e9es dans un format plat :\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: Le nom de l'outil d'analyse qui cr\u00e9e le hit\n- `map`: Une correspondance entre les donn\u00e9es brutes que vous avez et le sch\u00e9ma howler\n - Le format est un dictionnaire o\u00f9 les cl\u00e9s sont le chemin aplati des donn\u00e9es brutes, et les valeurs sont une liste de chemins aplatis pour les champs de Howler dans lesquels les donn\u00e9es seront copi\u00e9es.\n- `documents`: Les donn\u00e9es brutes que vous voulez ajouter \u00e0 Howler.\n\nVoici un exemple simple:\n\n```python\n# La correspondance entre nos donn\u00e9es et le sch\u00e9ma de Howler\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Quelques fausses donn\u00e9es dans un format personnalis\u00e9 que nous voulons ajouter \u00e0 howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Notez que le troisi\u00e8me argument est de type liste!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Interroger les hits en Python\n\nL'interrogation des hits avec le client python howler se fait en utilisant la fonction `howler.search.hit`. Elle poss\u00e8de un certain nombre d'arguments obligatoires et optionnels:\n\n- Obligatoire:\n - `query`: requ\u00eate lucene (cha\u00eene)\n- Facultatif: `filters`: requ\u00eate lucene (cha\u00eene):\n - `filters`: Requ\u00eates lucene additionnelles utilis\u00e9es pour filtrer les donn\u00e9es (liste de cha\u00eenes)\n - `fl`: Liste des champs \u00e0 retourner (cha\u00eene de champs s\u00e9par\u00e9s par des virgules)\n - `offset`: Offset auquel les \u00e9l\u00e9ments de la requ\u00eate doivent commencer (entier)\n - `rows`: Nombre d'enregistrements \u00e0 retourner (entier)\n - `sort`: Champ utilis\u00e9 pour le tri avec direction (cha\u00eene: ex. 'id desc')\n - `timeout`: Nombre maximum de millisecondes d'ex\u00e9cution de la requ\u00eate (entier)\n - `use_archive`: Interroge \u00e9galement l'archive\n - `track_total_hits`: Nombre de hits \u00e0 suivre (par d\u00e9faut: 10k)\n\nVoici quelques exemples de requ\u00eates:\n\n```python\n# Rechercher tous les hits cr\u00e9\u00e9s par assemblyline, afficher les 50 premiers et ne renvoyer que leurs identifiants.\nhowler.search.hit(\"howler.analytic:assemblyline\", fl=\"howler.id\", rows=50)\n\n# Recherche de toutes les occurrences r\u00e9solues cr\u00e9\u00e9es au cours des cinq derniers jours, avec indication de leur identifiant et de l'analyste qui les a cr\u00e9\u00e9es. N'en afficher que dix, d\u00e9cal\u00e9s de 40\nhowler.search.hit(\"howler.status:resolved\", filters=['event.created:[now-5d TO now]'] fl=\"howler.id,howler.analytic\", rows=10, offset=40)\n\n# Recherche de tous les r\u00e9sultats, d\u00e9lai d'attente si la requ\u00eate prend plus de 100 ms\nhowler.search.hit(\"howler.id:*\", track_total_hits=100000000, timeout=100, use_archive=True)\n```\n\n### Mise \u00e0 jour des r\u00e9sultats\n\nAfin de mettre \u00e0 jour les hits, il existe un certain nombre de fonctions support\u00e9es :\n\n- `howler.hit.update(...)`\n- `howler.hit.update_by_query(...)`\n- `howler.hit.overwrite(...)`\n\n#### `update()`\n\nSi vous souhaitez mettre \u00e0 jour un hit de mani\u00e8re transactionnelle, vous pouvez utiliser le code suivant:\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.update(hit_to_update[\"howler\"][\"id\"], [(UPDATE_SET, \"howler.score\", hit_to_update[\"howler\"][\"score\"] + 100)])\n```\n\nLes op\u00e9rations suivantes peuvent \u00eatre ex\u00e9cut\u00e9es pour mettre \u00e0 jour un r\u00e9sultat.\n\n**Op\u00e9rations de liste:**\n\n- `UPDATE_APPEND` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e\n- `UPDATE_APPEND_IF_MISSING` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e si la valeur n'est pas d\u00e9j\u00e0 dans la liste.\n- `UPDATE_REMOVE` : Supprime une valeur donn\u00e9e d'une liste\n\n**Op\u00e9rations num\u00e9riques:**\n\n- `UPDATE_DEC` : Diminue une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_INC` : Incr\u00e9mente une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_MAX` : Fixe une valeur num\u00e9rique au maximum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n- `UPDATE_MIN` : Fixe une valeur num\u00e9rique au minimum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n\n**Op\u00e9rations polyvalentes:**\n\n- `UPDATE_SET` : Fixe la valeur d'un champ \u00e0 la valeur donn\u00e9e.\n- `UPDATE_DELETE` : Supprime la valeur d'un champ donn\u00e9\n\n#### `update_by_query()`\n\nCette fonction vous permet de mettre \u00e0 jour un grand nombre d'occurrences \u00e0 l'aide d'une requ\u00eate :\n\n```python\nclient.hit.update_by_query(f'howler.analytic : \"Exemple d\\'analytic\"', [(UPDATE_INC, \"howler.score\", 100)])\n```\n\nLes m\u00eames op\u00e9rations que dans `update()` peuvent \u00eatre utilis\u00e9es.\n\n### `overwrite()`\n\nCette fonction vous permet d'\u00e9craser directement un hit avec un objet hit partiel. C'est la plus facile \u00e0 utiliser, mais elle perd une partie de la validation et du traitement suppl\u00e9mentaire des fonctions de mise \u00e0 jour.\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.overwrite(hit_to_update[\"howler\"][\"id\"], {\"source.ip\" : \"127.0.0.1\", \u201cdestination.ip\u201d : \"8.8.8.8\"})\n```\n"
1
+ export default "# Documentation du client Howler\n\nCette documentation explique comment interagir avec l'API howler en utilisant le client howler dans les environnements de d\u00e9veloppement Java et Python. Nous d\u00e9crirons le processus de base de la cr\u00e9ation d'un nouveau hit dans chaque environnement ainsi que la recherche dans howler des hits correspondant \u00e0 votre requ\u00eate.\n\n## Mise en route\n\n### Installation\n\nAfin d'utiliser le client howler, vous devez le lister comme une d\u00e9pendance dans votre projet.\n\n#### **Python**\n\nIl suffit de l'installer \u00e0 l'aide de pip:\n\n```bash\npip install howler-client\n```\n\nVous pouvez \u00e9galement l'ajouter \u00e0 votre requirements.txt, ou \u00e0 tout autre syst\u00e8me de gestion des d\u00e9pendances que vous utilisez.\n\n### Authentification\n\nComme indiqu\u00e9 dans la [Documentation sur l'authentification](/help/auth), les utilisateurs peuvent s'authentifier de diff\u00e9rentes mani\u00e8res. Pour interfacer avec le client howler, cependant, le flux sugg\u00e9r\u00e9 est d'utiliser une cl\u00e9 API. Avant de commencer, g\u00e9n\u00e9rons une cl\u00e9.\n\n1. Ouvrez l'interface utilisateur Howler avec laquelle vous souhaitez vous interfacer.\n2. Connectez-vous, puis cliquez sur votre profil en haut \u00e0 droite.\n3. Dans le menu utilisateur, cliquez sur Param\u00e8tres.\n4. Sous S\u00e9curit\u00e9 de l'utilisateur, appuyez sur l'ic\u00f4ne (+) sur la ligne Cl\u00e9s API.\n5. Nommez votre cl\u00e9 et donnez-lui les autorisations n\u00e9cessaires.\n6. Appuyez sur Create (Cr\u00e9er) et copiez la cha\u00eene fournie dans un endroit s\u00fbr. \\*\\*Vous ne reverrez plus cette cha\u00eene.\n\nCette cl\u00e9 API sera fournie \u00e0 votre code par la suite.\n\n## Client Python\n\nPour se connecter \u00e0 howler en utilisant le client python, il y a un processus assez simple \u00e0 suivre:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtenez-le \u00e0 partir de la page des param\u00e8tres de l'utilisateur de l'interface utilisateur de Howler.\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\nVoil\u00e0, c'est fait ! Vous pouvez maintenant utiliser l'objet `howler` pour interagir avec le serveur. A quoi cela ressemble-t-il en r\u00e9alit\u00e9?\n\n### Cr\u00e9er des hits en Python\n\nPour le client python, vous pouvez cr\u00e9er des hits en utilisant les fonctions `howler.hit.create` ou `howler.hit.create_from_map`.\n\n#### `create`\n\nCette fonction prend un seul argument - soit un hit unique, soit une liste de hits, conforme au [Howler Schema] (/help/hit?tab=schema). Voici un exemple simple :\n\n```python\n# Quelques donn\u00e9es bidons au format Howler Schema\nexemple_hit = {\n \"howler\" : {\n \"analytic\" : \"exemple\",\n \"score\" : 10.0\n },\n \"event\" : {\n \"reason\" : \"Exemple hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nVous pouvez \u00e9galement ing\u00e9rer des donn\u00e9es dans un format plat :\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: Le nom de l'outil d'analyse qui cr\u00e9e le hit\n- `map`: Une correspondance entre les donn\u00e9es brutes que vous avez et le sch\u00e9ma howler\n - Le format est un dictionnaire o\u00f9 les cl\u00e9s sont le chemin aplati des donn\u00e9es brutes, et les valeurs sont une liste de chemins aplatis pour les champs de Howler dans lesquels les donn\u00e9es seront copi\u00e9es.\n- `documents`: Les donn\u00e9es brutes que vous voulez ajouter \u00e0 Howler.\n\nVoici un exemple simple:\n\n```python\n# La correspondance entre nos donn\u00e9es et le sch\u00e9ma de Howler\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Quelques fausses donn\u00e9es dans un format personnalis\u00e9 que nous voulons ajouter \u00e0 howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Notez que le troisi\u00e8me argument est de type liste!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Interroger les hits en Python\n\nL'interrogation des hits avec le client python howler se fait en utilisant la fonction `howler.search.hit`. Elle poss\u00e8de un certain nombre d'arguments obligatoires et optionnels:\n\n- Obligatoire:\n - `query`: requ\u00eate lucene (cha\u00eene)\n- Facultatif: `filters`: requ\u00eate lucene (cha\u00eene):\n - `filters`: Requ\u00eates lucene additionnelles utilis\u00e9es pour filtrer les donn\u00e9es (liste de cha\u00eenes)\n - `fl`: Liste des champs \u00e0 retourner (cha\u00eene de champs s\u00e9par\u00e9s par des virgules)\n - `offset`: Offset auquel les \u00e9l\u00e9ments de la requ\u00eate doivent commencer (entier)\n - `rows`: Nombre d'enregistrements \u00e0 retourner (entier)\n - `sort`: Champ utilis\u00e9 pour le tri avec direction (cha\u00eene: ex. 'id desc')\n - `timeout`: Nombre maximum de millisecondes d'ex\u00e9cution de la requ\u00eate (entier)\n - `track_total_hits`: Nombre de hits \u00e0 suivre (par d\u00e9faut: 10k)\n\nVoici quelques exemples de requ\u00eates:\n\n```python\n# Rechercher tous les hits cr\u00e9\u00e9s par assemblyline, afficher les 50 premiers et ne renvoyer que leurs identifiants.\nhowler.search.hit(\"howler.analytic:assemblyline\", fl=\"howler.id\", rows=50)\n\n# Recherche de toutes les occurrences r\u00e9solues cr\u00e9\u00e9es au cours des cinq derniers jours, avec indication de leur identifiant et de l'analyste qui les a cr\u00e9\u00e9es. N'en afficher que dix, d\u00e9cal\u00e9s de 40\nhowler.search.hit(\"howler.status:resolved\", filters=['event.created:[now-5d TO now]'] fl=\"howler.id,howler.analytic\", rows=10, offset=40)\n\n# Recherche de tous les r\u00e9sultats, d\u00e9lai d'attente si la requ\u00eate prend plus de 100 ms\nhowler.search.hit(\"howler.id:*\", track_total_hits=100000000, timeout=100)\n```\n\n### Mise \u00e0 jour des r\u00e9sultats\n\nAfin de mettre \u00e0 jour les hits, il existe un certain nombre de fonctions support\u00e9es :\n\n- `howler.hit.update(...)`\n- `howler.hit.update_by_query(...)`\n- `howler.hit.overwrite(...)`\n\n#### `update()`\n\nSi vous souhaitez mettre \u00e0 jour un hit de mani\u00e8re transactionnelle, vous pouvez utiliser le code suivant:\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.update(hit_to_update[\"howler\"][\"id\"], [(UPDATE_SET, \"howler.score\", hit_to_update[\"howler\"][\"score\"] + 100)])\n```\n\nLes op\u00e9rations suivantes peuvent \u00eatre ex\u00e9cut\u00e9es pour mettre \u00e0 jour un r\u00e9sultat.\n\n**Op\u00e9rations de liste:**\n\n- `UPDATE_APPEND` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e\n- `UPDATE_APPEND_IF_MISSING` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e si la valeur n'est pas d\u00e9j\u00e0 dans la liste.\n- `UPDATE_REMOVE` : Supprime une valeur donn\u00e9e d'une liste\n\n**Op\u00e9rations num\u00e9riques:**\n\n- `UPDATE_DEC` : Diminue une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_INC` : Incr\u00e9mente une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_MAX` : Fixe une valeur num\u00e9rique au maximum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n- `UPDATE_MIN` : Fixe une valeur num\u00e9rique au minimum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n\n**Op\u00e9rations polyvalentes:**\n\n- `UPDATE_SET` : Fixe la valeur d'un champ \u00e0 la valeur donn\u00e9e.\n- `UPDATE_DELETE` : Supprime la valeur d'un champ donn\u00e9\n\n#### `update_by_query()`\n\nCette fonction vous permet de mettre \u00e0 jour un grand nombre d'occurrences \u00e0 l'aide d'une requ\u00eate :\n\n```python\nclient.hit.update_by_query(f'howler.analytic : \"Exemple d\\'analytic\"', [(UPDATE_INC, \"howler.score\", 100)])\n```\n\nLes m\u00eames op\u00e9rations que dans `update()` peuvent \u00eatre utilis\u00e9es.\n\n### `overwrite()`\n\nCette fonction vous permet d'\u00e9craser directement un hit avec un objet hit partiel. C'est la plus facile \u00e0 utiliser, mais elle perd une partie de la validation et du traitement suppl\u00e9mentaire des fonctions de mise \u00e0 jour.\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.overwrite(hit_to_update[\"howler\"][\"id\"], {\"source.ip\" : \"127.0.0.1\", \u201cdestination.ip\u201d : \"8.8.8.8\"})\n```\n"
@@ -0,0 +1,6 @@
1
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
+ import type { FC } from 'react';
3
+ declare const BundleParentMenu: FC<{
4
+ bundle: Hit;
5
+ }>;
6
+ export default BundleParentMenu;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AccountTree } from '@mui/icons-material';
3
+ import { IconButton, Paper, Popover, Skeleton, Stack, Tooltip } from '@mui/material';
4
+ import api from '@cccsaurora/howler-ui/api';
5
+ import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
6
+ import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
7
+ import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
8
+ import { useCallback, useEffect, useState } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+ import { useNavigate } from 'react-router-dom';
11
+ const BundleParentMenu = ({ bundle }) => {
12
+ const { t } = useTranslation();
13
+ const navigate = useNavigate();
14
+ const [parentAnchor, setParentAnchor] = useState(null);
15
+ const [parentHits, setParentHits] = useState([]);
16
+ const onSelect = useCallback((bundleId) => {
17
+ navigate(`/bundles/${bundleId}?span=date.range.all&query=howler.id%3A*`);
18
+ setParentAnchor(null);
19
+ }, [navigate]);
20
+ useEffect(() => {
21
+ if (!parentAnchor) {
22
+ return;
23
+ }
24
+ api.search.hit
25
+ .post({ query: `howler.id:(${bundle.howler.bundles.join(' OR ')})` })
26
+ .then(response => setParentHits(response.items));
27
+ }, [bundle.howler.bundles, parentAnchor]);
28
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: t('hit.bundle.parents.show'), children: _jsx(IconButton, { size: "small", onClick: event => setParentAnchor(event.currentTarget), children: _jsx(AccountTree, { fontSize: "small" }) }) }), _jsx(Popover, { open: !!parentAnchor, anchorEl: parentAnchor, anchorOrigin: { vertical: 'top', horizontal: 'left' }, transformOrigin: { horizontal: 'right', vertical: 'top' }, onClose: () => setParentAnchor(null), children: _jsx(Paper, { sx: { p: 1, minWidth: '750px' }, children: _jsx(Stack, { spacing: 1, children: parentHits.length < 1
29
+ ? bundle.howler.bundles.map(id => _jsx(Skeleton, { variant: "rounded", height: "100px" }, id))
30
+ : parentHits.map(parent => (_jsx(HowlerCard, { sx: { p: 1, cursor: 'pointer' }, onClick: () => onSelect(parent.howler.id), children: _jsx(HitBanner, { hit: parent, layout: HitLayout.DENSE }) }, parent.howler.id))) }) }) })] }));
31
+ };
32
+ export default BundleParentMenu;
@@ -0,0 +1,2 @@
1
+ import type { FC, PropsWithChildren } from 'react';
2
+ export declare const BundleScroller: FC<PropsWithChildren>;
@@ -0,0 +1,6 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useScrollRestoration } from '@cccsaurora/howler-ui/components/hooks/useScrollRestoration';
3
+ export const BundleScroller = ({ children }) => {
4
+ useScrollRestoration('hitscrollbar');
5
+ return _jsx(_Fragment, { children: children });
6
+ };
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronLeft, Close, ManageSearch } from '@mui/icons-material';
3
3
  import { Box, Card, Checkbox, Collapse, Drawer, Fab, IconButton, Stack, Tooltip, Typography, useMediaQuery, useTheme } from '@mui/material';
4
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
5
+ import HitSearchProvider, { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
4
6
  import ParameterProvider, { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
5
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
6
- import RecordSearchProvider, { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
7
7
  import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
8
8
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
9
9
  import FlexPort from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexPort';
@@ -32,14 +32,14 @@ const HitBrowser = () => {
32
32
  const setQuery = useContextSelector(ParameterContext, ctx => ctx.setQuery);
33
33
  const setOffset = useContextSelector(ParameterContext, ctx => ctx.setOffset);
34
34
  const selectedViews = useContextSelector(ParameterContext, ctx => ctx.views);
35
- const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
36
- const addHitToSelection = useContextSelector(RecordContext, ctx => ctx.addRecordToSelection);
37
- const removeHitFromSelection = useContextSelector(RecordContext, ctx => ctx.removeRecordFromSelection);
38
- const clearSelectedHits = useContextSelector(RecordContext, ctx => ctx.clearSelectedRecords);
35
+ const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
36
+ const addHitToSelection = useContextSelector(HitContext, ctx => ctx.addHitToSelection);
37
+ const removeHitFromSelection = useContextSelector(HitContext, ctx => ctx.removeHitFromSelection);
38
+ const clearSelectedHits = useContextSelector(HitContext, ctx => ctx.clearSelectedHits);
39
39
  const searchPaneWidth = useMyLocalStorageItem(StorageKey.SEARCH_PANE_WIDTH, null)[0];
40
40
  const forceDrawer = useMyLocalStorageItem(StorageKey.FORCE_DRAWER, false)[0];
41
- const displayType = useContextSelector(RecordSearchContext, ctx => ctx.displayType);
42
- const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
41
+ const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
42
+ const response = useContextSelector(HitSearchContext, ctx => ctx.response);
43
43
  const location = useLocation();
44
44
  const routeParams = useParams();
45
45
  const [show, setShow] = useState(!!selected);
@@ -128,6 +128,6 @@ const HitBrowser = () => {
128
128
  }, children: _jsx(ChevronLeft, { sx: { transition: 'rotate 250ms', rotate: show ? '180deg' : '0deg' } }) }))] }));
129
129
  };
130
130
  const HitBrowserProvider = () => {
131
- return (_jsx(ParameterProvider, { children: _jsx(RecordSearchProvider, { children: _jsx(HitBrowser, {}) }) }));
131
+ return (_jsx(ParameterProvider, { children: _jsx(HitSearchProvider, { children: _jsx(HitBrowser, {}) }) }));
132
132
  };
133
133
  export default HitBrowserProvider;
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  /**
4
4
  * Props for the HitContextMenu component
5
5
  */
6
- interface RecordContextMenuProps {
6
+ interface HitContextMenuProps {
7
7
  /**
8
8
  * Function to extract the hit ID from a mouse event
9
9
  */
@@ -18,5 +18,5 @@ interface RecordContextMenuProps {
18
18
  * Provides quick access to common hit actions including assessment, voting,
19
19
  * transitions, and exclusion filters based on template fields.
20
20
  */
21
- declare const RecordContextMenu: FC<PropsWithChildren<RecordContextMenuProps>>;
22
- export default RecordContextMenu;
21
+ declare const HitContextMenu: FC<PropsWithChildren<HitContextMenuProps>>;
22
+ export default HitContextMenu;
@@ -0,0 +1,239 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { AddCircleOutline, Assignment, Edit, HowToVote, KeyboardArrowRight, OpenInNew, QueryStats, RemoveCircleOutline, SettingsSuggest, Terminal } from '@mui/icons-material';
3
+ import { Box, Divider, Fade, ListItemIcon, ListItemText, Menu, MenuItem, MenuList, Paper } from '@mui/material';
4
+ import api from '@cccsaurora/howler-ui/api';
5
+ import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
6
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
+ import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
8
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
9
+ import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
10
+ import { TOP_ROW, VOTE_OPTIONS } from '@cccsaurora/howler-ui/components/elements/hit/actions/SharedComponents';
11
+ import useHitActions from '@cccsaurora/howler-ui/components/hooks/useHitActions';
12
+ import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
13
+ import useMyActionFunctions from '@cccsaurora/howler-ui/components/routes/action/useMyActionFunctions';
14
+ import { capitalize, get, groupBy, isEmpty, toString } from 'lodash-es';
15
+ import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
16
+ import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
17
+ import { useTranslation } from 'react-i18next';
18
+ import { usePluginStore } from 'react-pluggable';
19
+ import { Link } from 'react-router-dom';
20
+ import { useContextSelector } from 'use-context-selector';
21
+ import { DEFAULT_QUERY } from '@cccsaurora/howler-ui/utils/constants';
22
+ import { sanitizeLuceneQuery } from '@cccsaurora/howler-ui/utils/stringUtils';
23
+ /**
24
+ * Order in which action types should be displayed in the context menu
25
+ */
26
+ const ORDER = ['assessment', 'vote', 'action'];
27
+ /**
28
+ * The margin at the bottom of the screen by which the context menu should be inverted.
29
+ * That is, if right clicking within this many pixels of the bottom, render the context menu to the top right
30
+ * of the pointer instead of the bottom right.
31
+ */
32
+ const CONTEXTMENU_MARGIN = 350;
33
+ /**
34
+ * Icon mapping for different action types
35
+ */
36
+ const ICON_MAP = {
37
+ assessment: _jsx(Assignment, {}),
38
+ vote: _jsx(HowToVote, {}),
39
+ action: _jsx(Edit, {})
40
+ };
41
+ /**
42
+ * Context menu component for hit operations.
43
+ * Provides quick access to common hit actions including assessment, voting,
44
+ * transitions, and exclusion filters based on template fields.
45
+ */
46
+ const HitContextMenu = ({ children, getSelectedId, Component = Box }) => {
47
+ const { t } = useTranslation();
48
+ const { dispatchApi } = useMyApi();
49
+ const { executeAction } = useMyActionFunctions();
50
+ const appUser = useAppUser();
51
+ const { config } = useContext(ApiConfigContext);
52
+ const pluginStore = usePluginStore();
53
+ const { getMatchingAnalytic, getMatchingTemplate } = useMatchers();
54
+ const query = useContextSelector(ParameterContext, ctx => ctx?.query);
55
+ const setQuery = useContextSelector(ParameterContext, ctx => ctx?.setQuery);
56
+ const [id, setId] = useState(null);
57
+ const hit = useContextSelector(HitContext, ctx => ctx.hits[id]);
58
+ const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
59
+ const [analytic, setAnalytic] = useState(null);
60
+ const [template, setTemplate] = useState(null);
61
+ const [anchorEl, setAnchorEl] = useState();
62
+ const [transformProps, setTransformProps] = useState({});
63
+ const [actions, setActions] = useState([]);
64
+ const [show, setShow] = useState({});
65
+ const hits = useMemo(() => (selectedHits.some(_hit => _hit.howler.id === hit?.howler.id) ? selectedHits : [hit]), [hit, selectedHits]);
66
+ const { availableTransitions, canVote, canAssess, assess, vote } = useHitActions(hits);
67
+ /**
68
+ * Checks if the current user has permission to run actions.
69
+ * Users must have one of the automation or actionrunner roles, or be an admin.
70
+ */
71
+ const canRunActions = useCallback(() => {
72
+ const roles = ['admin', 'automation_advanced', 'automation_basic', 'actionrunner_advanced', 'actionrunner_basic'];
73
+ return roles.some((role) => appUser.user?.roles?.includes(role));
74
+ }, [appUser.user?.roles]);
75
+ /**
76
+ * Handles right-click context menu events.
77
+ * Opens the context menu at the click location and loads available actions.
78
+ */
79
+ const onContextMenu = useCallback(async (event) => {
80
+ if (anchorEl) {
81
+ event.preventDefault();
82
+ setAnchorEl(null);
83
+ return;
84
+ }
85
+ event.preventDefault();
86
+ const _id = getSelectedId(event);
87
+ setId(_id);
88
+ if (window.innerHeight - event.clientY < 300) {
89
+ setTransformProps({
90
+ position: 'fixed',
91
+ bottom: `${window.innerHeight - event.clientY}px !important`,
92
+ top: 'unset !important',
93
+ left: `${event.clientX}px !important`
94
+ });
95
+ }
96
+ else {
97
+ setTransformProps({
98
+ position: 'fixed',
99
+ top: `${event.clientY}px !important`,
100
+ left: `${event.clientX}px !important`
101
+ });
102
+ }
103
+ setAnchorEl(event.target);
104
+ // TODO: Bumping the number of rows is a temporary fix - we'll need to improve this.
105
+ const _actions = (await dispatchApi(api.search.action.post({ query: 'action_id:*', rows: 100, sort: 'name asc' }), {
106
+ throwError: false
107
+ }))?.items;
108
+ if (_actions) {
109
+ setActions(_actions);
110
+ }
111
+ }, [anchorEl, dispatchApi, getSelectedId]);
112
+ const rowStatus = useMemo(() => ({
113
+ assessment: canAssess,
114
+ vote: canVote
115
+ }), [canAssess, canVote]);
116
+ const pluginActions = howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.actions`, hits));
117
+ /**
118
+ * Generates grouped action entries for the context menu.
119
+ * Combines transitions, plugin actions, votes, and assessments based on permissions.
120
+ */
121
+ const entries = useMemo(() => {
122
+ let _actions = [...availableTransitions, ...pluginActions];
123
+ if (canVote) {
124
+ _actions = [
125
+ ..._actions,
126
+ ...VOTE_OPTIONS.map(option => ({ ...option, actionFunction: () => vote(option.name.toLowerCase()) }))
127
+ ];
128
+ }
129
+ if (config.lookups?.['howler.assessment'] && canAssess) {
130
+ _actions = [
131
+ ..._actions,
132
+ ...config.lookups['howler.assessment']
133
+ .filter(_assessment => analytic?.triage_settings?.valid_assessments
134
+ ? analytic.triage_settings?.valid_assessments.includes(_assessment)
135
+ : true)
136
+ .sort((a, b) => +TOP_ROW.includes(b) - +TOP_ROW.includes(a))
137
+ .map(assessment => ({
138
+ type: 'assessment',
139
+ name: assessment,
140
+ actionFunction: async () => {
141
+ await assess(assessment, analytic?.triage_settings?.skip_rationale);
142
+ }
143
+ }))
144
+ ];
145
+ }
146
+ return Object.entries(groupBy(_actions, 'type')).sort(([a], [b]) => ORDER.indexOf(a) - ORDER.indexOf(b));
147
+ }, [analytic, assess, availableTransitions, canAssess, canVote, config.lookups, vote, pluginActions]);
148
+ /**
149
+ * Calculates appropriate styles for submenu positioning.
150
+ * Adjusts position based on available screen space to prevent overflow.
151
+ */
152
+ const calculateSubMenuStyles = useCallback((parent) => {
153
+ const baseStyles = { position: 'absolute', maxHeight: '300px', overflow: 'auto' };
154
+ const defaultStyles = { ...baseStyles, top: 0, left: '100%' };
155
+ if (!parent) {
156
+ return defaultStyles;
157
+ }
158
+ const parentBounds = parent.getBoundingClientRect();
159
+ if (window.innerHeight - parentBounds.y < CONTEXTMENU_MARGIN) {
160
+ return { ...baseStyles, bottom: 0, left: '100%' };
161
+ }
162
+ return defaultStyles;
163
+ }, []);
164
+ // Load analytic and template data when a hit is selected
165
+ useEffect(() => {
166
+ if (!hit?.howler.analytic) {
167
+ return;
168
+ }
169
+ getMatchingAnalytic(hit).then(setAnalytic);
170
+ getMatchingTemplate(hit).then(setTemplate);
171
+ // eslint-disable-next-line react-hooks/exhaustive-deps
172
+ }, [hit]);
173
+ // Reset menu state when context menu is closed
174
+ useEffect(() => {
175
+ if (!anchorEl) {
176
+ setShow({});
177
+ setAnalytic(null);
178
+ }
179
+ }, [anchorEl]);
180
+ return (_jsxs(Component, { id: "contextMenu", onContextMenu: onContextMenu, children: [children, _jsxs(Menu, { id: "hit-menu", open: !!anchorEl, anchorEl: anchorEl, onClose: () => setAnchorEl(null), slotProps: {
181
+ paper: {
182
+ sx: {
183
+ ...transformProps,
184
+ overflow: 'visible !important'
185
+ }
186
+ }
187
+ }, MenuListProps: { dense: true, sx: { minWidth: '250px' } }, anchorOrigin: { vertical: 'top', horizontal: 'left' }, onClick: () => setAnchorEl(null), children: [_jsxs(MenuItem, { component: Link, to: `/hits/${hit?.howler.id}`, disabled: !hit, children: [_jsx(ListItemIcon, { children: _jsx(OpenInNew, {}) }), _jsx(ListItemText, { children: t('hit.panel.open') })] }), _jsxs(MenuItem, { component: Link, to: `/analytics/${analytic?.analytic_id}`, disabled: !analytic, children: [_jsx(ListItemIcon, { children: _jsx(QueryStats, {}) }), _jsx(ListItemText, { children: t('hit.panel.analytic.open') })] }), _jsx(Divider, {}), entries.map(([type, items]) => (_jsxs(MenuItem, { id: `${type}-menu-item`, sx: { position: 'relative' }, onMouseEnter: ev => setShow(_show => ({ ..._show, [type]: ev.target })), onMouseLeave: () => setShow(_show => ({ ..._show, [type]: null })), disabled: rowStatus[type] === false, children: [_jsx(ListItemIcon, { children: ICON_MAP[type] ?? _jsx(Terminal, {}) }), _jsx(ListItemText, { sx: { flex: 1 }, children: t(`hit.details.actions.${type}`) }), rowStatus[type] !== false && (_jsx(KeyboardArrowRight, { fontSize: "small", sx: { color: 'text.secondary', mr: -1 } })), _jsx(Fade, { in: !!show[type], unmountOnExit: true, children: _jsx(Paper, { id: `${type}-submenu`, sx: calculateSubMenuStyles(show[type]), elevation: 8, children: _jsx(MenuList, { sx: { p: 0, borderTopLeftRadius: 0 }, dense: true, role: "group", children: items.map(a => (_jsx(MenuItem, { value: a.name, onClick: a.actionFunction, children: a.i18nKey ? t(a.i18nKey) : capitalize(a.name) }, a.name))) }) }) })] }, type))), _jsxs(MenuItem, { id: "actions-menu-item", sx: { position: 'relative' }, onMouseEnter: ev => setShow(_show => ({ ..._show, actions: ev.target })), onMouseLeave: () => setShow(_show => ({ ..._show, actions: null })), disabled: actions.length < 1 || !canRunActions(), children: [_jsx(ListItemIcon, { children: _jsx(SettingsSuggest, {}) }), _jsx(ListItemText, { sx: { flex: 1 }, children: t('route.actions.change') }), actions.length > 0 && _jsx(KeyboardArrowRight, { fontSize: "small", sx: { color: 'text.secondary', mr: -1 } }), _jsx(Fade, { in: !!show.actions, unmountOnExit: true, children: _jsx(Paper, { id: "actions-submenu", sx: calculateSubMenuStyles(show.actions), elevation: 8, children: _jsx(MenuList, { sx: { p: 0 }, dense: true, role: "group", children: actions.map(action => (_jsx(MenuItem, { onClick: () => executeAction(action.action_id, `howler.id:${hit?.howler.id}`), children: _jsx(ListItemText, { children: action.name }) }, action.action_id))) }) }) })] }), !isEmpty(template?.keys ?? []) && setQuery && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsxs(MenuItem, { id: "excludes-menu-item", sx: { position: 'relative' }, onMouseEnter: ev => setShow(_show => ({ ..._show, excludes: ev.target })), onMouseLeave: () => setShow(_show => ({ ..._show, excludes: null })), children: [_jsx(ListItemIcon, { children: _jsx(RemoveCircleOutline, {}) }), _jsx(ListItemText, { sx: { flex: 1 }, children: t('hit.panel.exclude') }), _jsx(KeyboardArrowRight, { fontSize: "small", sx: { color: 'text.secondary', mr: -1 } }), _jsx(Fade, { in: !!show.excludes, unmountOnExit: true, children: _jsx(Paper, { id: "excludes-submenu", sx: calculateSubMenuStyles(show.excludes), elevation: 8, children: _jsx(MenuList, { sx: { p: 0 }, dense: true, role: "group", children: template?.keys.map(key => {
188
+ // Build exclusion query based on current query and field value
189
+ let newQuery = '';
190
+ if (query !== DEFAULT_QUERY) {
191
+ newQuery = `(${query}) AND `;
192
+ }
193
+ const value = get(hit, key);
194
+ if (!value) {
195
+ return null;
196
+ }
197
+ else if (Array.isArray(value)) {
198
+ // Handle array values by excluding all items
199
+ const sanitizedValues = value
200
+ .map(toString)
201
+ .filter(val => !!val)
202
+ .map(val => `"${sanitizeLuceneQuery(val)}"`);
203
+ if (sanitizedValues.length < 1) {
204
+ return null;
205
+ }
206
+ newQuery += `-${key}:(${sanitizedValues.join(' OR ')})`;
207
+ }
208
+ else {
209
+ // Handle single value
210
+ newQuery += `-${key}:"${sanitizeLuceneQuery(value.toString())}"`;
211
+ }
212
+ return (_jsx(MenuItem, { onClick: () => setQuery(newQuery), children: _jsx(ListItemText, { children: key }) }, key));
213
+ }) }) }) })] }), _jsxs(MenuItem, { id: "includes-menu-item", sx: { position: 'relative' }, onMouseEnter: ev => setShow(_show => ({ ..._show, includes: ev.target })), onMouseLeave: () => setShow(_show => ({ ..._show, includes: null })), children: [_jsx(ListItemIcon, { children: _jsx(AddCircleOutline, {}) }), _jsx(ListItemText, { sx: { flex: 1 }, children: t('hit.panel.include') }), _jsx(KeyboardArrowRight, { fontSize: "small", sx: { color: 'text.secondary', mr: -1 } }), _jsx(Fade, { in: !!show.includes, unmountOnExit: true, children: _jsx(Paper, { id: "includes-submenu", sx: calculateSubMenuStyles(show.includes), elevation: 8, children: _jsx(MenuList, { sx: { p: 0 }, dense: true, role: "group", children: template?.keys.map(key => {
214
+ // Build inclusion query based on current query and field
215
+ // If default, we include default query
216
+ let newQuery = `(${query}) AND `;
217
+ const value = get(hit, key);
218
+ if (!value) {
219
+ return null;
220
+ }
221
+ else if (Array.isArray(value)) {
222
+ // Handle array values by including all items
223
+ const sanitizedValues = value
224
+ .map(toString)
225
+ .filter(val => !!val)
226
+ .map(val => `"${sanitizeLuceneQuery(val)}"`);
227
+ if (sanitizedValues.length < 1) {
228
+ return null;
229
+ }
230
+ newQuery += `${key}:(${sanitizedValues.join(' OR ')})`;
231
+ }
232
+ else {
233
+ // Handle single value
234
+ newQuery += `${key}:"${sanitizeLuceneQuery(value.toString())}"`;
235
+ }
236
+ return (_jsx(MenuItem, { onClick: () => setQuery(newQuery), children: _jsx(ListItemText, { children: key }) }, key));
237
+ }) }) }) })] })] }))] })] }));
238
+ };
239
+ export default HitContextMenu;