@cccsaurora/howler-ui 2.18.0-dev.703 → 2.18.0-dev.704

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 (246) 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/ModalProvider.d.ts +0 -1
  17. package/components/app/providers/ParameterProvider.d.ts +2 -9
  18. package/components/app/providers/ParameterProvider.js +240 -165
  19. package/components/app/providers/ParameterProvider.test.js +14 -307
  20. package/components/elements/PluginTypography.d.ts +1 -2
  21. package/components/elements/PluginTypography.js +2 -3
  22. package/components/elements/UserList.d.ts +2 -5
  23. package/components/elements/UserList.js +5 -14
  24. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  25. package/components/elements/display/ChipPopper.d.ts +1 -1
  26. package/components/elements/display/HowlerCard.js +1 -1
  27. package/components/elements/display/Modal.js +0 -2
  28. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  29. package/components/elements/display/icons/BundleButton.js +32 -0
  30. package/components/elements/hit/HitActions.js +4 -4
  31. package/components/elements/hit/HitBanner.js +48 -28
  32. package/components/elements/hit/HitCard.js +5 -5
  33. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  34. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  35. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  36. package/components/elements/hit/HitLabels.js +2 -2
  37. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  38. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  39. package/components/elements/hit/HitRelated.d.ts +6 -0
  40. package/components/elements/hit/HitRelated.js +7 -0
  41. package/components/elements/hit/HitSummary.d.ts +1 -2
  42. package/components/elements/hit/HitSummary.js +5 -6
  43. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  44. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  45. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  46. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  47. package/components/elements/view/ViewTitle.d.ts +0 -1
  48. package/components/elements/view/ViewTitle.js +2 -9
  49. package/components/hooks/useHitActions.d.ts +1 -1
  50. package/components/hooks/useHitActions.js +4 -4
  51. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  52. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  53. package/components/hooks/useMyPreferences.js +1 -10
  54. package/components/hooks/useMySearch.js +2 -2
  55. package/components/hooks/useMySitemap.js +1 -4
  56. package/components/hooks/useMyTheme.js +2 -9
  57. package/components/hooks/useParamState.test.js +4 -3
  58. package/components/routes/action/edit/ActionEditor.js +2 -2
  59. package/components/routes/action/view/ActionSearch.js +1 -1
  60. package/components/routes/advanced/QueryBuilder.js +1 -1
  61. package/components/routes/advanced/QueryEditor.js +3 -3
  62. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  63. package/components/routes/analytics/AnalyticDetails.js +2 -2
  64. package/components/routes/analytics/AnalyticSearch.js +1 -1
  65. package/components/routes/dossiers/DossierEditor.js +2 -2
  66. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  67. package/components/routes/help/ApiDocumentation.js +1 -1
  68. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  69. package/components/routes/help/BundleDocumentation.js +12 -0
  70. package/components/routes/help/HitBannerDocumentation.js +0 -1
  71. package/components/routes/help/HitDocumentation.js +3 -1
  72. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  73. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  74. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  75. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  76. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  77. package/components/routes/hits/search/BundleScroller.js +6 -0
  78. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  79. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  80. package/components/routes/hits/search/HitContextMenu.js +227 -0
  81. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +39 -94
  82. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  83. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  84. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  85. package/components/routes/hits/search/InformationPane.js +60 -47
  86. package/components/routes/hits/search/LayoutSettings.js +3 -3
  87. package/components/routes/hits/search/QuerySettings.js +1 -2
  88. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  89. package/components/routes/hits/search/SearchPane.js +49 -26
  90. package/components/routes/hits/search/ViewLink.js +3 -3
  91. package/components/routes/hits/search/ViewLink.test.js +8 -8
  92. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  93. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  94. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  95. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  96. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  97. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +8 -10
  98. package/components/routes/hits/view/HitViewer.js +13 -12
  99. package/components/routes/home/ViewCard.js +41 -47
  100. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  101. package/components/routes/overviews/OverviewViewer.js +2 -2
  102. package/components/routes/views/ViewComposer.js +19 -46
  103. package/locales/en/translation.json +3 -80
  104. package/locales/fr/translation.json +3 -78
  105. package/models/WithMetadata.d.ts +1 -2
  106. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  107. package/models/entities/generated/Hit.d.ts +0 -1
  108. package/models/entities/generated/Howler.d.ts +4 -0
  109. package/models/entities/generated/Rule.d.ts +10 -2
  110. package/models/entities/generated/Threat.d.ts +2 -2
  111. package/models/entities/generated/View.d.ts +0 -1
  112. package/package.json +2 -19
  113. package/plugins/clue/components/ClueTypography.js +2 -2
  114. package/plugins/clue/helpers.js +1 -1
  115. package/plugins/clue/utils.d.ts +1 -2
  116. package/tests/server-handlers.js +1 -6
  117. package/tests/utils.d.ts +0 -4
  118. package/tests/utils.js +0 -20
  119. package/utils/constants.d.ts +3 -3
  120. package/utils/hitFunctions.d.ts +1 -2
  121. package/utils/hitFunctions.js +4 -4
  122. package/utils/viewUtils.js +0 -3
  123. package/api/search/case.d.ts +0 -4
  124. package/api/search/case.js +0 -8
  125. package/api/v2/case/index.d.ts +0 -8
  126. package/api/v2/case/index.js +0 -20
  127. package/api/v2/case/items.d.ts +0 -6
  128. package/api/v2/case/items.js +0 -18
  129. package/api/v2/index.d.ts +0 -4
  130. package/api/v2/index.js +0 -6
  131. package/api/v2/search/facet.d.ts +0 -3
  132. package/api/v2/search/facet.js +0 -12
  133. package/api/v2/search/index.d.ts +0 -5
  134. package/api/v2/search/index.js +0 -24
  135. package/components/app/providers/RecordProvider.d.ts +0 -23
  136. package/components/elements/ContextMenu.d.ts +0 -56
  137. package/components/elements/ContextMenu.js +0 -109
  138. package/components/elements/ContextMenu.test.js +0 -215
  139. package/components/elements/ObjectDetails.d.ts +0 -6
  140. package/components/elements/case/CaseCard.d.ts +0 -12
  141. package/components/elements/case/CaseCard.js +0 -42
  142. package/components/elements/case/CasePreview.d.ts +0 -6
  143. package/components/elements/case/CasePreview.js +0 -17
  144. package/components/elements/case/StatusIcon.d.ts +0 -5
  145. package/components/elements/case/StatusIcon.js +0 -13
  146. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
  147. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  148. package/components/elements/hit/related/RelatedRecords.js +0 -63
  149. package/components/elements/observable/ObservableCard.d.ts +0 -6
  150. package/components/elements/observable/ObservableCard.js +0 -23
  151. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  152. package/components/elements/observable/ObservablePreview.js +0 -12
  153. package/components/elements/record/RecordContextMenu.js +0 -247
  154. package/components/elements/record/RecordContextMenu.test.d.ts +0 -1
  155. package/components/elements/record/RecordRelated.d.ts +0 -7
  156. package/components/elements/record/RecordRelated.js +0 -34
  157. package/components/hooks/useRelatedRecords.d.ts +0 -13
  158. package/components/hooks/useRelatedRecords.js +0 -32
  159. package/components/routes/cases/CaseViewer.d.ts +0 -2
  160. package/components/routes/cases/CaseViewer.js +0 -22
  161. package/components/routes/cases/Cases.d.ts +0 -2
  162. package/components/routes/cases/Cases.js +0 -101
  163. package/components/routes/cases/constants.d.ts +0 -5
  164. package/components/routes/cases/constants.js +0 -5
  165. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  166. package/components/routes/cases/detail/AlertPanel.js +0 -33
  167. package/components/routes/cases/detail/CaseAssets.d.ts +0 -12
  168. package/components/routes/cases/detail/CaseAssets.js +0 -104
  169. package/components/routes/cases/detail/CaseAssets.test.d.ts +0 -1
  170. package/components/routes/cases/detail/CaseAssets.test.js +0 -167
  171. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  172. package/components/routes/cases/detail/CaseDashboard.js +0 -54
  173. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  174. package/components/routes/cases/detail/CaseDetails.js +0 -61
  175. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  176. package/components/routes/cases/detail/CaseOverview.js +0 -43
  177. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
  178. package/components/routes/cases/detail/CaseSidebar.js +0 -61
  179. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  180. package/components/routes/cases/detail/CaseTask.js +0 -57
  181. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  182. package/components/routes/cases/detail/ItemPage.js +0 -99
  183. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  184. package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
  185. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  186. package/components/routes/cases/detail/TaskPanel.js +0 -52
  187. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
  188. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
  189. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  190. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -30
  191. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  192. package/components/routes/cases/detail/assets/Asset.js +0 -12
  193. package/components/routes/cases/detail/assets/Asset.test.d.ts +0 -1
  194. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  195. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -14
  196. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -133
  197. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
  198. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -105
  199. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +0 -1
  200. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -351
  201. package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
  202. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  203. package/components/routes/cases/detail/sidebar/utils.js +0 -25
  204. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  205. package/components/routes/cases/hooks/useCase.js +0 -51
  206. package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
  207. package/components/routes/cases/modals/AddToCaseModal.js +0 -62
  208. package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
  209. package/components/routes/cases/modals/RenameItemModal.js +0 -48
  210. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  211. package/components/routes/cases/modals/ResolveModal.js +0 -62
  212. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  213. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  214. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  215. package/components/routes/observables/ObservableViewer.js +0 -27
  216. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  217. package/models/entities/generated/Case.d.ts +0 -28
  218. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  219. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  220. package/models/entities/generated/EmailParent.d.ts +0 -19
  221. package/models/entities/generated/Enrichments.d.ts +0 -7
  222. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  223. package/models/entities/generated/HttpResponse.d.ts +0 -11
  224. package/models/entities/generated/Item.d.ts +0 -9
  225. package/models/entities/generated/Observable.d.ts +0 -85
  226. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  227. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  228. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  229. package/models/entities/generated/ObservableFile.d.ts +0 -36
  230. package/models/entities/generated/ObservableHowler.d.ts +0 -43
  231. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  232. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  233. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  234. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  235. package/models/entities/generated/ObservableSource.d.ts +0 -23
  236. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  237. package/models/entities/generated/ObservableTls.d.ts +0 -12
  238. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  239. package/models/entities/generated/Task.d.ts +0 -10
  240. package/utils/typeUtils.d.ts +0 -7
  241. package/utils/typeUtils.js +0 -27
  242. /package/components/app/providers/{RecordSearchProvider.test.d.ts → HitSearchProvider.test.d.ts} +0 -0
  243. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  244. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  245. /package/components/{elements/ContextMenu.test.d.ts → routes/hits/search/HitContextMenu.test.d.ts} +0 -0
  246. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -3,24 +3,23 @@ import { Add, Check, Settings, TableChart } from '@mui/icons-material';
