@cccsaurora/howler-ui 2.18.0-dev.736 → 2.18.0-dev.739

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 (274) hide show
  1. package/api/index.d.ts +0 -2
  2. package/api/index.js +2 -4
  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.js +7 -39
  9. package/components/app/hooks/useMatchers.d.ts +1 -1
  10. package/components/app/hooks/useMatchers.js +11 -23
  11. package/components/app/hooks/useMatchers.test.js +22 -22
  12. package/components/app/hooks/useTitle.js +3 -3
  13. package/components/app/providers/FavouritesProvider.js +2 -2
  14. package/components/app/providers/HitProvider.d.ts +22 -0
  15. package/components/app/providers/{RecordProvider.js → HitProvider.js} +41 -41
  16. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  17. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  18. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +70 -51
  19. package/components/app/providers/ModalProvider.d.ts +0 -1
  20. package/components/app/providers/ParameterProvider.d.ts +2 -9
  21. package/components/app/providers/ParameterProvider.js +240 -165
  22. package/components/app/providers/ParameterProvider.test.js +94 -346
  23. package/components/app/providers/UserListProvider.js +8 -28
  24. package/components/elements/PluginTypography.d.ts +1 -2
  25. package/components/elements/PluginTypography.js +2 -3
  26. package/components/elements/UserList.d.ts +2 -5
  27. package/components/elements/UserList.js +8 -18
  28. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  29. package/components/elements/display/ChipPopper.d.ts +1 -1
  30. package/components/elements/display/HowlerCard.js +1 -1
  31. package/components/elements/display/Modal.js +0 -2
  32. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  33. package/components/elements/display/icons/BundleButton.js +32 -0
  34. package/components/elements/hit/HitActions.js +4 -4
  35. package/components/elements/hit/HitBanner.d.ts +0 -1
  36. package/components/elements/hit/HitBanner.js +49 -29
  37. package/components/elements/hit/HitCard.d.ts +0 -2
  38. package/components/elements/hit/HitCard.js +7 -7
  39. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  40. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  41. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  42. package/components/elements/hit/HitLabels.js +2 -2
  43. package/components/elements/hit/HitOutline.d.ts +0 -1
  44. package/components/elements/hit/HitOutline.js +3 -3
  45. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  46. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  47. package/components/elements/hit/HitRelated.d.ts +6 -0
  48. package/components/elements/hit/HitRelated.js +7 -0
  49. package/components/elements/hit/HitSummary.d.ts +1 -2
  50. package/components/elements/hit/HitSummary.js +5 -6
  51. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  52. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  53. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  54. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  55. package/components/elements/view/ViewTitle.d.ts +0 -1
  56. package/components/elements/view/ViewTitle.js +2 -9
  57. package/components/hooks/useHitActions.d.ts +1 -1
  58. package/components/hooks/useHitActions.js +4 -4
  59. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  60. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  61. package/components/hooks/useMyPreferences.js +1 -10
  62. package/components/hooks/useMySearch.js +2 -2
  63. package/components/hooks/useMySitemap.js +1 -4
  64. package/components/hooks/useMyTheme.js +2 -9
  65. package/components/hooks/useParamState.test.js +4 -3
  66. package/components/routes/action/edit/ActionEditor.js +2 -2
  67. package/components/routes/action/view/ActionSearch.js +1 -1
  68. package/components/routes/advanced/QueryBuilder.js +1 -1
  69. package/components/routes/advanced/QueryEditor.js +3 -3
  70. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  71. package/components/routes/analytics/AnalyticDetails.js +2 -2
  72. package/components/routes/analytics/AnalyticSearch.js +1 -1
  73. package/components/routes/dossiers/DossierEditor.js +2 -2
  74. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  75. package/components/routes/help/ApiDocumentation.js +1 -1
  76. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  77. package/components/routes/help/BundleDocumentation.js +12 -0
  78. package/components/routes/help/HitBannerDocumentation.js +0 -1
  79. package/components/routes/help/HitDocumentation.js +3 -1
  80. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  81. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  82. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  83. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  84. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  85. package/components/routes/hits/search/BundleScroller.js +6 -0
  86. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  87. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  88. package/components/routes/hits/search/HitContextMenu.js +227 -0
  89. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +39 -94
  90. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  91. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  92. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  93. package/components/routes/hits/search/InformationPane.js +60 -47
  94. package/components/routes/hits/search/LayoutSettings.js +3 -3
  95. package/components/routes/hits/search/QuerySettings.js +1 -2
  96. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  97. package/components/routes/hits/search/SearchPane.js +49 -26
  98. package/components/routes/hits/search/ViewLink.js +3 -3
  99. package/components/routes/hits/search/ViewLink.test.js +8 -8
  100. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  101. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  102. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  103. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  104. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  105. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +8 -10
  106. package/components/routes/hits/view/HitViewer.js +13 -12
  107. package/components/routes/home/ViewCard.js +41 -47
  108. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  109. package/components/routes/overviews/OverviewViewer.js +2 -2
  110. package/components/routes/views/ViewComposer.js +19 -46
  111. package/locales/en/translation.json +3 -89
  112. package/locales/fr/translation.json +3 -87
  113. package/models/WithMetadata.d.ts +1 -2
  114. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  115. package/models/entities/generated/Hit.d.ts +0 -1
  116. package/models/entities/generated/Howler.d.ts +4 -0
  117. package/models/entities/generated/Rule.d.ts +10 -2
  118. package/models/entities/generated/Threat.d.ts +2 -2
  119. package/models/entities/generated/View.d.ts +0 -1
  120. package/package.json +103 -120
  121. package/plugins/clue/components/ClueTypography.js +2 -2
  122. package/plugins/clue/utils.d.ts +1 -2
  123. package/tests/mocks.d.ts +1 -11
  124. package/tests/mocks.js +7 -12
  125. package/tests/server-handlers.js +1 -6
  126. package/tests/server.d.ts +1 -1
  127. package/tests/utils.d.ts +0 -4
  128. package/tests/utils.js +0 -20
  129. package/utils/constants.d.ts +3 -3
  130. package/utils/hitFunctions.d.ts +1 -2
  131. package/utils/hitFunctions.js +4 -4
  132. package/utils/viewUtils.js +0 -3
  133. package/api/search/case.d.ts +0 -4
  134. package/api/search/case.js +0 -8
  135. package/api/v2/case/index.d.ts +0 -8
  136. package/api/v2/case/index.js +0 -20
  137. package/api/v2/case/items.d.ts +0 -6
  138. package/api/v2/case/items.js +0 -18
  139. package/api/v2/index.d.ts +0 -4
  140. package/api/v2/index.js +0 -6
  141. package/api/v2/search/facet.d.ts +0 -3
  142. package/api/v2/search/facet.js +0 -12
  143. package/api/v2/search/index.d.ts +0 -5
  144. package/api/v2/search/index.js +0 -24
  145. package/components/app/providers/RecordProvider.d.ts +0 -23
  146. package/components/elements/ContextMenu.d.ts +0 -56
  147. package/components/elements/ContextMenu.js +0 -109
  148. package/components/elements/ContextMenu.test.js +0 -215
  149. package/components/elements/ObjectDetails.d.ts +0 -6
  150. package/components/elements/case/CaseCard.d.ts +0 -12
  151. package/components/elements/case/CaseCard.js +0 -42
  152. package/components/elements/case/CasePreview.d.ts +0 -6
  153. package/components/elements/case/CasePreview.js +0 -17
  154. package/components/elements/case/StatusIcon.d.ts +0 -5
  155. package/components/elements/case/StatusIcon.js +0 -13
  156. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -9
  157. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  158. package/components/elements/hit/related/RelatedRecords.js +0 -63
  159. package/components/elements/observable/ObservableCard.d.ts +0 -6
  160. package/components/elements/observable/ObservableCard.js +0 -22
  161. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  162. package/components/elements/observable/ObservablePreview.js +0 -12
  163. package/components/elements/record/RecordContextMenu.js +0 -247
  164. package/components/elements/record/RecordContextMenu.test.d.ts +0 -1
  165. package/components/elements/record/RecordRelated.d.ts +0 -7
  166. package/components/elements/record/RecordRelated.js +0 -34
  167. package/components/hooks/useRelatedRecords.d.ts +0 -13
  168. package/components/hooks/useRelatedRecords.js +0 -32
  169. package/components/routes/cases/CaseViewer.d.ts +0 -2
  170. package/components/routes/cases/CaseViewer.js +0 -22
  171. package/components/routes/cases/Cases.d.ts +0 -2
  172. package/components/routes/cases/Cases.js +0 -101
  173. package/components/routes/cases/constants.d.ts +0 -5
  174. package/components/routes/cases/constants.js +0 -5
  175. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  176. package/components/routes/cases/detail/AlertPanel.js +0 -33
  177. package/components/routes/cases/detail/CaseAssets.d.ts +0 -11
  178. package/components/routes/cases/detail/CaseAssets.js +0 -104
  179. package/components/routes/cases/detail/CaseAssets.test.d.ts +0 -1
  180. package/components/routes/cases/detail/CaseAssets.test.js +0 -167
  181. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  182. package/components/routes/cases/detail/CaseDashboard.js +0 -66
  183. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  184. package/components/routes/cases/detail/CaseDetails.js +0 -61
  185. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  186. package/components/routes/cases/detail/CaseOverview.js +0 -43
  187. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
  188. package/components/routes/cases/detail/CaseSidebar.js +0 -107
  189. package/components/routes/cases/detail/CaseSidebar.test.d.ts +0 -1
  190. package/components/routes/cases/detail/CaseSidebar.test.js +0 -246
  191. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  192. package/components/routes/cases/detail/CaseTask.js +0 -57
  193. package/components/routes/cases/detail/CaseTimeline.d.ts +0 -12
  194. package/components/routes/cases/detail/CaseTimeline.js +0 -106
  195. package/components/routes/cases/detail/CaseTimeline.test.d.ts +0 -1
  196. package/components/routes/cases/detail/CaseTimeline.test.js +0 -227
  197. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  198. package/components/routes/cases/detail/ItemPage.js +0 -99
  199. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  200. package/components/routes/cases/detail/RelatedCasePanel.js +0 -34
  201. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  202. package/components/routes/cases/detail/TaskPanel.js +0 -52
  203. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -11
  204. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -24
  205. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  206. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -26
  207. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  208. package/components/routes/cases/detail/assets/Asset.js +0 -12
  209. package/components/routes/cases/detail/assets/Asset.test.d.ts +0 -1
  210. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  211. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -20
  212. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -83
  213. package/components/routes/cases/detail/sidebar/CaseFolder.test.d.ts +0 -1
  214. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +0 -295
  215. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
  216. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -103
  217. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +0 -1
  218. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -363
  219. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +0 -25
  220. package/components/routes/cases/detail/sidebar/FolderEntry.js +0 -88
  221. package/components/routes/cases/detail/sidebar/FolderEntry.test.d.ts +0 -1
  222. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +0 -206
  223. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +0 -5
  224. package/components/routes/cases/detail/sidebar/RootDropZone.js +0 -33
  225. package/components/routes/cases/detail/sidebar/types.d.ts +0 -9
  226. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  227. package/components/routes/cases/detail/sidebar/utils.js +0 -29
  228. package/components/routes/cases/detail/sidebar/utils.test.d.ts +0 -1
  229. package/components/routes/cases/detail/sidebar/utils.test.js +0 -82
  230. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  231. package/components/routes/cases/hooks/useCase.js +0 -51
  232. package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
  233. package/components/routes/cases/modals/AddToCaseModal.js +0 -62
  234. package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
  235. package/components/routes/cases/modals/RenameItemModal.js +0 -48
  236. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  237. package/components/routes/cases/modals/ResolveModal.js +0 -115
  238. package/components/routes/cases/modals/ResolveModal.test.d.ts +0 -1
  239. package/components/routes/cases/modals/ResolveModal.test.js +0 -384
  240. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  241. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  242. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  243. package/components/routes/observables/ObservableViewer.js +0 -27
  244. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  245. package/models/entities/generated/Case.d.ts +0 -28
  246. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  247. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  248. package/models/entities/generated/EmailParent.d.ts +0 -19
  249. package/models/entities/generated/Enrichments.d.ts +0 -7
  250. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  251. package/models/entities/generated/HttpResponse.d.ts +0 -11
  252. package/models/entities/generated/Item.d.ts +0 -9
  253. package/models/entities/generated/Observable.d.ts +0 -85
  254. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  255. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  256. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  257. package/models/entities/generated/ObservableFile.d.ts +0 -36
  258. package/models/entities/generated/ObservableHowler.d.ts +0 -43
  259. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  260. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  261. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  262. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  263. package/models/entities/generated/ObservableSource.d.ts +0 -23
  264. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  265. package/models/entities/generated/ObservableTls.d.ts +0 -12
  266. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  267. package/models/entities/generated/Task.d.ts +0 -10
  268. package/utils/typeUtils.d.ts +0 -7
  269. package/utils/typeUtils.js +0 -27
  270. /package/components/app/providers/{RecordSearchProvider.test.d.ts → HitSearchProvider.test.d.ts} +0 -0
  271. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  272. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  273. /package/components/{elements/ContextMenu.test.d.ts → routes/hits/search/HitContextMenu.test.d.ts} +0 -0
  274. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Check, HourglassBottom, Pause, Troubleshoot } from '@mui/icons-material';
