@cccsaurora/howler-ui 2.18.0-dev.674 → 2.18.0-dev.676

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 (230) hide show
  1. package/api/index.d.ts +0 -2
  2. package/api/index.js +2 -4
  3. package/api/search/index.d.ts +1 -2
  4. package/api/search/index.js +1 -2
  5. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  6. package/components/app/App.js +7 -34
  7. package/components/app/hooks/useMatchers.js +2 -2
  8. package/components/app/hooks/useMatchers.test.js +22 -22
  9. package/components/app/hooks/useTitle.js +3 -3
  10. package/components/app/providers/FavouritesProvider.js +2 -2
  11. package/components/app/providers/HitProvider.d.ts +22 -0
  12. package/components/app/providers/{RecordProvider.js → HitProvider.js} +41 -41
  13. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  14. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  15. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +70 -51
  16. package/components/app/providers/ParameterProvider.d.ts +2 -9
  17. package/components/app/providers/ParameterProvider.js +240 -165
  18. package/components/app/providers/ParameterProvider.test.js +14 -307
  19. package/components/elements/PluginTypography.d.ts +1 -2
  20. package/components/elements/PluginTypography.js +2 -3
  21. package/components/elements/UserList.d.ts +2 -5
  22. package/components/elements/UserList.js +5 -14
  23. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  24. package/components/elements/display/ChipPopper.d.ts +1 -1
  25. package/components/elements/display/HowlerCard.js +1 -1
  26. package/components/elements/display/Modal.js +0 -1
  27. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  28. package/components/elements/display/icons/BundleButton.js +32 -0
  29. package/components/elements/hit/HitActions.js +4 -4
  30. package/components/elements/hit/HitBanner.js +48 -28
  31. package/components/elements/hit/HitCard.js +5 -5
  32. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  33. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  34. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  35. package/components/elements/hit/HitLabels.js +2 -2
  36. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  37. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  38. package/components/elements/hit/HitRelated.d.ts +6 -0
  39. package/components/elements/hit/HitRelated.js +7 -0
  40. package/components/elements/hit/HitSummary.d.ts +1 -2
  41. package/components/elements/hit/HitSummary.js +5 -6
  42. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  43. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  44. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  45. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  46. package/components/elements/view/ViewTitle.js +1 -1
  47. package/components/hooks/useHitActions.d.ts +1 -1
  48. package/components/hooks/useHitActions.js +4 -4
  49. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  50. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  51. package/components/hooks/useMyPreferences.js +1 -10
  52. package/components/hooks/useMySearch.js +2 -2
  53. package/components/hooks/useMySitemap.js +1 -4
  54. package/components/hooks/useMyTheme.js +2 -9
  55. package/components/hooks/useParamState.test.js +4 -3
  56. package/components/routes/action/edit/ActionEditor.js +2 -2
  57. package/components/routes/action/view/ActionSearch.js +1 -1
  58. package/components/routes/advanced/QueryBuilder.js +1 -1
  59. package/components/routes/advanced/QueryEditor.js +3 -3
  60. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  61. package/components/routes/analytics/AnalyticDetails.js +2 -2
  62. package/components/routes/analytics/AnalyticSearch.js +1 -1
  63. package/components/routes/dossiers/DossierEditor.js +2 -2
  64. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  65. package/components/routes/help/ApiDocumentation.js +1 -1
  66. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  67. package/components/routes/help/BundleDocumentation.js +12 -0
  68. package/components/routes/help/HitBannerDocumentation.js +0 -1
  69. package/components/routes/help/HitDocumentation.js +3 -1
  70. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  71. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  72. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  73. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  74. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  75. package/components/routes/hits/search/BundleScroller.js +6 -0
  76. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  77. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  78. package/components/routes/hits/search/HitContextMenu.js +227 -0
  79. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +39 -39
  80. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  81. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  82. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  83. package/components/routes/hits/search/InformationPane.js +60 -47
  84. package/components/routes/hits/search/LayoutSettings.js +3 -3
  85. package/components/routes/hits/search/QuerySettings.js +1 -2
  86. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  87. package/components/routes/hits/search/SearchPane.js +49 -26
  88. package/components/routes/hits/search/ViewLink.js +3 -3
  89. package/components/routes/hits/search/ViewLink.test.js +8 -8
  90. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  91. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  92. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  93. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  94. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  95. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +8 -10
  96. package/components/routes/hits/view/HitViewer.js +13 -12
  97. package/components/routes/home/ViewCard.js +4 -4
  98. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  99. package/components/routes/overviews/OverviewViewer.js +2 -2
  100. package/components/routes/views/ViewComposer.js +4 -4
  101. package/locales/en/translation.json +3 -65
  102. package/locales/fr/translation.json +3 -63
  103. package/models/WithMetadata.d.ts +1 -2
  104. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  105. package/models/entities/generated/Hit.d.ts +0 -1
  106. package/models/entities/generated/Howler.d.ts +4 -0
  107. package/models/entities/generated/Rule.d.ts +10 -2
  108. package/models/entities/generated/Threat.d.ts +2 -2
  109. package/package.json +106 -123
  110. package/plugins/clue/components/ClueTypography.js +2 -2
  111. package/plugins/clue/utils.d.ts +1 -2
  112. package/tests/utils.d.ts +0 -2
  113. package/tests/utils.js +0 -8
  114. package/utils/constants.d.ts +3 -3
  115. package/utils/hitFunctions.d.ts +1 -2
  116. package/utils/hitFunctions.js +4 -4
  117. package/api/search/case.d.ts +0 -4
  118. package/api/search/case.js +0 -8
  119. package/api/v2/case/index.d.ts +0 -6
  120. package/api/v2/case/index.js +0 -18
  121. package/api/v2/index.d.ts +0 -4
  122. package/api/v2/index.js +0 -6
  123. package/api/v2/search/facet.d.ts +0 -3
  124. package/api/v2/search/facet.js +0 -12
  125. package/api/v2/search/index.d.ts +0 -5
  126. package/api/v2/search/index.js +0 -24
  127. package/components/app/providers/RecordProvider.d.ts +0 -23
  128. package/components/elements/ContextMenu.d.ts +0 -56
  129. package/components/elements/ContextMenu.js +0 -109
  130. package/components/elements/ContextMenu.test.js +0 -215
  131. package/components/elements/ObjectDetails.d.ts +0 -6
  132. package/components/elements/case/CaseCard.d.ts +0 -8
  133. package/components/elements/case/CaseCard.js +0 -39
  134. package/components/elements/case/CasePreview.d.ts +0 -6
  135. package/components/elements/case/CasePreview.js +0 -17
  136. package/components/elements/case/StatusIcon.d.ts +0 -5
  137. package/components/elements/case/StatusIcon.js +0 -13
  138. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
  139. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  140. package/components/elements/hit/related/RelatedRecords.js +0 -63
  141. package/components/elements/observable/ObservableCard.d.ts +0 -6
  142. package/components/elements/observable/ObservableCard.js +0 -23
  143. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  144. package/components/elements/observable/ObservablePreview.js +0 -12
  145. package/components/elements/record/RecordContextMenu.js +0 -235
  146. package/components/elements/record/RecordContextMenu.test.d.ts +0 -1
  147. package/components/elements/record/RecordRelated.d.ts +0 -7
  148. package/components/elements/record/RecordRelated.js +0 -34
  149. package/components/hooks/useRelatedRecords.d.ts +0 -13
  150. package/components/hooks/useRelatedRecords.js +0 -32
  151. package/components/routes/cases/CaseViewer.d.ts +0 -2
  152. package/components/routes/cases/CaseViewer.js +0 -22
  153. package/components/routes/cases/Cases.d.ts +0 -2
  154. package/components/routes/cases/Cases.js +0 -101
  155. package/components/routes/cases/constants.d.ts +0 -5
  156. package/components/routes/cases/constants.js +0 -5
  157. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  158. package/components/routes/cases/detail/AlertPanel.js +0 -33
  159. package/components/routes/cases/detail/CaseAssets.d.ts +0 -12
  160. package/components/routes/cases/detail/CaseAssets.js +0 -101
  161. package/components/routes/cases/detail/CaseAssets.test.d.ts +0 -1
  162. package/components/routes/cases/detail/CaseAssets.test.js +0 -163
  163. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  164. package/components/routes/cases/detail/CaseDashboard.js +0 -51
  165. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  166. package/components/routes/cases/detail/CaseDetails.js +0 -61
  167. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  168. package/components/routes/cases/detail/CaseOverview.js +0 -43
  169. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -6
  170. package/components/routes/cases/detail/CaseSidebar.js +0 -61
  171. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  172. package/components/routes/cases/detail/CaseTask.js +0 -57
  173. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  174. package/components/routes/cases/detail/ItemPage.js +0 -99
  175. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  176. package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
  177. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  178. package/components/routes/cases/detail/TaskPanel.js +0 -52
  179. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
  180. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
  181. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  182. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -27
  183. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  184. package/components/routes/cases/detail/assets/Asset.js +0 -12
  185. package/components/routes/cases/detail/assets/Asset.test.d.ts +0 -1
  186. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  187. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -13
  188. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -131
  189. package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
  190. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  191. package/components/routes/cases/detail/sidebar/utils.js +0 -25
  192. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  193. package/components/routes/cases/hooks/useCase.js +0 -38
  194. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  195. package/components/routes/cases/modals/ResolveModal.js +0 -59
  196. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  197. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  198. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  199. package/components/routes/observables/ObservableViewer.js +0 -27
  200. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  201. package/models/entities/generated/Case.d.ts +0 -28
  202. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  203. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  204. package/models/entities/generated/EmailParent.d.ts +0 -19
  205. package/models/entities/generated/Enrichments.d.ts +0 -7
  206. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  207. package/models/entities/generated/HttpResponse.d.ts +0 -11
  208. package/models/entities/generated/Item.d.ts +0 -9
  209. package/models/entities/generated/Observable.d.ts +0 -85
  210. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  211. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  212. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  213. package/models/entities/generated/ObservableFile.d.ts +0 -36
  214. package/models/entities/generated/ObservableHowler.d.ts +0 -43
  215. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  216. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  217. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  218. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  219. package/models/entities/generated/ObservableSource.d.ts +0 -23
  220. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  221. package/models/entities/generated/ObservableTls.d.ts +0 -12
  222. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  223. package/models/entities/generated/Task.d.ts +0 -10
  224. package/utils/typeUtils.d.ts +0 -7
  225. package/utils/typeUtils.js +0 -27
  226. /package/components/app/providers/{RecordSearchProvider.test.d.ts → HitSearchProvider.test.d.ts} +0 -0
  227. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  228. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  229. /package/components/{elements/ContextMenu.test.d.ts → routes/hits/search/HitContextMenu.test.d.ts} +0 -0
  230. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -1,6 +0,0 @@