3
3
  import { Autocomplete, Chip, Divider, Grid, IconButton, Stack, TextField } from '@mui/material';
4
4
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
5
5
  import { FieldContext } from '@cccsaurora/howler-ui/components/app/providers/FieldProvider';
6
- import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
6
+ import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
7
7
  import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
8
- import { sortBy, uniq } from 'lodash-es';
8
+ import { has, sortBy, uniq } from 'lodash-es';
9
9
  import { memo, useContext, useEffect, useMemo, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { useContextSelector } from 'use-context-selector';
12
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
13
12
  const AddColumnModal = ({ addColumn, columns }) => {
14
13
  const { t } = useTranslation();
15
14
  const { hitFields } = useContext(FieldContext);
16
- const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
15
+ const response = useContextSelector(HitSearchContext, ctx => ctx.response);
17
16
  const { getMatchingTemplate } = useMatchers();
18
17
  const [columnToAdd, setColumnToAdd] = useState(null);
19
18
  const options = useMemo(() => hitFields.map(field => field.key), [hitFields]);
20
19
  const [suggestions, setSuggestions] = useState([]);
21
20
  useEffect(() => {
22
21
  (async () => {
23
- setSuggestions(uniq((await Promise.all((response?.items ?? []).filter(isHit).map(async (_hit) => (await getMatchingTemplate(_hit))?.keys ?? []))).flat()));
22
+ setSuggestions(uniq((await Promise.all((response?.items ?? []).map(async (_hit) => (has(_hit, '__template') ? _hit.__template?.keys : (await getMatchingTemplate(_hit))?.keys) ?? []))).flat()));
24
23
  })();
25
24
  }, [getMatchingTemplate, response?.items]);
26
25
  return (_jsx(ChipPopper, { icon: _jsx(TableChart, {}), deleteIcon: _jsx(Settings, {}), toggleOnDelete: true, slotProps: { chip: { size: 'small' } }, children: _jsxs(Stack, { spacing: 1, p: 1, width: "500px", children: [_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Autocomplete, { sx: { flex: 1 }, size: "small", options: options, value: columnToAdd, renderInput: params => _jsx(TextField, { fullWidth: true, placeholder: t('hit.fields'), ...params }), onChange: (_ev, value) => setColumnToAdd(value) }), _jsx(IconButton, { disabled: !columnToAdd, onClick: () => {
@@ -1,8 +1,7 @@
1
1
  import { type SxProps } from '@mui/material';
2
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
3
  declare const _default: import("react").NamedExoticComponent<{
5
- record: Hit | Observable;
4
+ hit: Hit;
6
5
  value: string;
7
6
  sx?: SxProps;
8
7
  className: string;
@@ -4,7 +4,7 @@ import { Stack, TableCell } from '@mui/material';
4
4
  import PluginTypography from '@cccsaurora/howler-ui/components/elements/PluginTypography';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
- const EnhancedCell = ({ record, value: rawValue, sx = {}, className, field }) => {
7
+ const EnhancedCell = ({ hit, value: rawValue, sx = {}, className, field }) => {
8
8
  const { t } = useTranslation();
9
9
  if (!rawValue) {
10
10
  return _jsx(TableCell, { style: { borderBottom: 'none' }, children: t('none') });
@@ -13,6 +13,6 @@ const EnhancedCell = ({ record, value: rawValue, sx = {}, className, field }) =>
13
13
  return (_jsx(TableCell, { sx: { borderBottom: 'none', borderRight: 'thin solid', borderRightColor: 'divider', fontSize: '0.8rem' }, children: _jsx(Stack, { direction: "row", className: className, spacing: 0.5, sx: [
14
14
  { display: 'flex', justifyContent: 'start', width: '100%', overflow: 'hidden' },
15
15
  ...(Array.isArray(sx) ? sx : [sx])
16
- ], children: values.map((value, index) => (_jsx(PluginTypography, { context: "table", sx: { fontSize: 'inherit', textOverflow: 'ellipsis' }, value: value, field: field, obj: record, children: value }, value + index))) }) }));
16
+ ], children: values.map((value, index) => (_jsx(PluginTypography, { context: "table", sx: { fontSize: 'inherit', textOverflow: 'ellipsis' }, value: value, field: field, hit: hit, children: value }, value + index))) }) }));
17
17
  };
18
18
  export default memo(EnhancedCell);
@@ -4,37 +4,36 @@ import { arrayMove, SortableContext, sortableKeyboardCoordinates } from '@dnd-ki
4
4
  import { FormatIndentDecrease, FormatIndentIncrease, Info, List, Search, TableChart } from '@mui/icons-material';
5
5
  import { IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow, ToggleButton, ToggleButtonGroup, Typography, useTheme } from '@mui/material';
6
6
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
+ import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
7
9
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
8
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
9
- import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
10
10
  import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
11
11
  import DevelopmentBanner from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentBanner';
12
- import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
12
+ import useHitSelection from '@cccsaurora/howler-ui/components/hooks/useHitSelection';
13
13
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
14
- import useRecordSelection from '@cccsaurora/howler-ui/components/hooks/useRecordSelection';
15
14
  import { uniq } from 'lodash-es';
16
15
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
17
16
  import { useTranslation } from 'react-i18next';
18
17
  import { useContextSelector } from 'use-context-selector';
19
18
  import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
20
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
19
+ import HitContextMenu from '../HitContextMenu';
20
+ import HitQuery from '../HitQuery';
21
21
  import QuerySettings from '../QuerySettings';
22
- import RecordQuery from '../RecordQuery';
23
22
  import AddColumnModal from './AddColumnModal';
24
23
  import ColumnHeader from './ColumnHeader';
25
- import RecordRow from './RecordRow';
24
+ import HitRow from './HitRow';
26
25
  const HitGrid = () => {
27
26
  const { t } = useTranslation();
28
27
  const theme = useTheme();
29
28
  const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
30
- const { onClick } = useRecordSelection();
29
+ const { onClick } = useHitSelection();
31
30
  const { getMatchingAnalytic } = useMatchers();
32
- const search = useContextSelector(RecordSearchContext, ctx => ctx.search);
33
- const displayType = useContextSelector(RecordSearchContext, ctx => ctx.displayType);
34
- const setDisplayType = useContextSelector(RecordSearchContext, ctx => ctx.setDisplayType);
35
- const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
36
- const searching = useContextSelector(RecordSearchContext, ctx => ctx.searching);
37
- const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
31
+ const search = useContextSelector(HitSearchContext, ctx => ctx.search);
32
+ const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
33
+ const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
34
+ const response = useContextSelector(HitSearchContext, ctx => ctx.response);
35
+ const searching = useContextSelector(HitSearchContext, ctx => ctx.searching);
36
+ const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
38
37
  const query = useContextSelector(ParameterContext, ctx => ctx.query);
39
38
  const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
40
39
  const [collapseMainColumn, setCollapseMainColumn] = useMyLocalStorageItem(StorageKey.GRID_COLLAPSE_COLUMN, false);
@@ -57,11 +56,10 @@ const HitGrid = () => {
57
56
  return false;
58
57
  }, [selected, selectedHits]);
59
58
  useEffect(() => {
60
- response?.items.forEach(record => {
61
- if (!isHit(record) || analyticIds[record.howler.analytic]) {
62
- return;
59
+ response?.items.forEach(hit => {
60
+ if (!analyticIds[hit.howler.analytic]) {
61
+ getMatchingAnalytic(hit).then(_analytic => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [hit.howler.analytic]: _analytic.analytic_id })));
63
62
  }
64
- getMatchingAnalytic(record).then(_analytic => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [record.howler.analytic]: _analytic.analytic_id })));
65
63
  });
66
64
  // eslint-disable-next-line react-hooks/exhaustive-deps
67
65
  }, [analyticIds, response]);
@@ -119,7 +117,7 @@ const HitGrid = () => {
119
117
  }
120
118
  return selectedElement.id;
121
119
  }, []);
122
- return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), response && (_jsx(SearchTotal, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5 }, variant: "body2", offset: response.offset, pageLength: response.rows, total: response.total }))] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(RecordQuery, { searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
120
+ return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), response && (_jsx(SearchTotal, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5 }, variant: "body2", offset: response.offset, pageLength: response.rows, total: response.total }))] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(HitQuery, { searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
123
121
  position: 'absolute',
124
122
  left: 0,
125
123
  right: 0,
@@ -129,6 +127,6 @@ const HitGrid = () => {
129
127
  } }))] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", children: [_jsx(ToggleButton, { value: "list", children: _jsx(List, {}) }), _jsx(ToggleButton, { value: "grid", children: _jsx(TableChart, {}) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", alignItems: "center", children: [_jsx(QuerySettings, { boxSx: { flex: 1 } }), _jsx(AddColumnModal, { columns: columns, addColumn: key => setColumns(uniq([...columns, key])) })] }), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsxs(Table, { sx: { '& td,th': { px: 1, py: 0.25, whiteSpace: 'nowrap' } }, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { sx: {
130
128
  borderRight: 'thin solid',
131
129
  borderRightColor: 'divider'
132
- }, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(RecordContextMenu, { Component: TableBody, getSelectedId: getSelectedId, children: [response?.items.map(hit => (_jsx(RecordRow, { record: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn, onClick: onClick }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
130
+ }, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(HitContextMenu, { Component: TableBody, getSelectedId: getSelectedId, children: [response?.items.map(hit => (_jsx(HitRow, { hit: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn, onClick: onClick }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
133
131
  };
134
132
  export default HitGrid;
@@ -1,11 +1,10 @@
1
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
2
  declare const _default: import("react").NamedExoticComponent<{
4
- record: Hit | Observable;
3
+ hit: Hit;
5
4
  analyticIds: Record<string, string>;
6
5
  columns: string[];
7
6
  columnWidths: Record<string, string>;
8
7
  collapseMainColumn: boolean;
9
- onClick: (e: React.MouseEvent<HTMLDivElement>, record: Hit | Observable) => void;
8
+ onClick: (e: React.MouseEvent<HTMLDivElement>, hit: Hit) => void;
10
9
  }>;
11
10
  export default _default;
@@ -1,27 +1,25 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { KeyboardArrowUp } from '@mui/icons-material';
3
3
  import { Box, Collapse, IconButton, lighten, Stack, TableCell, TableRow, Typography, useTheme } from '@mui/material';
4
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
4
5
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
5
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
6
6
  import Assigned from '@cccsaurora/howler-ui/components/elements/hit/elements/Assigned';
7
7
  import EscalationChip from '@cccsaurora/howler-ui/components/elements/hit/elements/EscalationChip';
8
8
  import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
9
9
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
10
- import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
11
10
  import { get } from 'lodash-es';
12
11
  import { memo, useState } from 'react';
13
12
  import { useTranslation } from 'react-i18next';
14
13
  import { Link } from 'react-router-dom';
15
14
  import { useContextSelector } from 'use-context-selector';
16
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
17
15
  import EnhancedCell from './EnhancedCell';
18
- const RecordRow = ({ record, analyticIds, columns, columnWidths, collapseMainColumn, onClick }) => {
16
+ const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn, onClick }) => {
19
17
  const theme = useTheme();
20
18
  const { t } = useTranslation();
21
- const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
19
+ const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
22
20
  const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
23
21
  const [expandRow, setExpandRow] = useState(false);
24
- return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { id: record.howler.id, onClick: ev => onClick(ev, record), sx: [
22
+ return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { id: hit.howler.id, onClick: ev => onClick(ev, hit), sx: [
25
23
  {
26
24
  transition: theme.transitions.create('background-color'),
27
25
  '&:hover': {
@@ -29,10 +27,10 @@ const RecordRow = ({ record, analyticIds, columns, columnWidths, collapseMainCol
29
27
  backgroundColor: theme.palette.background.paper
30
28
  }
31
29
  },
32
- selectedHits.some(_hit => _hit.howler.id === record.howler.id) && {
30
+ selectedHits.some(_hit => _hit.howler.id === hit.howler.id) && {
33
31
  backgroundColor: lighten(theme.palette.background.paper, 0.15)
34
32
  },
35
- selected === record.howler.id && {
33
+ selected === hit.howler.id && {
36
34
  backgroundColor: lighten(theme.palette.background.paper, 0.25)
37
35
  }
38
36
  ], children: [_jsx(TableCell, { sx: {
@@ -47,6 +45,6 @@ const RecordRow = ({ record, analyticIds, columns, columnWidths, collapseMainCol
47
45
  e.preventDefault();
48
46
  e.stopPropagation();
49
47
  setExpandRow(_expanded => !_expanded);
50
- }, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [isHit(record) && _jsx(EscalationChip, { hit: record, layout: HitLayout.DENSE, hideLabel: true }), _jsxs(Typography, { sx: { textWrap: 'nowrap', whiteSpace: 'nowrap', fontSize: 'inherit' }, children: [analyticIds[record.howler.analytic] ? (_jsx(Link, { to: `/analytics/${analyticIds[record.howler.analytic]}`, onClick: e => e.stopPropagation(), children: record.howler.analytic })) : (record.howler.analytic), record.howler.detection && ': ', record.howler.detection] }), isHit(record) && record.howler.assignment !== 'unassigned' && (_jsx(Assigned, { hit: record, layout: HitLayout.DENSE, hideLabel: true }))] }) })] }) }), columns.map(col => (_jsx(EnhancedCell, { record: record, className: `col-${col.replaceAll('.', '-')}`, value: get(record, col) ?? t('none'), sx: columnWidths[col] ? { width: columnWidths[col] } : { width: '220px', maxWidth: '300px' }, field: col }, col)))] }, record.howler.id), _jsx(TableRow, { onClick: ev => onClick(ev, record), children: _jsx(TableCell, { colSpan: columns.length + 2, style: { paddingBottom: 0, paddingTop: 0 }, children: _jsx(Collapse, { in: expandRow, unmountOnExit: true, children: _jsx(Box, { width: "100%", maxWidth: "1200px", margin: 1, children: isHit(record) ? (_jsx(HitCard, { id: record.howler.id, layout: HitLayout.NORMAL })) : (_jsx(ObservableCard, { id: record.howler.id, observable: record })) }) }) }) })] }));
48
+ }, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [_jsx(EscalationChip, { hit: hit, layout: HitLayout.DENSE, hideLabel: true }), _jsxs(Typography, { sx: { textWrap: 'nowrap', whiteSpace: 'nowrap', fontSize: 'inherit' }, children: [analyticIds[hit.howler.analytic] ? (_jsx(Link, { to: `/analytics/${analyticIds[hit.howler.analytic]}`, onClick: e => e.stopPropagation(), children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }), hit.howler.assignment !== 'unassigned' && _jsx(Assigned, { hit: hit, layout: HitLayout.DENSE, hideLabel: true })] }) })] }) }), columns.map(col => (_jsx(EnhancedCell, { hit: hit, className: `col-${col.replaceAll('.', '-')}`, value: get(hit, col) ?? t('none'), sx: columnWidths[col] ? { width: columnWidths[col] } : { width: '220px', maxWidth: '300px' }, field: col }, col)))] }, hit.howler.id), _jsx(TableRow, { onClick: ev => onClick(ev, hit), children: _jsx(TableCell, { colSpan: columns.length + 2, style: { paddingBottom: 0, paddingTop: 0 }, children: _jsx(Collapse, { in: expandRow, unmountOnExit: true, children: _jsx(Box, { width: "100%", maxWidth: "1200px", margin: 1, children: _jsx(HitCard, { id: hit.howler.id, layout: HitLayout.NORMAL }) }) }) }) })] }));
51
49
  };