3
- import { Tooltip } from '@mui/material';
4
- import { useTranslation } from 'react-i18next';
5
- const StatusIcon = ({ status }) => {
6
- const { t } = useTranslation();
7
- return (_jsx(Tooltip, { title: t(`page.cases.status.${status}`), children: {
8
- 'in-progress': _jsx(HourglassBottom, { color: "warning" }),
9
- 'on-hold': _jsx(Pause, { color: "disabled" }),
10
- resolved: _jsx(Check, { color: "success" })
11
- }[status] ?? _jsx(Troubleshoot, { color: "primary" }) }));
12
- };
13
- export default StatusIcon;
@@ -1,9 +0,0 @@
1
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
- import { type FC } from 'react';
3
- declare const AnalyticLink: FC<{
4
- hit: Hit;
5
- lazy?: boolean;
6
- compressed?: boolean;
7
- alignSelf?: string;
8
- }>;
9
- export default AnalyticLink;
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Typography } from '@mui/material';
3
- import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
4
- import { useEffect, useState } from 'react';
5
- import { Link } from 'react-router-dom';
6
- const AnalyticLink = ({ hit, lazy = false, compressed, alignSelf = 'start' }) => {
7
- const { getMatchingAnalytic } = useMatchers(lazy);
8
- const [analyticId, setAnalyticId] = useState();
9
- useEffect(() => {
10
- if (!hit?.howler.analytic) {
11
- return;
12
- }
13
- getMatchingAnalytic(hit).then(analytic => setAnalyticId(analytic?.analytic_id));
14
- // eslint-disable-next-line react-hooks/exhaustive-deps
15
- }, [hit?.howler.analytic]);
16
- return (_jsxs(Typography, { variant: compressed ? 'body1' : 'h6', fontWeight: compressed && 'bold', sx: { alignSelf, '& a': { color: 'text.primary' } }, children: [analyticId ? (_jsx(Link, { to: `/analytics/${analyticId}`, onAuxClick: e => {
17
- e.stopPropagation();
18
- }, onClick: e => {
19
- e.stopPropagation();
20
- }, children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }));
21
- };
22
- export default AnalyticLink;
@@ -1,63 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link as LinkIcon } from '@mui/icons-material';
3
- import { alpha, Box, Chip, Divider, Stack, useTheme } from '@mui/material';
4
- import CasePreview from '@cccsaurora/howler-ui/components/elements/case/CasePreview';
5
- import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
6
- import ObservablePreview from '@cccsaurora/howler-ui/components/elements/observable/ObservablePreview';
7
- import useRelatedRecords from '@cccsaurora/howler-ui/components/hooks/useRelatedRecords';
8
- import { identity, uniq } from 'lodash-es';
9
- import { memo, useMemo, useState } from 'react';
10
- import { useTranslation } from 'react-i18next';
11
- import { Link } from 'react-router-dom';
12
- import { isCase, isHit, isObservable } from '@cccsaurora/howler-ui/utils/typeUtils';
13
- import HitPreview from '../HitPreview';
14
- const RecordLink = ({ to, ariaLabel, children }) => {
15
- const theme = useTheme();
16
- return (_jsxs(Box, { p: 1, flex: 1, position: "relative", sx: {
17
- '& > a': {
18
- backgroundColor: 'transparent',
19
- transition: theme.transitions.create('background-color', {
20
- duration: theme.transitions.duration.shortest
21
- }),
22
- '&:hover': {
23
- backgroundColor: alpha('#555', 0.5)
24
- }
25
- }
26
- }, children: [_jsx(Link, { to: to, style: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel }), children] }));
27
- };
28
- const RelatedRecords = ({ hit }) => {
29
- const { t } = useTranslation();
30
- const [open, setOpen] = useState(false);
31
- const [filter, setFilter] = useState(null);
32
- const related = useMemo(() => hit?.howler.related ?? [], [hit?.howler.related]);
33
- const records = useRelatedRecords(related, open);
34
- return (_jsxs(ChipPopper
35
- // eslint-disable-next-line jsx-a11y/anchor-is-valid
36
- , {
37
- // eslint-disable-next-line jsx-a11y/anchor-is-valid
38
- icon: _jsx(LinkIcon, {}), label: _jsx("span", { style: { cursor: 'pointer' }, children: t('hit.header.related', { count: related.length }) }), slotProps: {
39
- chip: { disabled: related.length < 1 },
40
- paper: {
41
- elevation: 4,
42
- onAuxClick: ev => ev.stopPropagation()
43
- }
44
- }, disablePortal: false, placement: "bottom-end", onToggle: _open => setOpen(_open), children: [_jsx(Stack, { direction: "row", spacing: 1, mb: 1, justifyContent: "end", children: uniq(records.map(record => record.__index))
45
- .filter(identity)
46
- .map(_type => (_jsx(Chip, { color: _type === filter ? 'primary' : 'default', label: _type, onClick: () => setFilter(current => (current === _type ? null : _type)) }, _type))) }), _jsxs(Stack, { maxWidth: "40vw", maxHeight: "70vh", sx: { overflowY: 'auto' }, children: [_jsx(Divider, {}), records
47
- .filter(record => !filter || record.__index === filter)
48
- .map(entry => {
49
- if (isHit(entry)) {
50
- const key = entry.howler.id;
51
- return (_jsx(RecordLink, { to: `/hits/${key}`, ariaLabel: t('hit.header.view.hit', { id: key }), children: _jsx(HitPreview, { hit: entry }) }, key));
52
- }
53
- else if (isCase(entry)) {
54
- const key = entry.case_id;
55
- return (_jsx(RecordLink, { to: `/cases/${key}`, ariaLabel: t('hit.header.view.case', { id: key }), children: _jsx(CasePreview, { case: entry }) }, key));
56
- }
57
- else if (isObservable(entry)) {
58
- const key = entry.howler.id;
59
- return (_jsx(RecordLink, { to: `/observables/${key}`, ariaLabel: t('hit.header.view.observable', { id: key }), children: _jsx(ObservablePreview, { observable: entry }) }, key));
60
- }
61
- })] })] }));
62
- };
63
- export default memo(RelatedRecords);
@@ -1,6 +0,0 @@
1
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
2
- declare const _default: import("react").NamedExoticComponent<{
3
- id?: string;
4
- observable?: Observable;
5
- }>;
6
- export default _default;
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { CardContent, Skeleton } from '@mui/material';
3
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
4
- import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
5
- import { memo, useEffect } from 'react';
6
- import { useContextSelector } from 'use-context-selector';
7
- import ObservablePreview from './ObservablePreview';
8
- const ObservableCard = ({ id, observable: _observable }) => {
9
- const getRecord = useContextSelector(RecordContext, ctx => ctx.getRecord);
10
- const observable = useContextSelector(RecordContext, ctx => _observable ?? ctx.records[id]);
11
- useEffect(() => {
12
- if (!observable) {
13
- getRecord(id);
14
- }
15
- // eslint-disable-next-line react-hooks/exhaustive-deps
16
- }, [id]);
17
- if (!observable) {
18
- return _jsx(Skeleton, { variant: "rounded", height: "200px" });
19
- }
20
- return (_jsx(HowlerCard, { sx: { position: 'relative' }, children: _jsx(CardContent, { children: _jsx(ObservablePreview, { observable: observable }) }) }));
21
- };
22
- export default memo(ObservableCard);
@@ -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,247 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { AddCircleOutline, Assignment, CreateNewFolder, 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 { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
7
- import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
8
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
9
- import ContextMenu, {} from '@cccsaurora/howler-ui/components/elements/ContextMenu';
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 AddToCaseModal from '@cccsaurora/howler-ui/components/routes/cases/modals/AddToCaseModal';
15
- import { capitalize, get, groupBy, isEmpty, toString } from 'lodash-es';
16
- import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
17
- import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
18
- import { useTranslation } from 'react-i18next';
19
- import { usePluginStore } from 'react-pluggable';
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
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
24
- /**
25
- * Order in which action types should be displayed in the context menu
26
- */
27
- const ORDER = ['assessment', 'vote', 'action'];
28
- /**
29
- * Icon mapping for different action types
30
- */
31
- const ICON_MAP = {
32
- assessment: _jsx(Assignment, {}),
33
- vote: _jsx(HowToVote, {}),
34
- action: _jsx(Edit, {})
35
- };
36
- /**
37
- * Context menu component for hit operations.
38
- * Provides quick access to common hit actions including assessment, voting,
39
- * transitions, and exclusion filters based on template fields.
40
- */
41
- const RecordContextMenu = ({ children, getSelectedId, Component }) => {
42
- const { t } = useTranslation();
43
- const { dispatchApi } = useMyApi();
44
- const { executeAction } = useMyActionFunctions();
45
- const { config } = useContext(ApiConfigContext);
46
- const { showModal } = useContext(ModalContext);
47
- const pluginStore = usePluginStore();
48
- const { getMatchingAnalytic, getMatchingTemplate } = useMatchers();
49
- const query = useContextSelector(ParameterContext, ctx => ctx?.query);
50
- const setQuery = useContextSelector(ParameterContext, ctx => ctx?.setQuery);
51
- const [id, setId] = useState(null);
52
- const record = useContextSelector(RecordContext, ctx => ctx.records[id]);
53
- const selectedRecords = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
54
- const [analytic, setAnalytic] = useState(null);
55
- const [template, setTemplate] = useState(null);
56
- const [actions, setActions] = useState([]);
57
- const records = useMemo(() => selectedRecords.some(_record => _record.howler.id === record?.howler.id)
58
- ? selectedRecords
59
- : record
60
- ? [record]
61
- : [], [record, selectedRecords]);
62
- const hits = useMemo(() => records.filter(isHit), [records]);
63
- const { availableTransitions, canVote, canAssess, assess, vote } = useHitActions(hits);
64
- /**
65
- * Called by ContextMenu after the menu is positioned and opened.
66
- * Identifies the clicked record and fetches available actions.
67
- */
68
- const onOpen = useCallback(async (event) => {
69
- const _id = getSelectedId(event);
70
- setId(_id);
71
- const _actions = (await dispatchApi(api.search.action.post({ query: 'action_id:*' }), { throwError: false }))
72
- ?.items;
73
- if (_actions) {
74
- setActions(_actions);
75
- }
76
- }, [dispatchApi, getSelectedId]);
77
- const rowStatus = useMemo(() => ({
78
- assessment: canAssess,
79
- vote: canVote
80
- }), [canAssess, canVote]);
81
- const pluginActions = howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.actions`, records));
82
- /**
83
- * Generates grouped action entries for the context menu.
84
- * Combines transitions, plugin actions, votes, and assessments based on permissions.
85
- */
86
- const entries = useMemo(() => {
87
- let _actions = [...availableTransitions, ...pluginActions];
88
- if (canVote) {
89
- _actions = [
90
- ..._actions,
91
- ...VOTE_OPTIONS.map(option => ({ ...option, actionFunction: () => vote(option.name.toLowerCase()) }))
92
- ];
93
- }
94
- if (config.lookups?.['howler.assessment'] && canAssess) {
95
- _actions = [
96
- ..._actions,
97
- ...config.lookups['howler.assessment']
98
- .filter(_assessment => analytic?.triage_settings?.valid_assessments
99
- ? analytic.triage_settings?.valid_assessments.includes(_assessment)
100
- : true)
101
- .sort((a, b) => +TOP_ROW.includes(b) - +TOP_ROW.includes(a))
102
- .map(assessment => ({
103
- type: 'assessment',
104
- name: assessment,
105
- actionFunction: async () => {
106
- await assess(assessment, analytic?.triage_settings?.skip_rationale);
107
- }
108
- }))
109
- ];
110
- }
111
- return Object.entries(groupBy(_actions, 'type')).sort(([a], [b]) => ORDER.indexOf(a) - ORDER.indexOf(b));
112
- }, [analytic, assess, availableTransitions, canAssess, canVote, config.lookups, vote, pluginActions]);
113
- // Load analytic and template data when a hit is selected
114
- useEffect(() => {
115
- if (!record?.howler.analytic) {
116
- return;
117
- }
118
- getMatchingAnalytic(record).then(setAnalytic);
119
- getMatchingTemplate(record).then(setTemplate);
120
- // eslint-disable-next-line react-hooks/exhaustive-deps
121
- }, [record]);
122
- /**
123
- * Builds the declarative items structure for the ContextMenu component.
124
- */
125
- const items = useMemo(() => {
126
- const result = [
127
- {
128
- kind: 'item',
129
- id: 'open-record',
130
- icon: _jsx(OpenInNew, {}),
131
- label: t(`${record?.__index ?? 'hit'}.open`),
132
- disabled: !record,
133
- to: `/${record?.__index}s/${record?.howler.id}`
134
- }
135
- ];
136
- if (isHit(record)) {
137
- result.push({
138
- kind: 'item',
139
- id: 'open-analytic',
140
- icon: _jsx(QueryStats, {}),
141
- label: t('analytic.open'),
142
- disabled: !analytic,
143
- to: `/analytics/${analytic?.analytic_id}`
144
- });
145
- result.push({ kind: 'divider', id: 'actions-divider' });
146
- for (const [type, typeItems] of entries) {
147
- result.push({
148
- kind: 'submenu',
149
- id: type,
150
- icon: ICON_MAP[type] ?? _jsx(Terminal, {}),
151
- label: t(`hit.details.actions.${type}`),
152
- disabled: rowStatus[type] === false,
153
- items: typeItems.map(a => ({
154
- key: a.name,
155
- label: a.i18nKey ? t(a.i18nKey) : capitalize(a.name),
156
- onClick: a.actionFunction
157
- }))
158
- });
159
- }
160
- result.push({
161
- kind: 'submenu',
162
- id: 'actions',
163
- icon: _jsx(SettingsSuggest, {}),
164
- label: t('route.actions.change'),
165
- disabled: actions.length < 1,
166
- items: actions.map(action => ({
167
- key: action.action_id,
168
- label: action.name,
169
- onClick: () => executeAction(action.action_id, `howler.id:${record?.howler.id}`)
170
- }))
171
- });
172
- if (!isEmpty(template?.keys ?? []) && setQuery) {
173
- result.push({ kind: 'divider', id: 'filter-divider' });
174
- result.push({
175
- kind: 'submenu',
176
- id: 'excludes',
177
- icon: _jsx(RemoveCircleOutline, {}),
178
- label: t('hit.panel.exclude'),
179
- items: (template?.keys ?? []).flatMap(key => {
180
- let newQuery = '';
181
- if (query !== DEFAULT_QUERY) {
182
- newQuery = `(${query}) AND `;
183
- }
184
- const value = get(record, key);
185
- if (!value) {
186
- return [];
187
- }
188
- else if (Array.isArray(value)) {
189
- const sanitizedValues = value
190
- .map(toString)
191
- .filter(val => !!val)
192
- .map(val => `"${sanitizeLuceneQuery(val)}"`);
193
- if (sanitizedValues.length < 1) {
194
- return [];
195
- }
196
- newQuery += `-${key}:(${sanitizedValues.join(' OR ')})`;
197
- }
198
- else {
199
- newQuery += `-${key}:"${sanitizeLuceneQuery(value.toString())}"`;
200
- }
201
- return [{ key, label: key, onClick: () => setQuery(newQuery) }];
202
- })
203
- });
204
- result.push({
205
- kind: 'submenu',
206
- id: 'includes',
207
- icon: _jsx(AddCircleOutline, {}),
208
- label: t('hit.panel.include'),
209
- items: (template?.keys ?? []).flatMap(key => {
210
- let newQuery = `(${query}) AND `;
211
- const value = get(record, key);
212
- if (!value) {
213
- return [];
214
- }
215
- else if (Array.isArray(value)) {
216
- const sanitizedValues = value
217
- .map(toString)
218
- .filter(val => !!val)
219
- .map(val => `"${sanitizeLuceneQuery(val)}"`);
220
- if (sanitizedValues.length < 1) {
221
- return [];
222
- }
223
- newQuery += `${key}:(${sanitizedValues.join(' OR ')})`;
224
- }
225
- else {
226
- newQuery += `${key}:"${sanitizeLuceneQuery(value.toString())}"`;
227
- }
228
- return [{ key, label: key, onClick: () => setQuery(newQuery) }];
229
- })
230
- });
231
- }
232
- }
233
- result.push({ kind: 'divider', id: 'add-to-case-divider' });
234
- result.push({
235
- kind: 'item',
236
- id: 'add-to-case',
237
- icon: _jsx(CreateNewFolder, {}),
238
- label: t('modal.cases.add_to_case'),
239
- disabled: !record,
240
- onClick: () => showModal(_jsx(AddToCaseModal, { records: records }))
241
- });
242
- return result;
243
- // eslint-disable-next-line react-hooks/exhaustive-deps
244
- }, [record, analytic, template, entries, rowStatus, actions, query, t, setQuery, executeAction, showModal, records]);
245
- return (_jsx(ContextMenu, { id: "contextMenu", Component: Component, onOpen: onOpen, onClose: () => setAnalytic(null), items: items, children: children }));
246
- };
247
- 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, update } = useCase({ caseId: params.id });
13
- if (missing) {
14
- return _jsx(NotFoundPage, {});
15
- }
16
- return (_jsx(ErrorBoundary, { children: _jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case, update: updatedCase => update(updatedCase, false) }), _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
- };