1
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
2
- type PreviewProps = {
3
- observable: Observable;
4
- };
5
- declare const _default: import("react").NamedExoticComponent<PreviewProps>;
6
- export default _default;
@@ -1,12 +0,0 @@
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 ObservablePreview = ({ observable }) => {
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: observable.event.provider }), _jsx("div", { style: { flex: 1 } }), _jsx(Chip, { label: observable.event.kind }), observable.event.reference && (_jsx(IconButton, { size: "small", component: Link, to: observable.event.reference, sx: { opacity: 1 }, target: "_blank", rel: "noopener noreferrer", children: _jsx(OpenInNew, { fontSize: "small" }) }))] }), observable.event.type && (_jsxs(Typography, { variant: "caption", children: [t('event.type'), " - ", observable.event.type.join(', ')] })), observable.event.module && (_jsxs(Typography, { variant: "caption", children: [t('event.module'), " - ", observable.event.module] }))] }) }));
11
- };
12
- export default memo(ObservablePreview);
@@ -1,235 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { AddCircleOutline, Assignment, Edit, HowToVote, OpenInNew, QueryStats, RemoveCircleOutline, SettingsSuggest, Terminal } from '@mui/icons-material';
3
- import api from '@cccsaurora/howler-ui/api';
4
- import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
5
- import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
- import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
7
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
8
- import ContextMenu, {} from '@cccsaurora/howler-ui/components/elements/ContextMenu';
9
- import { TOP_ROW, VOTE_OPTIONS } from '@cccsaurora/howler-ui/components/elements/hit/actions/SharedComponents';
10
- import useHitActions from '@cccsaurora/howler-ui/components/hooks/useHitActions';
11
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
12
- import useMyActionFunctions from '@cccsaurora/howler-ui/components/routes/action/useMyActionFunctions';
13
- import { capitalize, get, groupBy, isEmpty, toString } from 'lodash-es';
14
- import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
15
- import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
16
- import { useTranslation } from 'react-i18next';
17
- import { usePluginStore } from 'react-pluggable';
18
- import { useContextSelector } from 'use-context-selector';
19
- import { DEFAULT_QUERY } from '@cccsaurora/howler-ui/utils/constants';
20
- import { sanitizeLuceneQuery } from '@cccsaurora/howler-ui/utils/stringUtils';
21
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
22
- /**
23
- * Order in which action types should be displayed in the context menu
24
- */
25
- const ORDER = ['assessment', 'vote', 'action'];
26
- /**
27
- * Icon mapping for different action types
28
- */
29
- const ICON_MAP = {
30
- assessment: _jsx(Assignment, {}),
31
- vote: _jsx(HowToVote, {}),
32
- action: _jsx(Edit, {})
33
- };
34
- /**
35
- * Context menu component for hit operations.
36
- * Provides quick access to common hit actions including assessment, voting,
37
- * transitions, and exclusion filters based on template fields.
38
- */
39
- const RecordContextMenu = ({ children, getSelectedId, Component }) => {
40
- const { t } = useTranslation();
41
- const { dispatchApi } = useMyApi();
42
- const { executeAction } = useMyActionFunctions();
43
- const { config } = useContext(ApiConfigContext);
44
- const pluginStore = usePluginStore();
45
- const { getMatchingAnalytic, getMatchingTemplate } = useMatchers();
46
- const query = useContextSelector(ParameterContext, ctx => ctx?.query);
47
- const setQuery = useContextSelector(ParameterContext, ctx => ctx?.setQuery);
48
- const [id, setId] = useState(null);
49
- const record = useContextSelector(RecordContext, ctx => ctx.records[id]);
50
- const selectedRecords = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
51
- const [analytic, setAnalytic] = useState(null);
52
- const [template, setTemplate] = useState(null);
53
- const [actions, setActions] = useState([]);
54
- const records = useMemo(() => selectedRecords.some(_record => _record.howler.id === record?.howler.id)
55
- ? selectedRecords
56
- : record
57
- ? [record]
58
- : [], [record, selectedRecords]);
59
- const hits = useMemo(() => records.filter(isHit), [records]);
60
- const { availableTransitions, canVote, canAssess, assess, vote } = useHitActions(hits);
61
- /**
62
- * Called by ContextMenu after the menu is positioned and opened.
63
- * Identifies the clicked record and fetches available actions.
64
- */
65
- const onOpen = useCallback(async (event) => {
66
- const _id = getSelectedId(event);
67
- setId(_id);
68
- const _actions = (await dispatchApi(api.search.action.post({ query: 'action_id:*' }), { throwError: false }))
69
- ?.items;
70
- if (_actions) {
71
- setActions(_actions);
72
- }
73
- }, [dispatchApi, getSelectedId]);
74
- const rowStatus = useMemo(() => ({
75
- assessment: canAssess,
76
- vote: canVote
77
- }), [canAssess, canVote]);
78
- const pluginActions = howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.actions`, records));
79
- /**
80
- * Generates grouped action entries for the context menu.
81
- * Combines transitions, plugin actions, votes, and assessments based on permissions.
82
- */
83
- const entries = useMemo(() => {
84
- let _actions = [...availableTransitions, ...pluginActions];
85
- if (canVote) {
86
- _actions = [
87
- ..._actions,
88
- ...VOTE_OPTIONS.map(option => ({ ...option, actionFunction: () => vote(option.name.toLowerCase()) }))
89
- ];
90
- }
91
- if (config.lookups?.['howler.assessment'] && canAssess) {
92
- _actions = [
93
- ..._actions,
94
- ...config.lookups['howler.assessment']
95
- .filter(_assessment => analytic?.triage_settings?.valid_assessments
96
- ? analytic.triage_settings?.valid_assessments.includes(_assessment)
97
- : true)
98
- .sort((a, b) => +TOP_ROW.includes(b) - +TOP_ROW.includes(a))
99
- .map(assessment => ({
100
- type: 'assessment',
101
- name: assessment,
102
- actionFunction: async () => {
103
- await assess(assessment, analytic?.triage_settings?.skip_rationale);
104
- }
105
- }))
106
- ];
107
- }
108
- return Object.entries(groupBy(_actions, 'type')).sort(([a], [b]) => ORDER.indexOf(a) - ORDER.indexOf(b));
109
- }, [analytic, assess, availableTransitions, canAssess, canVote, config.lookups, vote, pluginActions]);
110
- // Load analytic and template data when a hit is selected
111
- useEffect(() => {
112
- if (!record?.howler.analytic) {
113
- return;
114
- }
115
- getMatchingAnalytic(record).then(setAnalytic);
116
- getMatchingTemplate(record).then(setTemplate);
117
- // eslint-disable-next-line react-hooks/exhaustive-deps
118
- }, [record]);
119
- /**
120
- * Builds the declarative items structure for the ContextMenu component.
121
- */
122
- const items = useMemo(() => {
123
- const result = [
124
- {
125
- kind: 'item',
126
- id: 'open-record',
127
- icon: _jsx(OpenInNew, {}),
128
- label: t(`${record?.__index ?? 'hit'}.open`),
129
- disabled: !record,
130
- to: `/${record?.__index}s/${record?.howler.id}`
131
- }
132
- ];
133
- if (isHit(record)) {
134
- result.push({
135
- kind: 'item',
136
- id: 'open-analytic',
137
- icon: _jsx(QueryStats, {}),
138
- label: t('analytic.open'),
139
- disabled: !analytic,
140
- to: `/analytics/${analytic?.analytic_id}`
141
- });
142
- result.push({ kind: 'divider', id: 'actions-divider' });
143
- for (const [type, typeItems] of entries) {
144
- result.push({
145
- kind: 'submenu',
146
- id: type,
147
- icon: ICON_MAP[type] ?? _jsx(Terminal, {}),
148
- label: t(`hit.details.actions.${type}`),
149
- disabled: rowStatus[type] === false,
150
- items: typeItems.map(a => ({
151
- key: a.name,
152
- label: a.i18nKey ? t(a.i18nKey) : capitalize(a.name),
153
- onClick: a.actionFunction
154
- }))
155
- });
156
- }
157
- result.push({
158
- kind: 'submenu',
159
- id: 'actions',
160
- icon: _jsx(SettingsSuggest, {}),
161
- label: t('route.actions.change'),
162
- disabled: actions.length < 1,
163
- items: actions.map(action => ({
164
- key: action.action_id,
165
- label: action.name,
166
- onClick: () => executeAction(action.action_id, `howler.id:${record?.howler.id}`)
167
- }))
168
- });
169
- if (!isEmpty(template?.keys ?? []) && setQuery) {
170
- result.push({ kind: 'divider', id: 'filter-divider' });
171
- result.push({
172
- kind: 'submenu',
173
- id: 'excludes',
174
- icon: _jsx(RemoveCircleOutline, {}),
175
- label: t('hit.panel.exclude'),
176
- items: (template?.keys ?? []).flatMap(key => {
177
- let newQuery = '';
178
- if (query !== DEFAULT_QUERY) {
179
- newQuery = `(${query}) AND `;
180
- }
181
- const value = get(record, key);
182
- if (!value) {
183
- return [];
184
- }
185
- else if (Array.isArray(value)) {
186
- const sanitizedValues = value
187
- .map(toString)
188
- .filter(val => !!val)
189
- .map(val => `"${sanitizeLuceneQuery(val)}"`);
190
- if (sanitizedValues.length < 1) {
191
- return [];
192
- }
193
- newQuery += `-${key}:(${sanitizedValues.join(' OR ')})`;
194
- }
195
- else {
196
- newQuery += `-${key}:"${sanitizeLuceneQuery(value.toString())}"`;
197
- }
198
- return [{ key, label: key, onClick: () => setQuery(newQuery) }];
199
- })
200
- });
201
- result.push({
202
- kind: 'submenu',
203
- id: 'includes',
204
- icon: _jsx(AddCircleOutline, {}),
205
- label: t('hit.panel.include'),
206
- items: (template?.keys ?? []).flatMap(key => {
207
- let newQuery = `(${query}) AND `;
208
- const value = get(record, key);
209
- if (!value) {
210
- return [];
211
- }
212
- else if (Array.isArray(value)) {
213
- const sanitizedValues = value
214
- .map(toString)
215
- .filter(val => !!val)
216
- .map(val => `"${sanitizeLuceneQuery(val)}"`);
217
- if (sanitizedValues.length < 1) {
218
- return [];
219
- }
220
- newQuery += `${key}:(${sanitizedValues.join(' OR ')})`;
221
- }
222
- else {
223
- newQuery += `${key}:"${sanitizeLuceneQuery(value.toString())}"`;
224
- }
225
- return [{ key, label: key, onClick: () => setQuery(newQuery) }];
226
- })
227
- });
228
- }
229
- }
230
- return result;
231
- // eslint-disable-next-line react-hooks/exhaustive-deps
232
- }, [record, analytic, template, entries, rowStatus, actions, query, t, setQuery, executeAction]);
233
- return (_jsx(ContextMenu, { id: "contextMenu", Component: Component, onOpen: onOpen, onClose: () => setAnalytic(null), items: items, children: children }));
234
- };
235
- export default RecordContextMenu;
@@ -1,7 +0,0 @@
1
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
3
- import { type FC } from 'react';
4
- declare const RecordRelated: FC<{
5
- record: Hit | Observable;
6
- }>;
7
- export default RecordRelated;
@@ -1,34 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Stack, Tab, Tabs, useTheme } from '@mui/material';
3
- import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
4
- import useRelatedRecords from '@cccsaurora/howler-ui/components/hooks/useRelatedRecords';
5
- import { groupBy } from 'lodash-es';
6
- import { useMemo, useState } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Link } from 'react-router-dom';
9
- import { isCase, isHit, isObservable } from '@cccsaurora/howler-ui/utils/typeUtils';
10
- import CaseCard from '../case/CaseCard';
11
- import HitCard from '../hit/HitCard';
12
- import { HitLayout } from '../hit/HitLayout';
13
- import RelatedLink from '../hit/related/RelatedLink';
14
- const RecordRelated = ({ record }) => {
15
- const theme = useTheme();
16
- const { t } = useTranslation();
17
- const related = useMemo(() => record?.howler.related ?? [], [record?.howler.related]);
18
- const records = useRelatedRecords(related, related.length > 0);
19
- const groups = groupBy(records, '__index');
20
- const hasLinks = (record?.howler.links?.length ?? 0) > 0;
21
- const tabs = [
22
- hasLinks && 'links',
23
- groups.hit?.length > 0 && 'hit',
24
- groups.case?.length > 0 && 'case',
25
- groups.observable?.length > 0 && 'observable'
26
- ].filter(Boolean);
27
- const [activeTab, setActiveTab] = useState(false);
28
- const currentTab = activeTab !== false && tabs.includes(activeTab) ? activeTab : (tabs[0] ?? false);
29
- if (!record) {
30
- return null;
31
- }
32
- return (_jsxs(Box, { sx: { borderTop: `thin solid ${theme.palette.divider}`, height: '100%', flex: 1, mr: 2, pb: 2 }, children: [_jsxs(Tabs, { value: currentTab, onChange: (_, v) => setActiveTab(v), variant: "scrollable", scrollButtons: "auto", children: [hasLinks && _jsx(Tab, { value: "links", label: t('hit.related.tab.links') }), groups.hit?.length > 0 && _jsx(Tab, { value: "hit", label: t('hit.related.tab.hit') }), groups.case?.length > 0 && _jsx(Tab, { value: "case", label: t('hit.related.tab.case') }), groups.observable?.length > 0 && _jsx(Tab, { value: "observable", label: t('hit.related.tab.observable') })] }), currentTab === 'links' && (_jsx(Box, { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 1, pt: 1, children: record.howler.links.map(l => (_jsx(RelatedLink, { ...l }, l.title + l.href))) })), currentTab === 'hit' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isHit).map(h => (_jsx(Link, { to: `/hits/${h.howler.id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(HitCard, { id: h.howler.id, layout: HitLayout.NORMAL }) }, h.howler.id))) })), currentTab === 'case' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isCase).map(c => (_jsx(Link, { to: `/cases/${c.case_id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(CaseCard, { case: c }) }, c.case_id))) })), currentTab === 'observable' && (_jsx(Stack, { spacing: 1, pt: 1, children: records.filter(isObservable).map(o => (_jsx(Link, { to: `/observables/${o.howler.id}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsx(ObservableCard, { observable: o }) }, o.howler.id))) }))] }));
33
- };
34
- export default RecordRelated;
@@ -1,13 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
4
- import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
5
- type MixedRecords = Hit | Observable | Case;
6
- /**
7
- * Fetches records matching the provided IDs from the hit, observable, and case indexes.
8
- *
9
- * @param ids - List of howler.id / case_id values to look up.
10
- * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
11
- */
12
- declare const useRelatedRecords: <T = MixedRecords>(ids: string[], enabled?: boolean) => WithMetadata<T>[];
13
- export default useRelatedRecords;
@@ -1,32 +0,0 @@
1
- import api from '@cccsaurora/howler-ui/api';
2
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
- import { useEffect, useState } from 'react';
4
- /**
5
- * Fetches records matching the provided IDs from the hit, observable, and case indexes.
6
- *
7
- * @param ids - List of howler.id / case_id values to look up.
8
- * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
9
- */
10
- const useRelatedRecords = (ids, enabled = true) => {
11
- const { dispatchApi } = useMyApi();
12
- const [records, setRecords] = useState([]);
13
- useEffect(() => {
14
- if (!enabled || ids.length === 0) {
15
- if (records.length > 0) {
16
- setRecords([]);
17
- }
18
- return;
19
- }
20
- (async () => {
21
- const joined = ids.join(' OR ');
22
- const result = await dispatchApi(api.v2.search.post('hit,observable,case', {
23
- query: `howler.id:(${joined}) OR case_id:(${joined})`
24
- }), { throwError: false, showError: true });
25
- if (result) {
26
- setRecords(result.items);
27
- }
28
- })();
29
- }, [dispatchApi, enabled, ids, records.length]);
30
- return records;
31
- };
32
- export default useRelatedRecords;
@@ -1,2 +0,0 @@
1
- declare const _default: import("react").NamedExoticComponent<{}>;
2
- export default _default;
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Stack } from '@mui/material';
3
- import { memo } from 'react';
4
- import { Outlet, useParams } from 'react-router-dom';
5
- import NotFoundPage from '../404';
6
- import ErrorBoundary from '../ErrorBoundary';
7
- import CaseDetails from './detail/CaseDetails';
8
- import CaseSidebar from './detail/CaseSidebar';
9
- import useCase from './hooks/useCase';
10
- const CaseViewer = () => {
11
- const params = useParams();
12
- const { case: _case, missing } = useCase({ caseId: params.id });
13
- if (missing) {
14
- return _jsx(NotFoundPage, {});
15
- }
16
- return (_jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case }), _jsx(Box, { sx: {
17
- maxHeight: 'calc(100vh - 64px)',
18
- flex: 1,
19
- overflow: 'auto'
20
- }, children: _jsx(ErrorBoundary, { children: _jsx(Outlet, { context: _case }) }) }), _jsx(CaseDetails, { case: _case })] }));
21
- };
22
- export default memo(CaseViewer);
@@ -1,2 +0,0 @@
1
- declare const Cases: () => import("react/jsx-runtime").JSX.Element;
2
- export default Cases;
@@ -1,101 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Topic } from '@mui/icons-material';
3
- import { Typography } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import { TuiListProvider } from '@cccsaurora/howler-ui/components/elements/addons/lists';
6
- import { TuiListMethodContext } from '@cccsaurora/howler-ui/components/elements/addons/lists/TuiListProvider';
7
- import ItemManager from '@cccsaurora/howler-ui/components/elements/display/ItemManager';
8
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
9
- import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
10
- import { useCallback, useContext, useEffect, useState } from 'react';
11
- import { useTranslation } from 'react-i18next';
12
- import { useNavigate, useSearchParams } from 'react-router-dom';
13
- import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
14
- import CaseCard from '../../elements/case/CaseCard';
15
- const CasesBase = () => {
16
- const { t } = useTranslation();
17
- const navigate = useNavigate();
18
- const { dispatchApi } = useMyApi();
19
- const [searchParams, setSearchParams] = useSearchParams();
20
- const { load } = useContext(TuiListMethodContext);
21
- const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
22
- const [phrase, setPhrase] = useState('');
23
- const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
24
- const [response, setResponse] = useState(null);
25
- const [hasError, setHasError] = useState(false);
26
- const [loading, setLoading] = useState(false);
27
- const onSearch = useCallback(async () => {
28
- try {
29
- setLoading(true);
30
- setHasError(false);
31
- if (phrase) {
32
- searchParams.set('phrase', phrase);
33
- }
34
- else {
35
- searchParams.delete('phrase');
36
- }
37
- setSearchParams(searchParams, { replace: true });
38
- // Check for the actual search query
39
- const query = phrase ? `*:*${phrase}*` : '*:*';
40
- // Ensure the overview should be visible and/or matches the type we are filtering for
41
- setResponse(await dispatchApi(api.search.case.post({
42
- query,
43
- rows: pageCount,
44
- offset
45
- })));
46
- }
47
- catch (e) {
48
- setHasError(true);
49
- }
50
- finally {
51
- setLoading(false);
52
- }
53
- }, [phrase, setSearchParams, searchParams, dispatchApi, pageCount, offset]);
54
- // Load the items into list when response changes.
55
- // This hook should only trigger when the 'response' changes.
56
- useEffect(() => {
57
- if (response) {
58
- load(response.items.map((item) => ({
59
- id: item.case_id,
60
- item,
61
- selected: false,
62
- cursor: false
63
- })));
64
- }
65
- // eslint-disable-next-line react-hooks/exhaustive-deps
66
- }, [response, load]);
67
- const onPageChange = useCallback((_offset) => {
68
- if (_offset !== offset) {
69
- searchParams.set('offset', _offset.toString());
70
- setSearchParams(searchParams, { replace: true });
71
- setOffset(_offset);
72
- }
73
- }, [offset, searchParams, setSearchParams]);
74
- useEffect(() => {
75
- onSearch();
76
- if (!searchParams.has('offset')) {
77
- searchParams.set('offset', '0');
78
- setSearchParams(searchParams, { replace: true });
79
- }
80
- // eslint-disable-next-line react-hooks/exhaustive-deps
81
- }, []);
82
- useEffect(() => {
83
- if (response?.total <= offset) {
84
- setOffset(0);
85
- searchParams.set('offset', '0');
86
- setSearchParams(searchParams, { replace: true });
87
- }
88
- }, [offset, response?.total, searchParams, setSearchParams]);
89
- useEffect(() => {
90
- if (!loading) {
91
- onSearch();
92
- }
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
- }, [offset]);
95
- const renderer = useCallback((item, className) => _jsx(CaseCard, { case: item, className: className }), []);
96
- return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: loading, aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.cases.search.prompt') }), renderer: ({ item }, classRenderer) => renderer(item.item, classRenderer()), response: response, onSelect: (item) => navigate(`/cases/${item.id}`), onCreate: () => navigate('/cases/create'), createPrompt: "route.cases.create", searchPrompt: "route.cases.manager.search", createIcon: _jsx(Topic, { sx: { mr: 1 } }) }));
97
- };
98
- const Cases = () => {
99
- return (_jsx(TuiListProvider, { children: _jsx(CasesBase, {}) }));
100
- };
101
- export default Cases;
@@ -1,5 +0,0 @@
1
- export declare const ESCALATION_COLOR_MAP: {
2
- normal: string;
3
- focus: string;
4
- crisis: string;
5
- };
@@ -1,5 +0,0 @@
1
- export const ESCALATION_COLOR_MAP = {
2
- normal: 'default',
3
- focus: 'warning',
4
- crisis: 'error'
5
- };
@@ -1,6 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import { type FC } from 'react';
3
- declare const AlertPanel: FC<{
4
- case: Case;
5
- }>;
6
- export default AlertPanel;
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Divider, Pagination, Skeleton, Stack, Typography, useTheme } from '@mui/material';
3
- import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
4
- import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
5
- import { chunk, uniq } from 'lodash-es';
6
- import { useMemo, useState } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Link } from 'react-router-dom';
9
- const AlertPanel = ({ case: _case }) => {
10
- const theme = useTheme();
11
- const { t } = useTranslation();
12
- const [alertPage, setAlertPage] = useState(1);
13
- const alertPages = useMemo(() => chunk(uniq((_case?.items ?? []).filter(item => item.type === 'hit')), 5), [_case?.items]);
14
- if (!_case) {
15
- return _jsx(Skeleton, { height: 240 });
16
- }
17
- return (_jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.alerts') }), _jsx(Pagination, { count: alertPages.length, page: alertPage, onChange: (_, page) => setAlertPage(page) })] }), _jsx(Divider, {}), alertPages?.length > 0 &&
18
- alertPages[alertPage - 1].map(item => (_jsxs(Box, { position: "relative", children: [_jsx(HitCard, { layout: HitLayout.DENSE, id: item.id }), _jsx(Box, { component: Link, to: item.path, sx: {
19
- position: 'absolute',
20
- top: 0,
21
- left: 0,
22
- width: '100%',
23
- height: '100%',
24
- cursor: 'pointer',
25
- zIndex: 100,
26
- borderRadius: '4px',
27
- '&:hover': {
28
- background: theme.palette.divider,
29
- border: `thin solid ${theme.palette.primary.light}`
30
- }
31
- } })] }, item.id)))] }));
32
- };
33
- export default AlertPanel;
@@ -1,12 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
4
- import { type FC } from 'react';
5
- import { type AssetEntry } from './assets/Asset';
6
- /** Deduplicate and merge seenIn lists into a map keyed by `type:value` */
7
- export declare const buildAssetEntries: (records: Partial<Hit | Observable>[]) => AssetEntry[];
8
- declare const CaseAssets: FC<{
9
- case?: Case;
10
- caseId?: string;
11
- }>;
12
- export default CaseAssets;