52
- export default memo(RecordRow);
50
+ export default memo(HitRow);
@@ -4,22 +4,23 @@ import { Code, Comment, DataObject, History, LinkSharp, QueryStats, ViewAgenda }
4
4
  import { Badge, Box, CardContent, Collapse, IconButton, Skeleton, Stack, Tab, Tabs, Tooltip, useMediaQuery, useTheme } from '@mui/material';
5
5
  import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
6
6
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
7
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
8
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
9
9
  import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
10
+ import BundleButton from '@cccsaurora/howler-ui/components/elements/display/icons/BundleButton';
10
11
  import SocketBadge from '@cccsaurora/howler-ui/components/elements/display/icons/SocketBadge';
11
12
  import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
12
13
  import HitActions from '@cccsaurora/howler-ui/components/elements/hit/HitActions';
13
14
  import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
15
+ import HitComments from '@cccsaurora/howler-ui/components/elements/hit/HitComments';
16
+ import HitDetails from '@cccsaurora/howler-ui/components/elements/hit/HitDetails';
14
17
  import HitLabels from '@cccsaurora/howler-ui/components/elements/hit/HitLabels';
15
18
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
16
19
  import HitLinks from '@cccsaurora/howler-ui/components/elements/hit/HitLinks';
17
20
  import HitOutline from '@cccsaurora/howler-ui/components/elements/hit/HitOutline';
18
21
  import HitOverview from '@cccsaurora/howler-ui/components/elements/hit/HitOverview';
19
- import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
20
- import RecordComments from '@cccsaurora/howler-ui/components/elements/record/RecordComments';
21
- import RecordRelated from '@cccsaurora/howler-ui/components/elements/record/RecordRelated';
22
- import RecordWorklog from '@cccsaurora/howler-ui/components/elements/record/RecordWorklog';
22
+ import HitRelated from '@cccsaurora/howler-ui/components/elements/hit/HitRelated';
23
+ import HitWorklog from '@cccsaurora/howler-ui/components/elements/hit/HitWorklog';
23
24
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
24
25
  import useMyUserList from '@cccsaurora/howler-ui/components/hooks/useMyUserList';
25
26
  import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -43,8 +44,8 @@ const HitViewer = () => {
43
44
  const isUnderLg = useMediaQuery(theme.breakpoints.down('lg'));
44
45
  const [orientation, setOrientation] = useMyLocalStorageItem(StorageKey.VIEWER_ORIENTATION, Orientation.VERTICAL);
45
46
  const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
46
- const getHit = useContextSelector(RecordContext, ctx => ctx.getRecord);
47
- const hit = useContextSelector(RecordContext, ctx => ctx.records[params.id]);
47
+ const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
48
+ const hit = useContextSelector(HitContext, ctx => ctx.hits[params.id]);
48
49
  const [userIds, setUserIds] = useState(new Set());
49
50
  const users = useMyUserList(userIds);
50
51
  const [tab, setTab] = useState('details');
@@ -93,12 +94,12 @@ const HitViewer = () => {
93
94
  }
94
95
  return {
95
96
  overview: () => _jsx(HitOverview, { hit: hit }),
96
- details: () => _jsx(ObjectDetails, { obj: hit }),
97
- hit_comments: () => _jsx(RecordComments, { record: hit, users: users }),
97
+ details: () => _jsx(HitDetails, { hit: hit }),
98
+ hit_comments: () => _jsx(HitComments, { hit: hit, users: users }),
98
99
  hit_raw: () => _jsx(JSONViewer, { data: hit }),
99
100
  hit_data: () => _jsx(JSONViewer, { data: hit?.howler?.data?.map(entry => tryParse(entry)), collapse: false }),
100
- hit_worklog: () => _jsx(RecordWorklog, { record: hit, users: users }),
101
- hit_related: () => _jsx(RecordRelated, { record: hit }),
101
+ hit_worklog: () => _jsx(HitWorklog, { hit: hit, users: users }),
102
+ hit_related: () => _jsx(HitRelated, { hit: hit }),
102
103
  ...Object.fromEntries(hit?.howler.dossier?.map((lead, index) => ['lead:' + index, () => _jsx(LeadRenderer, { lead: lead, hit: hit })]) ?? []),
103
104
  ...Object.fromEntries(dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => [
104
105
  `external-lead:${dossierIndex}:${leadIndex}`,
@@ -131,7 +132,7 @@ const HitViewer = () => {
131
132
  position: 'absolute',
132
133
  top: theme.spacing(2),
133
134
  right: theme.spacing(-6)
134
- }, children: [_jsx(Tooltip, { title: t('hit.panel.view.layout'), children: _jsx(IconButton, { onClick: onOrientationChange, children: _jsx(ViewAgenda, { sx: { transition: 'rotate 250ms', rotate: orientation === 'vertical' ? '90deg' : '0deg' } }) }) }), _jsx(SocketBadge, { size: "medium" }), analytic && (_jsx(Tooltip, { title: t('analytic.open'), children: _jsx(IconButton, { onClick: () => navigate(`/analytics/${analytic.analytic_id}`), children: _jsx(QueryStats, {}) }) }))] }))] }), _jsx(HowlerCard, { sx: [orientation === 'horizontal' && { height: '0px' }], children: _jsx(CardContent, { sx: { padding: 1, position: 'relative' }, children: _jsx(HitActions, { hit: hit, orientation: "vertical" }) }) }), _jsx(Box, { sx: { gridColumn: '1 / span 2', mb: 1 }, children: _jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: { display: 'flex', flexDirection: 'row', pr: 2, alignItems: 'center' }, children: [hasOverview && (_jsx(Tab, { label: t('hit.viewer.overview'), value: "overview", onClick: () => setTab('overview') })), _jsx(Tab, { label: t('hit.viewer.details'), value: "details", onClick: () => setTab('details') }), hit?.howler.dossier?.map((lead, index) => (_jsx(Tab
135
+ }, children: [_jsx(Tooltip, { title: t('hit.panel.view.layout'), children: _jsx(IconButton, { onClick: onOrientationChange, children: _jsx(ViewAgenda, { sx: { transition: 'rotate 250ms', rotate: orientation === 'vertical' ? '90deg' : '0deg' } }) }) }), _jsx(SocketBadge, { size: "medium" }), analytic && (_jsx(Tooltip, { title: t('hit.panel.analytic.open'), children: _jsx(IconButton, { onClick: () => navigate(`/analytics/${analytic.analytic_id}`), children: _jsx(QueryStats, {}) }) })), hit?.howler.bundles?.length > 0 && _jsx(BundleButton, { ids: hit.howler.bundles })] }))] }), _jsx(HowlerCard, { sx: [orientation === 'horizontal' && { height: '0px' }], children: _jsx(CardContent, { sx: { padding: 1, position: 'relative' }, children: _jsx(HitActions, { hit: hit, orientation: "vertical" }) }) }), _jsx(Box, { sx: { gridColumn: '1 / span 2', mb: 1 }, children: _jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: { display: 'flex', flexDirection: 'row', pr: 2, alignItems: 'center' }, children: [hit?.howler?.is_bundle && (_jsx(Tab, { label: t('hit.viewer.aggregate'), value: "hit_aggregate", onClick: () => setTab('hit_aggregate') })), hasOverview && (_jsx(Tab, { label: t('hit.viewer.overview'), value: "overview", onClick: () => setTab('overview') })), _jsx(Tab, { label: t('hit.viewer.details'), value: "details", onClick: () => setTab('details') }), hit?.howler.dossier?.map((lead, index) => (_jsx(Tab
135
136
  // eslint-disable-next-line react/no-array-index-key
136
137
  , { label: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [lead.icon && _jsx(Icon, { icon: lead.icon }), _jsx("span", { children: i18n.language === 'en' ? lead.label.en : lead.label.fr })] }), value: 'lead:' + index, onClick: () => setTab('lead:' + index) }, 'lead:' + index))), dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => (_jsx(Tab
137
138
  // eslint-disable-next-line react/no-array-index-key
@@ -3,80 +3,74 @@ import { OpenInNew } from '@mui/icons-material';
3
3
  import { Card, CardContent, IconButton, Skeleton, Stack, Typography } from '@mui/material';
4
4
  import api from '@cccsaurora/howler-ui/api';
5
5
  import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
6
- import { useHitContextSelector as useRecordContextSelector } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
6
+ import { useHitContextSelector } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
7
7
  import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
8
8
  import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
9
9
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
10
- import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
11
- import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
12
10
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
11
+ import HitContextMenu from '@cccsaurora/howler-ui/components/routes/hits/search/HitContextMenu';
13
12
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
14
13
  import { useTranslation } from 'react-i18next';
15
14
  import { Link, useNavigate } from 'react-router-dom';
16
15
  import { useContextSelector } from 'use-context-selector';
17
- import { isObservable } from '@cccsaurora/howler-ui/utils/typeUtils';
18
16
  import { buildViewUrl } from '@cccsaurora/howler-ui/utils/viewUtils';
19
- // Custom hook to select records by IDs with proper memoization
20
- const useSelectRecordsByIds = (recordIds) => {
21
- const recordIdsRef = useRef(recordIds);
17
+ // Custom hook to select hits by IDs with proper memoization
18
+ const useSelectHitsByIds = (hitIds) => {
19
+ const hitIdsRef = useRef(hitIds);
22
20
  const prevResultRef = useRef([]);
23
- const prevRecordIdsRef = useRef([]);
24
- // Keep ref up to date with latest recordIds
25
- recordIdsRef.current = recordIds;
21
+ const prevHitIdsRef = useRef([]);
22
+ // Keep ref up to date with latest hitIds
23
+ hitIdsRef.current = hitIds;
26
24
  const selector = useCallback(ctx => {
27
- const currentRecordIds = recordIdsRef.current;
28
- // Fast path: if recordIds array didn't change, check if record objects changed
29
- if (prevRecordIdsRef.current.length === currentRecordIds.length &&
30
- currentRecordIds.every((id, i) => id === prevRecordIdsRef.current[i])) {
31
- // RecordIds unchanged - check if any record objects changed by reference
32
- const anyRecordChanged = currentRecordIds.some((id, i) => ctx.records[id] !== prevResultRef.current[i]);
33
- if (!anyRecordChanged) {
25
+ const currentHitIds = hitIdsRef.current;
26
+ // Fast path: if hitIds array didn't change, check if hit objects changed
27
+ if (prevHitIdsRef.current.length === currentHitIds.length &&
28
+ currentHitIds.every((id, i) => id === prevHitIdsRef.current[i])) {
29
+ // HitIds unchanged - check if any hit objects changed by reference
30
+ const anyHitChanged = currentHitIds.some((id, i) => ctx.hits[id] !== prevResultRef.current[i]);
31
+ if (!anyHitChanged) {
34
32
  return prevResultRef.current;
35
33
  }
36
34
  }
37
35
  // Something changed - rebuild the array
38
- const currentRecords = currentRecordIds.map(id => ctx.records[id]).filter(Boolean);
39
- prevRecordIdsRef.current = currentRecordIds;
40
- prevResultRef.current = currentRecords;
41
- return currentRecords;
36
+ const currentHits = currentHitIds.map(id => ctx.hits[id]).filter(Boolean);
37
+ prevHitIdsRef.current = currentHitIds;
38
+ prevResultRef.current = currentHits;
39
+ return currentHits;
42
40
  }, []); // Empty deps - selector never changes
43
- return useRecordContextSelector(selector);
41
+ return useHitContextSelector(selector);
44
42
  };
45
43
  // Utility functions
46
44
  const normalize = (val) => (val == null ? '' : String(val));
47
45
  // Have to normalize the fields as websockets and api return null and undefined respectively. This causes false positives when comparing signatures if not normalized to a consistent value. We also stringify non-primitive values to ensure changes are detected.
48
- const createRecordSignature = (record) => {
49
- if (!record) {
46
+ const createHitSignature = (hit) => {
47
+ if (!hit)
50
48
  return '';
51
- }
52
- if (isObservable(record)) {
53
- return record.howler?.id;
54
- }
55
- return `${record.howler?.id}:${normalize(record.howler?.status)}:${normalize(record.howler?.assignment)}:${normalize(record.howler?.assessment)}`;
49
+ return `${hit.howler?.id}:${normalize(hit.howler?.status)}:${normalize(hit.howler?.assignment)}:${normalize(hit.howler?.assessment)}`;
56
50
  };
57
- const createSignatureFromRecords = (records) => {
58
- if (records.length === 0)
51
+ const createSignatureFromHits = (hits) => {
52
+ if (hits.length === 0)
59
53
  return '';
60
- return records.map(createRecordSignature).join('|');
54
+ return hits.map(createHitSignature).join('|');
61
55
  };
62
56
  const DEBOUNCE_TIME = 1000; // 1 second debounce for signature changes
63
57
  const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
64
58
  const navigate = useNavigate();
65
59
  const { t } = useTranslation();
66
60
  const { dispatchApi } = useMyApi();
67
- const [recordIds, setRecordIds] = useState([]);
61
+ const [hitIds, setHitIds] = useState([]);
68
62
  const [loading, setLoading] = useState(false);
69
63
  const debounceTimerRef = useRef(null);
70
64
  const isRefreshing = useRef(false);
71
65
  const lastSignature = useRef('');
72
66
  const view = useContextSelector(ViewContext, ctx => ctx.views[viewId]);
73
67
  const fetchViews = useContextSelector(ViewContext, ctx => ctx.fetchViews);
74
- const loadRecords = useRecordContextSelector(ctx => ctx.loadRecords);
68
+ const loadHits = useHitContextSelector(ctx => ctx.loadHits);
75
69
  // Subscribe to hits from HitProvider cache based on current hitIds in the view
76
70
  // Uses memoized selector to avoid unnecessary re-renders on unrelated hit updates
77
- const records = useSelectRecordsByIds(recordIds);
71
+ const hits = useSelectHitsByIds(hitIds);
78
72
  // Create a stable signature that only changes when relevant fields change
79
- const recordsSignature = useMemo(() => createSignatureFromRecords(records), [records]);
73
+ const hitsSignature = useMemo(() => createSignatureFromHits(hits), [hits]);
80
74
  const refreshView = useCallback(async () => {
81
75
  if (!view?.query || isRefreshing.current) {
82
76
  onRefreshComplete?.();
@@ -84,21 +78,21 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
84
78
  }
85
79
  isRefreshing.current = true;
86
80
  try {
87
- const res = await dispatchApi(api.v2.search.post(view.indexes, {
81
+ const res = await dispatchApi(api.search.hit.post({
88
82
  query: view.query,
89
83
  rows: limit,
90
84
  metadata: ['analytic']
91
85
  }));
92
- const fetchedRecords = res.items ?? [];
93
- loadRecords(fetchedRecords);
94
- setRecordIds(fetchedRecords.map(r => r.howler.id));
95
- lastSignature.current = createSignatureFromRecords(fetchedRecords);
86
+ const fetchedHits = res.items ?? [];
87
+ loadHits(fetchedHits);
88
+ setHitIds(fetchedHits.map(h => h.howler.id));
89
+ lastSignature.current = createSignatureFromHits(fetchedHits);
96
90
  }
97
91
  finally {
98
92
  isRefreshing.current = false;
99
93
  onRefreshComplete?.();
100
94
  }
101
- }, [dispatchApi, limit, view?.query, view?.indexes, loadRecords, onRefreshComplete]);
95
+ }, [dispatchApi, limit, view?.query, loadHits, onRefreshComplete]);
102
96
  const debouncedRefresh = useCallback(() => {
103
97
  if (debounceTimerRef.current) {
104
98
  clearTimeout(debounceTimerRef.current);
@@ -131,16 +125,16 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
131
125
  }, [view?.query, limit, refreshView]);
132
126
  // Monitor hits currently in the view for changes that might affect query results
133
127
  useEffect(() => {
134
- if (!recordsSignature || recordIds.length === 0 || !lastSignature.current) {
135
- lastSignature.current = recordsSignature;
128
+ if (!hitsSignature || hitIds.length === 0 || !lastSignature.current) {
129
+ lastSignature.current = hitsSignature;
136
130
  return;
137
131
  }
138
132
  // Check if signature actually changed
139
- if (lastSignature.current === recordsSignature) {
133
+ if (lastSignature.current === hitsSignature) {
140
134
  return;
141
135
  }
142
136
  debouncedRefresh();
143
- }, [recordsSignature, recordIds, debouncedRefresh]);
137
+ }, [hitsSignature, hitIds, debouncedRefresh]);
144
138
  useEffect(() => {
145
139
  return () => {
146
140
  if (debounceTimerRef.current) {
@@ -157,6 +151,6 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
157
151
  }
158
152
  return selectedElement.id;
159
153
  }, []);
160
- return (_jsx(Card, { variant: "outlined", sx: { height: '100%' }, children: _jsxs(Stack, { spacing: 1, sx: { p: 1, minHeight: 100 }, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h6", children: t(view?.title) || _jsx(Skeleton, { variant: "text", height: "2em", width: "100px" }) }), _jsx(IconButton, { size: "small", component: Link, disabled: !view, to: view ? buildViewUrl(view) : '', onClick: () => onClick(view.query), children: _jsx(OpenInNew, { fontSize: "small" }) })] }), loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { height: 150, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 160, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 140, width: "100%", variant: "rounded" })] })) : records.length > 0 ? (_jsx(RecordContextMenu, { getSelectedId: getSelectedId, children: records.map((r) => (_jsx(Card, { id: r.howler.id, variant: "outlined", sx: { cursor: 'pointer' }, onClick: () => navigate(`/hits/${r.howler.id}`), children: _jsx(CardContent, { children: r.__index == 'hit' ? (_jsx(HitBanner, { layout: HitLayout.DENSE, hit: r })) : (_jsx(ObservableCard, { observable: r })) }) }, r.howler.id))) })) : (_jsx(AppListEmpty, {}))] }) }));
154
+ return (_jsx(Card, { variant: "outlined", sx: { height: '100%' }, children: _jsxs(Stack, { spacing: 1, sx: { p: 1, minHeight: 100 }, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h6", children: t(view?.title) || _jsx(Skeleton, { variant: "text", height: "2em", width: "100px" }) }), _jsx(IconButton, { size: "small", component: Link, disabled: !view, to: view ? buildViewUrl(view) : '', onClick: () => onClick(view.query), children: _jsx(OpenInNew, { fontSize: "small" }) })] }), loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { height: 150, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 160, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 140, width: "100%", variant: "rounded" })] })) : hits.length > 0 ? (_jsx(HitContextMenu, { getSelectedId: getSelectedId, children: hits.map(h => (_jsx(Card, { id: h.howler.id, variant: "outlined", sx: { cursor: 'pointer' }, onClick: () => navigate((h.howler.is_bundle ? '/bundles/' : '/hits/') + h.howler.id), children: _jsx(CardContent, { children: _jsx(HitBanner, { layout: HitLayout.DENSE, hit: h }) }) }, h.howler.id))) })) : (_jsx(AppListEmpty, {}))] }) }));
161
155
  };
162
156
  export default ViewCard;
@@ -4,8 +4,8 @@ import { useTheme } from '@mui/material';
4
4
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
5
5
  import ThemedEditor from '@cccsaurora/howler-ui/components/elements/ThemedEditor';
6
6
  import { memo, useCallback, useContext, useEffect, useMemo } from 'react';
7
- import { conf, language } from '../routes/overviews/markdownExtendedTokenProvider';
8
- const MarkdownEditor = ({ content, setContent, onMount, fontSize = 16, height = '100%', width = '100%', editorOptions = {} }) => {
7
+ import { conf, language } from './markdownExtendedTokenProvider';
8
+ const OverviewEditor = ({ content, setContent, onMount, fontSize = 16, height = '100%', width = '100%', editorOptions = {} }) => {
9
9
  const theme = useTheme();
10
10
  const monaco = useMonaco();
11
11
  const { config } = useContext(ApiConfigContext);
@@ -53,4 +53,4 @@ const MarkdownEditor = ({ content, setContent, onMount, fontSize = 16, height =
53
53
  }), [fontSize, editorOptions]);
54
54
  return (_jsx(ThemedEditor, { height: height, width: width, theme: theme.palette.mode === 'light' ? 'howler' : 'howler-dark', value: content, onChange: value => setContent(value), beforeMount: beforeEditorMount, onMount: onMount, options: options }));
55
55
  };
56
- export default memo(MarkdownEditor);
56
+ export default memo(OverviewEditor);
@@ -15,7 +15,7 @@ import useMyTheme from '@cccsaurora/howler-ui/components/hooks/useMyTheme';
15
15
  import { useSearchParams } from 'react-router-dom';
16
16
  import hitsData from '@cccsaurora/howler-ui/utils/hit.json';
17
17
  import { sanitizeLuceneQuery } from '@cccsaurora/howler-ui/utils/stringUtils';
18
- import MarkdownEditor from '../../elements/MarkdownEditor';
18
+ import OverviewEditor from './OverviewEditor';
19
19
  import { useStartingTemplate } from './startingTemplate';
20
20
  const OverviewViewer = () => {
21
21
  const theme = useTheme();
@@ -188,7 +188,7 @@ const OverviewViewer = () => {
188
188
  right: `calc(50% + 7px - ${x}px)`,
189
189
  mr: -2.4,
190
190
  pr: 1.5
191
- }, children: _jsx(MarkdownEditor, { height: "100%", content: content, setContent: setContent }) }) }), _jsx(Box, { sx: {
191
+ }, children: _jsx(OverviewEditor, { height: "100%", content: content, setContent: setContent }) }) }), _jsx(Box, { sx: {
192
192
  position: 'absolute',
193
193
  top: 0,
194
194
  bottom: 0,