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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/api/index.d.ts +0 -2
  2. package/api/index.js +2 -4
  3. package/api/search/index.d.ts +1 -2
  4. package/api/search/index.js +1 -2
  5. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  6. package/components/app/App.js +7 -34
  7. package/components/app/hooks/useMatchers.js +2 -2
  8. package/components/app/hooks/useMatchers.test.js +22 -22
  9. package/components/app/hooks/useTitle.js +3 -3
  10. package/components/app/providers/FavouritesProvider.js +2 -2
  11. package/components/app/providers/HitProvider.d.ts +22 -0
  12. package/components/app/providers/{RecordProvider.js → HitProvider.js} +41 -41
  13. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  14. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  15. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +70 -51
  16. package/components/app/providers/ParameterProvider.d.ts +2 -9
  17. package/components/app/providers/ParameterProvider.js +240 -165
  18. package/components/app/providers/ParameterProvider.test.js +14 -307
  19. package/components/elements/PluginTypography.d.ts +1 -2
  20. package/components/elements/PluginTypography.js +2 -3
  21. package/components/elements/UserList.d.ts +2 -5
  22. package/components/elements/UserList.js +5 -14
  23. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  24. package/components/elements/display/ChipPopper.d.ts +1 -1
  25. package/components/elements/display/HowlerCard.js +1 -1
  26. package/components/elements/display/Modal.js +0 -1
  27. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  28. package/components/elements/display/icons/BundleButton.js +32 -0
  29. package/components/elements/hit/HitActions.js +4 -4
  30. package/components/elements/hit/HitBanner.js +48 -28
  31. package/components/elements/hit/HitCard.js +5 -5
  32. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  33. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  34. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  35. package/components/elements/hit/HitLabels.js +2 -2
  36. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  37. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  38. package/components/elements/hit/HitRelated.d.ts +6 -0
  39. package/components/elements/hit/HitRelated.js +7 -0
  40. package/components/elements/hit/HitSummary.d.ts +1 -2
  41. package/components/elements/hit/HitSummary.js +5 -6
  42. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  43. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  44. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  45. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  46. package/components/elements/view/ViewTitle.js +1 -1
  47. package/components/hooks/useHitActions.d.ts +1 -1
  48. package/components/hooks/useHitActions.js +4 -4
  49. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  50. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  51. package/components/hooks/useMyPreferences.js +1 -10
  52. package/components/hooks/useMySearch.js +2 -2
  53. package/components/hooks/useMySitemap.js +1 -4
  54. package/components/hooks/useMyTheme.js +2 -9
  55. package/components/hooks/useParamState.test.js +4 -3
  56. package/components/routes/action/edit/ActionEditor.js +2 -2
  57. package/components/routes/action/view/ActionSearch.js +1 -1
  58. package/components/routes/advanced/QueryBuilder.js +1 -1
  59. package/components/routes/advanced/QueryEditor.js +3 -3
  60. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  61. package/components/routes/analytics/AnalyticDetails.js +2 -2
  62. package/components/routes/analytics/AnalyticSearch.js +1 -1
  63. package/components/routes/dossiers/DossierEditor.js +2 -2
  64. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  65. package/components/routes/help/ApiDocumentation.js +1 -1
  66. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  67. package/components/routes/help/BundleDocumentation.js +12 -0
  68. package/components/routes/help/HitBannerDocumentation.js +0 -1
  69. package/components/routes/help/HitDocumentation.js +3 -1
  70. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  71. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  72. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  73. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  74. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  75. package/components/routes/hits/search/BundleScroller.js +6 -0
  76. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  77. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  78. package/components/routes/hits/search/HitContextMenu.js +227 -0
  79. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +39 -39
  80. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  81. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  82. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  83. package/components/routes/hits/search/InformationPane.js +60 -47
  84. package/components/routes/hits/search/LayoutSettings.js +3 -3
  85. package/components/routes/hits/search/QuerySettings.js +1 -2
  86. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  87. package/components/routes/hits/search/SearchPane.js +49 -26
  88. package/components/routes/hits/search/ViewLink.js +3 -3
  89. package/components/routes/hits/search/ViewLink.test.js +8 -8
  90. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  91. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  92. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  93. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  94. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  95. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +8 -10
  96. package/components/routes/hits/view/HitViewer.js +13 -12
  97. package/components/routes/home/ViewCard.js +4 -4
  98. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  99. package/components/routes/overviews/OverviewViewer.js +2 -2
  100. package/components/routes/views/ViewComposer.js +4 -4
  101. package/locales/en/translation.json +3 -65
  102. package/locales/fr/translation.json +3 -63
  103. package/models/WithMetadata.d.ts +1 -2
  104. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  105. package/models/entities/generated/Hit.d.ts +0 -1
  106. package/models/entities/generated/Howler.d.ts +4 -0
  107. package/models/entities/generated/Rule.d.ts +10 -2
  108. package/models/entities/generated/Threat.d.ts +2 -2
  109. package/package.json +106 -123
  110. package/plugins/clue/components/ClueTypography.js +2 -2
  111. package/plugins/clue/utils.d.ts +1 -2
  112. package/tests/utils.d.ts +0 -2
  113. package/tests/utils.js +0 -8
  114. package/utils/constants.d.ts +3 -3
  115. package/utils/hitFunctions.d.ts +1 -2
  116. package/utils/hitFunctions.js +4 -4
  117. package/api/search/case.d.ts +0 -4
  118. package/api/search/case.js +0 -8
  119. package/api/v2/case/index.d.ts +0 -6
  120. package/api/v2/case/index.js +0 -18
  121. package/api/v2/index.d.ts +0 -4
  122. package/api/v2/index.js +0 -6
  123. package/api/v2/search/facet.d.ts +0 -3
  124. package/api/v2/search/facet.js +0 -12
  125. package/api/v2/search/index.d.ts +0 -5
  126. package/api/v2/search/index.js +0 -24
  127. package/components/app/providers/RecordProvider.d.ts +0 -23
  128. package/components/elements/ContextMenu.d.ts +0 -56
  129. package/components/elements/ContextMenu.js +0 -109
  130. package/components/elements/ContextMenu.test.js +0 -215
  131. package/components/elements/ObjectDetails.d.ts +0 -6
  132. package/components/elements/case/CaseCard.d.ts +0 -8
  133. package/components/elements/case/CaseCard.js +0 -39
  134. package/components/elements/case/CasePreview.d.ts +0 -6
  135. package/components/elements/case/CasePreview.js +0 -17
  136. package/components/elements/case/StatusIcon.d.ts +0 -5
  137. package/components/elements/case/StatusIcon.js +0 -13
  138. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
  139. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  140. package/components/elements/hit/related/RelatedRecords.js +0 -63
  141. package/components/elements/observable/ObservableCard.d.ts +0 -6
  142. package/components/elements/observable/ObservableCard.js +0 -23
  143. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  144. package/components/elements/observable/ObservablePreview.js +0 -12
  145. package/components/elements/record/RecordContextMenu.js +0 -235
  146. package/components/elements/record/RecordContextMenu.test.d.ts +0 -1
  147. package/components/elements/record/RecordRelated.d.ts +0 -7
  148. package/components/elements/record/RecordRelated.js +0 -34
  149. package/components/hooks/useRelatedRecords.d.ts +0 -13
  150. package/components/hooks/useRelatedRecords.js +0 -32
  151. package/components/routes/cases/CaseViewer.d.ts +0 -2
  152. package/components/routes/cases/CaseViewer.js +0 -22
  153. package/components/routes/cases/Cases.d.ts +0 -2
  154. package/components/routes/cases/Cases.js +0 -101
  155. package/components/routes/cases/constants.d.ts +0 -5
  156. package/components/routes/cases/constants.js +0 -5
  157. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  158. package/components/routes/cases/detail/AlertPanel.js +0 -33
  159. package/components/routes/cases/detail/CaseAssets.d.ts +0 -12
  160. package/components/routes/cases/detail/CaseAssets.js +0 -101
  161. package/components/routes/cases/detail/CaseAssets.test.d.ts +0 -1
  162. package/components/routes/cases/detail/CaseAssets.test.js +0 -163
  163. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  164. package/components/routes/cases/detail/CaseDashboard.js +0 -51
  165. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  166. package/components/routes/cases/detail/CaseDetails.js +0 -61
  167. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  168. package/components/routes/cases/detail/CaseOverview.js +0 -43
  169. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -6
  170. package/components/routes/cases/detail/CaseSidebar.js +0 -61
  171. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  172. package/components/routes/cases/detail/CaseTask.js +0 -57
  173. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  174. package/components/routes/cases/detail/ItemPage.js +0 -99
  175. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  176. package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
  177. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  178. package/components/routes/cases/detail/TaskPanel.js +0 -52
  179. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
  180. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
  181. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  182. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -27
  183. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  184. package/components/routes/cases/detail/assets/Asset.js +0 -12
  185. package/components/routes/cases/detail/assets/Asset.test.d.ts +0 -1
  186. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  187. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -13
  188. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -131
  189. package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
  190. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  191. package/components/routes/cases/detail/sidebar/utils.js +0 -25
  192. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  193. package/components/routes/cases/hooks/useCase.js +0 -38
  194. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  195. package/components/routes/cases/modals/ResolveModal.js +0 -59
  196. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  197. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  198. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  199. package/components/routes/observables/ObservableViewer.js +0 -27
  200. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  201. package/models/entities/generated/Case.d.ts +0 -28
  202. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  203. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  204. package/models/entities/generated/EmailParent.d.ts +0 -19
  205. package/models/entities/generated/Enrichments.d.ts +0 -7
  206. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  207. package/models/entities/generated/HttpResponse.d.ts +0 -11
  208. package/models/entities/generated/Item.d.ts +0 -9
  209. package/models/entities/generated/Observable.d.ts +0 -85
  210. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  211. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  212. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  213. package/models/entities/generated/ObservableFile.d.ts +0 -36
  214. package/models/entities/generated/ObservableHowler.d.ts +0 -43
  215. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  216. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  217. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  218. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  219. package/models/entities/generated/ObservableSource.d.ts +0 -23
  220. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  221. package/models/entities/generated/ObservableTls.d.ts +0 -12
  222. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  223. package/models/entities/generated/Task.d.ts +0 -10
  224. package/utils/typeUtils.d.ts +0 -7
  225. package/utils/typeUtils.js +0 -27
  226. /package/components/app/providers/{RecordSearchProvider.test.d.ts → HitSearchProvider.test.d.ts} +0 -0
  227. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  228. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  229. /package/components/{elements/ContextMenu.test.d.ts → routes/hits/search/HitContextMenu.test.d.ts} +0 -0
  230. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -91,13 +91,13 @@ vi.mock('@mui/material', async () => {
91
91
  });
92
92
  // Import component after mocks
93
93
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
94
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
94
95
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
95
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
96
96
  import i18n from '@cccsaurora/howler-ui/i18n';
97
97
  import { I18nextProvider } from 'react-i18next';
98
98
  import { createMockAction, createMockAnalytic, createMockHit, createMockTemplate } from '@cccsaurora/howler-ui/tests/utils';
99
99
  import { DEFAULT_QUERY } from '@cccsaurora/howler-ui/utils/constants';
100
- import RecordContextMenu from './RecordContextMenu';
100
+ import HitContextMenu from './HitContextMenu';
101
101
  const mockGetSelectedId = vi.fn(() => 'test-hit-1');
102
102
  const mockConfig = {
103
103
  lookups: {
@@ -105,16 +105,16 @@ const mockConfig = {
105
105
  }
106
106
  };
107
107
  const mockApiContext = { config: mockConfig };
108
- const mockRecordContext = {
109
- records: {
108
+ const mockHitContext = {
109
+ hits: {
110
110
  'test-hit-1': createMockHit()
111
111
  },
112
- selectedRecords: []
112
+ selectedHits: []
113
113
  };
114
114
  const mockParameterContext = { query: DEFAULT_QUERY, setQuery: vi.fn() };
115
115
  // Test wrapper
116
116
  const Wrapper = ({ children }) => {
117
- return (_jsx(I18nextProvider, { i18n: i18n, children: _jsx(ApiConfigContext.Provider, { value: mockApiContext, children: _jsx(RecordContext.Provider, { value: mockRecordContext, children: _jsx(ParameterContext.Provider, { value: mockParameterContext, children: children }) }) }) }));
117
+ return (_jsx(I18nextProvider, { i18n: i18n, children: _jsx(ApiConfigContext.Provider, { value: mockApiContext, children: _jsx(HitContext.Provider, { value: mockHitContext, children: _jsx(ParameterContext.Provider, { value: mockParameterContext, children: children }) }) }) }));
118
118
  };
119
119
  describe('HitContextMenu', () => {
120
120
  let user;
@@ -122,11 +122,11 @@ describe('HitContextMenu', () => {
122
122
  beforeEach(() => {
123
123
  user = userEvent.setup();
124
124
  vi.clearAllMocks();
125
- mockRecordContext.selectedRecords.length = 0;
126
- mockRecordContext.records['test-hit-1'] = createMockHit();
125
+ mockHitContext.selectedHits.length = 0;
126
+ mockHitContext.hits['test-hit-1'] = createMockHit();
127
127
  mockGetMatchingAnalytic.mockResolvedValue(createMockAnalytic());
128
128
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate());
129
- rerender = render(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) })).rerender;
129
+ rerender = render(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) })).rerender;
130
130
  });
131
131
  describe('Context Menu Initialization', () => {
132
132
  it('should open menu on right-click', async () => {
@@ -190,13 +190,13 @@ describe('HitContextMenu', () => {
190
190
  });
191
191
  it('should disable "Open Hit" when hit is null', async () => {
192
192
  act(() => {
193
- mockRecordContext.records['test-hit-1'] = null;
193
+ mockHitContext.hits['test-hit-1'] = null;
194
194
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
195
195
  fireEvent.contextMenu(contextMenuWrapper);
196
196
  });
197
197
  await waitFor(() => {
198
198
  const menuItems = screen.getAllByRole('menuitem');
199
- const openHitItem = menuItems.find(item => item.textContent?.toLowerCase().includes('open hit'));
199
+ const openHitItem = menuItems.find(item => item.textContent?.toLowerCase().includes('open hit viewer'));
200
200
  expect(openHitItem).toHaveAttribute('aria-disabled', 'true');
201
201
  });
202
202
  });
@@ -237,7 +237,7 @@ describe('HitContextMenu', () => {
237
237
  skip_rationale: false
238
238
  }
239
239
  }));
240
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
240
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
241
241
  act(() => {
242
242
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
243
243
  fireEvent.contextMenu(contextMenuWrapper);
@@ -295,7 +295,7 @@ describe('HitContextMenu', () => {
295
295
  createMockAction({ action_id: 'action-2', name: 'Custom Action 2' })
296
296
  ];
297
297
  mockDispatchApi.mockResolvedValue({ items: mockActions });
298
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
298
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
299
299
  act(() => {
300
300
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
301
301
  fireEvent.contextMenu(contextMenuWrapper);
@@ -339,7 +339,7 @@ describe('HitContextMenu', () => {
339
339
  });
340
340
  it('should disable custom actions menu when no actions are available', async () => {
341
341
  mockDispatchApi.mockResolvedValueOnce({ items: [] });
342
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
342
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
343
343
  act(() => {
344
344
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
345
345
  fireEvent.contextMenu(contextMenuWrapper);
@@ -381,7 +381,7 @@ describe('HitContextMenu', () => {
381
381
  skip_rationale: true
382
382
  }
383
383
  }));
384
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
384
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
385
385
  act(() => {
386
386
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
387
387
  fireEvent.contextMenu(contextMenuWrapper);
@@ -449,7 +449,7 @@ describe('HitContextMenu', () => {
449
449
  it('should call executeAction with action_id and hit query', async () => {
450
450
  const mockActions = [createMockAction({ action_id: 'action-1', name: 'Custom Action' })];
451
451
  mockDispatchApi.mockResolvedValue({ items: mockActions });
452
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
452
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
453
453
  act(() => {
454
454
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
455
455
  fireEvent.contextMenu(contextMenuWrapper);
@@ -502,7 +502,7 @@ describe('HitContextMenu', () => {
502
502
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
503
503
  keys: ['howler.detection', 'event.id']
504
504
  }));
505
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
505
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
506
506
  });
507
507
  it('should render exclusion submenu with template keys', async () => {
508
508
  act(() => {
@@ -550,7 +550,7 @@ describe('HitContextMenu', () => {
550
550
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
551
551
  keys: ['howler.outline.indicators']
552
552
  }));
553
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
553
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
554
554
  act(() => {
555
555
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
556
556
  fireEvent.contextMenu(contextMenuWrapper);
@@ -593,7 +593,7 @@ describe('HitContextMenu', () => {
593
593
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
594
594
  keys: []
595
595
  }));
596
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
596
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
597
597
  act(() => {
598
598
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
599
599
  fireEvent.contextMenu(contextMenuWrapper);
@@ -605,7 +605,7 @@ describe('HitContextMenu', () => {
605
605
  });
606
606
  it('should skip null field values in exclusion menu', async () => {
607
607
  act(() => {
608
- mockRecordContext.records['test-hit-1'].event = {};
608
+ mockHitContext.hits['test-hit-1'].event = {};
609
609
  });
610
610
  act(() => {
611
611
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -628,7 +628,7 @@ describe('HitContextMenu', () => {
628
628
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
629
629
  keys: ['howler.detection', 'event.id']
630
630
  }));
631
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
631
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
632
632
  });
633
633
  it('should render inclusion submenu with template keys', async () => {
634
634
  act(() => {
@@ -676,7 +676,7 @@ describe('HitContextMenu', () => {
676
676
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
677
677
  keys: ['howler.outline.indicators']
678
678
  }));
679
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
679
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
680
680
  act(() => {
681
681
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
682
682
  fireEvent.contextMenu(contextMenuWrapper);
@@ -719,7 +719,7 @@ describe('HitContextMenu', () => {
719
719
  mockGetMatchingTemplate.mockResolvedValue(createMockTemplate({
720
720
  keys: []
721
721
  }));
722
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
722
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
723
723
  act(() => {
724
724
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
725
725
  fireEvent.contextMenu(contextMenuWrapper);
@@ -731,7 +731,7 @@ describe('HitContextMenu', () => {
731
731
  });
732
732
  it('should skip null field values in inclusion menu', async () => {
733
733
  act(() => {
734
- mockRecordContext.records['test-hit-1'].event = {};
734
+ mockHitContext.hits['test-hit-1'].event = {};
735
735
  });
736
736
  act(() => {
737
737
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -750,24 +750,24 @@ describe('HitContextMenu', () => {
750
750
  });
751
751
  });
752
752
  describe('Multiple Hit Selection', () => {
753
- it('should use selectedRecords when current hit is included', async () => {
753
+ it('should use selectedHits when current hit is included', async () => {
754
754
  act(() => {
755
- mockRecordContext.records['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
756
- mockRecordContext.records['hit-2'] = createMockHit({ howler: { id: 'hit-2' } });
757
- mockRecordContext.selectedRecords.push(mockRecordContext.records['hit-1'], mockRecordContext.records['hit-2']);
755
+ mockHitContext.hits['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
756
+ mockHitContext.hits['hit-2'] = createMockHit({ howler: { id: 'hit-2' } });
757
+ mockHitContext.selectedHits.push(mockHitContext.hits['hit-1'], mockHitContext.hits['hit-2']);
758
758
  mockGetSelectedId.mockReturnValue('hit-1');
759
759
  });
760
760
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
761
761
  await user.pointer({ keys: '[MouseRight]', target: contextMenuWrapper });
762
- // The component should use selectedRecords for actions
762
+ // The component should use selectedHits for actions
763
763
  // We can verify this indirectly through the useHitActions hook receiving the right data
764
764
  expect(screen.getByRole('menu')).toBeInTheDocument();
765
765
  expect(mockGetSelectedId).toHaveBeenCalled();
766
766
  });
767
- it('should use only current hit when not in selectedRecords', async () => {
767
+ it('should use only current hit when not in selectedHits', async () => {
768
768
  act(() => {
769
- mockRecordContext.records['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
770
- mockRecordContext.selectedRecords.push(mockRecordContext.records['hit-1']);
769
+ mockHitContext.hits['hit-1'] = createMockHit({ howler: { id: 'hit-1' } });
770
+ mockHitContext.selectedHits.push(mockHitContext.hits['hit-1']);
771
771
  mockGetSelectedId.mockReturnValue('test-hit-1');
772
772
  });
773
773
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
@@ -786,12 +786,12 @@ describe('HitContextMenu', () => {
786
786
  });
787
787
  it('should call getMatchingAnalytic when hit has analytic', async () => {
788
788
  await waitFor(() => {
789
- expect(mockGetMatchingAnalytic).toHaveBeenCalledWith(mockRecordContext.records['test-hit-1']);
789
+ expect(mockGetMatchingAnalytic).toHaveBeenCalledWith(mockHitContext.hits['test-hit-1']);
790
790
  });
791
791
  });
792
792
  it('should call getMatchingTemplate when menu opens', async () => {
793
793
  await waitFor(() => {
794
- expect(mockGetMatchingTemplate).toHaveBeenCalledWith(mockRecordContext.records['test-hit-1']);
794
+ expect(mockGetMatchingTemplate).toHaveBeenCalledWith(mockHitContext.hits['test-hit-1']);
795
795
  });
796
796
  });
797
797
  it('should reset state when menu closes', async () => {
@@ -824,7 +824,7 @@ describe('HitContextMenu', () => {
824
824
  describe('Edge Cases and Error Handling', () => {
825
825
  it('should not crash when hit is null', async () => {
826
826
  act(() => {
827
- mockRecordContext.records = {};
827
+ mockHitContext.hits = {};
828
828
  });
829
829
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
830
830
  fireEvent.contextMenu(contextMenuWrapper);
@@ -835,7 +835,7 @@ describe('HitContextMenu', () => {
835
835
  });
836
836
  it('should not render exclusion menu when template is null', async () => {
837
837
  mockGetMatchingTemplate.mockResolvedValue(null);
838
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
838
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
839
839
  act(() => {
840
840
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
841
841
  fireEvent.contextMenu(contextMenuWrapper);
@@ -849,7 +849,7 @@ describe('HitContextMenu', () => {
849
849
  });
850
850
  it('should not render inclusion menu when template is null', async () => {
851
851
  mockGetMatchingTemplate.mockResolvedValue(null);
852
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
852
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
853
853
  act(() => {
854
854
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
855
855
  fireEvent.contextMenu(contextMenuWrapper);
@@ -863,7 +863,7 @@ describe('HitContextMenu', () => {
863
863
  });
864
864
  it('should handle API failure gracefully', async () => {
865
865
  mockDispatchApi.mockResolvedValue(null);
866
- rerender(_jsx(Wrapper, { children: _jsx(RecordContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
866
+ rerender(_jsx(Wrapper, { children: _jsx(HitContextMenu, { getSelectedId: mockGetSelectedId, children: _jsx("div", { children: "Test Content" }) }) }));
867
867
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
868
868
  fireEvent.contextMenu(contextMenuWrapper);
869
869
  await waitFor(() => {
@@ -874,7 +874,7 @@ describe('HitContextMenu', () => {
874
874
  });
875
875
  it('should not call getMatchingAnalytic or getMatchingTemplate when hit has no analytic', async () => {
876
876
  act(() => {
877
- mockRecordContext.records['test-hit-1'].howler.analytic = null;
877
+ mockHitContext.hits['test-hit-1'].howler.analytic = null;
878
878
  });
879
879
  const contextMenuWrapper = screen.getByText('Test Content').parentElement;
880
880
  fireEvent.contextMenu(contextMenuWrapper);
@@ -1,9 +1,9 @@
1
- export type RecordQueryProps = {
1
+ export type HitQueryProps = {
2
2
  triggerSearch: (query: string) => void;
3
3
  onChange?: (query: string, isDirty: boolean) => void;
4
4
  searching?: boolean;
5
5
  disabled?: boolean;
6
6
  compact?: boolean;
7
7
  };
8
- declare const _default: import("react").NamedExoticComponent<RecordQueryProps>;
8
+ declare const _default: import("react").NamedExoticComponent<HitQueryProps>;
9
9
  export default _default;
@@ -5,7 +5,7 @@ import { Badge, Box, Card, Skeleton, Tooltip, alpha, useTheme } from '@mui/mater
5
5
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
6
6
  import TuiIconButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomIconButton';
7
7
  import QueryEditor from '@cccsaurora/howler-ui/components/routes/advanced/QueryEditor';
8
- import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
8
+ import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
9
9
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { useLocation } from 'react-router-dom';
@@ -13,8 +13,8 @@ import { useContextSelector } from 'use-context-selector';
13
13
  import { DEFAULT_QUERY } from '@cccsaurora/howler-ui/utils/constants';
14
14
  import { sanitizeMultilineLucene } from '@cccsaurora/howler-ui/utils/stringUtils';
15
15
  const DEFAULT_MULTILINE_HEIGHT = 250;
16
- const PROMPT_CONTEXT = 'isRecordQuery && !suggestWidgetVisible && !renameInputVisible && !inSnippetMode && !quickFixWidgetVisible';
17
- const RecordQuery = ({ searching = false, disabled = false, compact = false, triggerSearch, onChange }) => {
16
+ const PROMPT_CONTEXT = 'isHitQuery && !suggestWidgetVisible && !renameInputVisible && !inSnippetMode && !quickFixWidgetVisible';
17
+ const HitQuery = ({ searching = false, disabled = false, compact = false, triggerSearch, onChange }) => {
18
18
  const { t } = useTranslation();
19
19
  const location = useLocation();
20
20
  const theme = useTheme();
@@ -22,7 +22,7 @@ const RecordQuery = ({ searching = false, disabled = false, compact = false, tri
22
22
  const savedQuery = useContextSelector(ParameterContext, ctx => ctx.query || DEFAULT_QUERY);
23
23
  const prevQuery = useRef(null);
24
24
  const [query, setQuery] = useState(new URLSearchParams(window.location.search).get('query') || DEFAULT_QUERY);
25
- const fzfSearch = useContextSelector(RecordSearchContext, ctx => ctx?.fzfSearch ?? false);
25
+ const fzfSearch = useContextSelector(HitSearchContext, ctx => ctx?.fzfSearch ?? false);
26
26
  const [loaded, setLoaded] = useState(false);
27
27
  const [multiline, setMultiline] = useState(false);
28
28
  const [y, setY] = useState(0);
@@ -90,7 +90,7 @@ const RecordQuery = ({ searching = false, disabled = false, compact = false, tri
90
90
  window.addEventListener('mouseup', onMouseUp);
91
91
  }, [onMouseMove, onMouseUp]);
92
92
  const onMount = useCallback((ed) => {
93
- ed.createContextKey('isRecordQuery', true);
93
+ ed.createContextKey('isHitQuery', true);
94
94
  setLoaded(true);
95
95
  }, []);
96
96
  const options = useMemo(() => ({
@@ -148,4 +148,4 @@ const RecordQuery = ({ searching = false, disabled = false, compact = false, tri
148
148
  zIndex: 1000
149
149
  }, onMouseDown: onMouseDown }))] }));
150
150
  };
151
- export default memo(RecordQuery);
151
+ export default memo(HitQuery);
@@ -1,6 +1,5 @@
1
1
  import type { FC } from 'react';
2
2
  declare const InformationPane: FC<{
3
- selected?: string;
4
3
  onClose?: () => void;
5
4
  }>;
6
5
  export default InformationPane;
@@ -4,28 +4,29 @@ import { Badge, Box, Divider, IconButton, Skeleton, Stack, Tab, Tabs, Tooltip, u
4
4
  import TuiIconButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomIconButton';
5
5
  import { Icon } from '@iconify/react';
6
6
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
+ import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
7
8
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
8
- import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
9
9
  import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
10
10
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
11
11
  import VSBox from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBox';
12
12
  import VSBoxContent from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxContent';
13
13
  import VSBoxHeader from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxHeader';
14
14
  import Phrase from '@cccsaurora/howler-ui/components/elements/addons/search/phrase/Phrase';
15
+ import BundleButton from '@cccsaurora/howler-ui/components/elements/display/icons/BundleButton';
15
16
  import SocketBadge from '@cccsaurora/howler-ui/components/elements/display/icons/SocketBadge';
16
17
  import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
17
18
  import HitActions from '@cccsaurora/howler-ui/components/elements/hit/HitActions';
18
19
  import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
20
+ import HitComments from '@cccsaurora/howler-ui/components/elements/hit/HitComments';
21
+ import HitDetails from '@cccsaurora/howler-ui/components/elements/hit/HitDetails';
19
22
  import HitLabels from '@cccsaurora/howler-ui/components/elements/hit/HitLabels';
20
23
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
21
24
  import HitLinks from '@cccsaurora/howler-ui/components/elements/hit/HitLinks';
22
25
  import HitOutline from '@cccsaurora/howler-ui/components/elements/hit/HitOutline';
23
26
  import HitOverview from '@cccsaurora/howler-ui/components/elements/hit/HitOverview';
27
+ import HitRelated from '@cccsaurora/howler-ui/components/elements/hit/HitRelated';
24
28
  import HitSummary from '@cccsaurora/howler-ui/components/elements/hit/HitSummary';
25
- import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
26
- import RecordComments from '@cccsaurora/howler-ui/components/elements/record/RecordComments';
27
- import RecordRelated from '@cccsaurora/howler-ui/components/elements/record/RecordRelated';
28
- import RecordWorklog from '@cccsaurora/howler-ui/components/elements/record/RecordWorklog';
29
+ import HitWorklog from '@cccsaurora/howler-ui/components/elements/hit/HitWorklog';
29
30
  import useMyUserList from '@cccsaurora/howler-ui/components/hooks/useMyUserList';
30
31
  import ErrorBoundary from '@cccsaurora/howler-ui/components/routes/ErrorBoundary';
31
32
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
@@ -36,18 +37,17 @@ import { useLocation } from 'react-router-dom';
36
37
  import { useContextSelector } from 'use-context-selector';
37
38
  import { getUserList } from '@cccsaurora/howler-ui/utils/hitFunctions';
38
39
  import { validateRegex } from '@cccsaurora/howler-ui/utils/stringUtils';
39
- import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
40
40
  import { tryParse } from '@cccsaurora/howler-ui/utils/utils';
41
41
  import LeadRenderer from '../view/LeadRenderer';
42
- const InformationPane = ({ onClose, selected: _selected }) => {
42
+ const InformationPane = ({ onClose }) => {
43
43
  const { t, i18n } = useTranslation();
44
44
  const theme = useTheme();
45
45
  const location = useLocation();
46
46
  const { emit, isOpen } = useContext(SocketContext);
47
47
  const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
48
- const selected = useContextSelector(ParameterContext, ctx => ctx?.selected) ?? _selected;
48
+ const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
49
49
  const pluginStore = usePluginStore();
50
- const getRecord = useContextSelector(RecordContext, ctx => ctx.getRecord);
50
+ const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
51
51
  const [userIds, setUserIds] = useState(new Set());
52
52
  const [analytic, setAnalytic] = useState();
53
53
  const [hasOverview, setHasOverview] = useState(false);
@@ -58,7 +58,7 @@ const InformationPane = ({ onClose, selected: _selected }) => {
58
58
  const [_dossiers, setDossiers] = useState(null);
59
59
  const dossiers = useMemo(() => _dossiers ?? [], [_dossiers]);
60
60
  const users = useMyUserList(userIds);
61
- const record = useContextSelector(RecordContext, ctx => ctx.records[selected]);
61
+ const hit = useContextSelector(HitContext, ctx => ctx.hits[selected]);
62
62
  howlerPluginStore.plugins.forEach(plugin => {
63
63
  pluginStore.executeFunction(`${plugin}.on`, 'viewing');
64
64
  });
@@ -66,14 +66,14 @@ const InformationPane = ({ onClose, selected: _selected }) => {
66
66
  if (!selected) {
67
67
  return;
68
68
  }
69
- if (!record?.howler.data) {
69
+ if (!hit?.howler.data) {
70
70
  setLoading(true);
71
- getRecord(selected, true).finally(() => setLoading(false));
71
+ getHit(selected, true).finally(() => setLoading(false));
72
72
  return;
73
73
  }
74
- setUserIds(getUserList(record));
74
+ setUserIds(getUserList(hit));
75
75
  // eslint-disable-next-line react-hooks/exhaustive-deps
76
- }, [getRecord, selected]);
76
+ }, [getHit, selected]);
77
77
  useEffect(() => {
78
78
  if (selected) {
79
79
  setAnalytic(null);
@@ -82,20 +82,23 @@ const InformationPane = ({ onClose, selected: _selected }) => {
82
82
  }
83
83
  }, [selected]);
84
84
  useEffect(() => {
85
- if (isHit(record) && !analytic) {
86
- getMatchingAnalytic(record).then(setAnalytic);
85
+ if (hit && !analytic) {
86
+ getMatchingAnalytic(hit).then(setAnalytic);
87
87
  }
88
- }, [analytic, getMatchingAnalytic, record]);
88
+ }, [analytic, getMatchingAnalytic, hit]);
89
89
  useEffect(() => {
90
- if (isHit(record) && !_dossiers) {
91
- getMatchingDossiers(record).then(setDossiers);
90
+ if (hit && !_dossiers) {
91
+ getMatchingDossiers(hit).then(setDossiers);
92
92
  }
93
- }, [_dossiers, getMatchingDossiers, record]);
93
+ }, [_dossiers, getMatchingDossiers, hit]);
94
94
  useEffect(() => {
95
- if (isHit(record)) {
96
- getMatchingOverview(record).then(_overview => setHasOverview(!!_overview));
95
+ getMatchingOverview(hit).then(_overview => setHasOverview(!!_overview));
96
+ }, [getMatchingOverview, hit]);
97
+ useEffect(() => {
98
+ if (tab === 'hit_aggregate' && !hit?.howler.is_bundle) {
99
+ setTab('overview');
97
100
  }
98
- }, [getMatchingOverview, record]);
101
+ }, [hit?.howler.is_bundle, tab]);
99
102
  useEffect(() => {
100
103
  if (selected && isOpen()) {
101
104
  emit({
@@ -119,37 +122,48 @@ const InformationPane = ({ onClose, selected: _selected }) => {
119
122
  }
120
123
  // eslint-disable-next-line react-hooks/exhaustive-deps
121
124
  }, [hasOverview]);
125
+ /**
126
+ * What to show as the header? If loading a skeleton, then it depends on bundle or not. Bundles don't
127
+ * show anything while normal hits do
128
+ */
129
+ const header = useMemo(() => {
130
+ if (loading && !hit?.howler?.is_bundle) {
131
+ return _jsx(Skeleton, { variant: "rounded", height: 152 });
132
+ }
133
+ else if (!!hit && !hit.howler.is_bundle) {
134
+ return _jsx(HitBanner, { layout: HitLayout.DENSE, hit: hit });
135
+ }
136
+ else {
137
+ return null;
138
+ }
139
+ }, [hit, loading]);
122
140
  const tabContent = useMemo(() => {
123
141
  if (!tab) {
124
142
  return;
125
143
  }
126
- const defaultContent = {
127
- details: () => _jsx(ObjectDetails, { obj: record }),
128
- comments: () => _jsx(RecordComments, { record: record, users: users }),
129
- raw: () => _jsx(JSONViewer, { data: !loading && record, hideSearch: true, filter: filter }),
130
- data: () => (_jsx(JSONViewer, { data: !loading && record?.howler?.data?.map(entry => tryParse(entry)), collapse: false, hideSearch: true, filter: filter })),
131
- related: () => _jsx(RecordRelated, { record: record }),
132
- worklog: () => _jsx(RecordWorklog, { record: !loading && record, users: users })
133
- };
134
- if (!isHit(record)) {
135
- return defaultContent[tab]?.();
136
- }
137
144
  return {
138
- ...defaultContent,
139
- overview: () => _jsx(HitOverview, { hit: record }),
145
+ overview: () => _jsx(HitOverview, { hit: hit }),
146
+ details: () => _jsx(HitDetails, { hit: hit }),
147
+ hit_comments: () => _jsx(HitComments, { hit: hit, users: users }),
148
+ hit_raw: () => _jsx(JSONViewer, { data: !loading && hit, hideSearch: true, filter: filter }),
149
+ hit_data: () => (_jsx(JSONViewer, { data: !loading && hit?.howler?.data?.map(entry => tryParse(entry)), collapse: false, hideSearch: true, filter: filter })),
150
+ hit_worklog: () => _jsx(HitWorklog, { hit: !loading && hit, users: users }),
140
151
  hit_aggregate: () => _jsx(HitSummary, {}),
141
- ...Object.fromEntries((record?.howler.dossier ?? []).map((lead, index) => [
152
+ hit_related: () => _jsx(HitRelated, { hit: hit }),
153
+ ...Object.fromEntries((hit?.howler.dossier ?? []).map((lead, index) => [
142
154
  'lead:' + index,
143
- () => _jsx(LeadRenderer, { lead: lead, hit: record })
155
+ () => _jsx(LeadRenderer, { lead: lead, hit: hit })
144
156
  ])),
145
157
  ...Object.fromEntries(dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => [
146
158
  `external-lead:${dossierIndex}:${leadIndex}`,
147
- () => _jsx(LeadRenderer, { lead: _lead, hit: record })
159
+ () => _jsx(LeadRenderer, { lead: _lead, hit: hit })
148
160
  ])))
149
161
  }[tab]?.();
150
- }, [dossiers, filter, record, loading, tab, users]);
162
+ }, [dossiers, filter, hit, loading, tab, users]);
151
163
  const hasError = useMemo(() => !validateRegex(filter), [filter]);
152
- return (_jsxs(VSBox, { top: 10, sx: { height: '100%', flex: 1 }, children: [_jsxs(Stack, { direction: "column", flex: 1, sx: { overflowY: 'auto', flexGrow: 1 }, position: "relative", spacing: 1, ml: 2, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, flexShrink: 0, pr: 2, children: [_jsx(FlexOne, {}), onClose && !location.pathname.startsWith('/bundles') && (_jsx(TuiIconButton, { size: "small", onClick: onClose, tooltip: t('hit.panel.details.exit'), children: _jsx(Clear, {}) })), _jsx(SocketBadge, { size: "small" }), analytic && (_jsx(TuiIconButton, { size: "small", tooltip: t('analytic.open'), disabled: !analytic || loading, route: `/analytics/${analytic.analytic_id}`, children: _jsx(QueryStats, {}) })), !!record && (_jsx(TuiIconButton, { tooltip: t(`${record.__index}.open`), href: `/${record.__index}s/${selected}`, disabled: !record || loading, size: "small", target: "_blank", children: _jsx(OpenInNew, {}) }))] }), isHit(record) && (_jsxs(_Fragment, { children: [_jsx(Box, { pr: 2, children: _jsx(HitBanner, { layout: HitLayout.DENSE, hit: record }) }), !loading && (_jsxs(_Fragment, { children: [_jsx(HitOutline, { hit: record, layout: HitLayout.DENSE, forceAllFields: true }), _jsx(HitLabels, { hit: record })] })), _jsx(HitLinks, { hit: record, analytic: analytic, dossiers: dossiers })] })), loading && (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "rounded", height: 152 }), _jsx(Skeleton, { height: 124 })] })), _jsxs(VSBoxHeader, { ml: -1, mr: -1, pb: 1, sx: { top: '0px' }, children: [_jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: {
164
+ return (_jsxs(VSBox, { top: 10, sx: { height: '100%', flex: 1 }, children: [_jsxs(Stack, { direction: "column", flex: 1, sx: { overflowY: 'auto', flexGrow: 1 }, position: "relative", spacing: 1, ml: 2, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, flexShrink: 0, pr: 2, sx: [hit?.howler?.is_bundle && { position: 'absolute', top: 1, right: 0, zIndex: 1100 }], children: [_jsx(FlexOne, {}), onClose && !location.pathname.startsWith('/bundles') && (_jsx(TuiIconButton, { size: "small", onClick: onClose, tooltip: t('hit.panel.details.exit'), children: _jsx(Clear, {}) })), _jsx(SocketBadge, { size: "small" }), analytic && (_jsx(TuiIconButton, { size: "small", tooltip: t('hit.panel.analytic.open'), disabled: !analytic || loading, route: `/analytics/${analytic.analytic_id}`, children: _jsx(QueryStats, {}) })), hit?.howler.bundles?.length > 0 && _jsx(BundleButton, { ids: hit.howler.bundles, disabled: loading }), !!hit && !hit.howler.is_bundle && (_jsx(TuiIconButton, { tooltip: t('hit.panel.open'), href: `/hits/${selected}`, disabled: !hit || loading, size: "small", target: "_blank", children: _jsx(OpenInNew, {}) }))] }), _jsx(Box, { pr: 2, children: header }), !!hit &&
165
+ !hit.howler.is_bundle &&
166
+ (!loading ? (_jsxs(_Fragment, { children: [_jsx(HitOutline, { hit: hit, layout: HitLayout.DENSE, forceAllFields: true }), _jsx(HitLabels, { hit: hit })] })) : (_jsx(Skeleton, { height: 124 }))), _jsx(HitLinks, { hit: hit, analytic: analytic, dossiers: dossiers }), _jsxs(VSBoxHeader, { ml: -1, mr: -1, pb: 1, sx: { top: '0px' }, children: [_jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: {
153
167
  display: 'flex',
154
168
  flexDirection: 'row',
155
169
  pr: 2,
@@ -171,18 +185,17 @@ const InformationPane = ({ onClose, selected: _selected }) => {
171
185
  right: 0
172
186
  }
173
187
  }
174
- }, variant: "scrollable", children: [_jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('viewer.comments'), children: _jsx(Badge, { sx: {
188
+ }, variant: "scrollable", children: [_jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.comments'), children: _jsx(Badge, { sx: {
175
189
  '& > .MuiBadge-badge': {
176
190
  backgroundColor: theme.palette.divider,
177
191
  zIndex: 1,
178
192
  right: theme.spacing(-0.5)
179
193
  },
180
194
  '& > svg': { zIndex: 2 }
181
- }, badgeContent: record?.howler.comment?.length ?? 0, children: _jsx(Comment, {}) }) }), value: "comments", onClick: () => setTab('comments') }), isHit(record) && 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') }), isHit(record) &&
182
- record?.howler.dossier?.map((lead, index) => (_jsx(Tab
183
- // eslint-disable-next-line react/no-array-index-key
184
- , { 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
195
+ }, badgeContent: hit?.howler.comment?.length ?? 0, children: _jsx(Comment, {}) }) }), value: "hit_comments", onClick: () => setTab('hit_comments') }), 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
196
+ // eslint-disable-next-line react/no-array-index-key
197
+ , { 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
185
198
  // eslint-disable-next-line react/no-array-index-key
186
- , { 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: `external-lead:${dossierIndex}:${leadIndex}`, onClick: () => setTab(`external-lead:${dossierIndex}:${leadIndex}`) }, `external-lead:${dossierIndex}:${leadIndex}`)))), _jsx(FlexOne, {}), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.data'), children: _jsx(DataObject, {}) }), value: "data", onClick: () => setTab('data'), disabled: !record?.howler?.data }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.json'), children: _jsx(Code, {}) }), value: "raw", onClick: () => setTab('raw') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.worklog'), children: _jsx(History, {}) }), value: "worklog", onClick: () => setTab('worklog') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.related'), children: _jsx(LinkSharp, {}) }), value: "related", onClick: () => setTab('related') })] }), ['raw', 'data'].includes(tab) && (_jsx(Phrase, { sx: { mt: 1, pr: 1 }, value: filter, onChange: setFilter, error: hasError, label: t('json.viewer.search.label'), placeholder: t('json.viewer.search.prompt'), endAdornment: _jsx(IconButton, { onClick: () => setFilter(''), children: _jsx(Clear, {}) }) }))] }), _jsx(ErrorBoundary, { children: _jsx(VSBoxContent, { mr: -1, ml: -1, height: "100%", children: _jsx(Stack, { height: "100%", flex: 1, children: tabContent }) }) })] }), isHit(record) && (_jsxs(Box, { pr: 2, bgcolor: theme.palette.background.default, position: "relative", children: [_jsx(Divider, { orientation: "horizontal" }), _jsx(HitActions, { hit: record })] }))] }));
199
+ , { 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: `external-lead:${dossierIndex}:${leadIndex}`, onClick: () => setTab(`external-lead:${dossierIndex}:${leadIndex}`) }, `external-lead:${dossierIndex}:${leadIndex}`)))), _jsx(FlexOne, {}), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.data'), children: _jsx(DataObject, {}) }), value: "hit_data", onClick: () => setTab('hit_data'), disabled: !hit?.howler?.data }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.json'), children: _jsx(Code, {}) }), value: "hit_raw", onClick: () => setTab('hit_raw') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.worklog'), children: _jsx(History, {}) }), value: "hit_worklog", onClick: () => setTab('hit_worklog') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.related'), children: _jsx(LinkSharp, {}) }), value: "hit_related", onClick: () => setTab('hit_related') })] }), ['hit_raw', 'hit_data'].includes(tab) && (_jsx(Phrase, { sx: { mt: 1, pr: 1 }, value: filter, onChange: setFilter, error: hasError, label: t('json.viewer.search.label'), placeholder: t('json.viewer.search.prompt'), endAdornment: _jsx(IconButton, { onClick: () => setFilter(''), children: _jsx(Clear, {}) }) }))] }), _jsx(ErrorBoundary, { children: _jsx(VSBoxContent, { mr: -1, ml: -1, height: "100%", children: _jsx(Stack, { height: "100%", flex: 1, children: tabContent }) }) })] }), !!hit && hit?.howler && (_jsxs(Box, { pr: 2, bgcolor: theme.palette.background.default, position: "relative", children: [_jsx(Divider, { orientation: "horizontal" }), _jsx(HitActions, { hit: hit })] }))] }));
187
200
  };
188
201
  export default InformationPane;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ArrowDropDown, InfoOutlined, List, Settings, TableChart, ViewComfy, ViewCompact, ViewModule } from '@mui/icons-material';
3
3
  import { Checkbox, Divider, FormLabel, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip } from '@mui/material';
4
- import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
4
+ import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
5
5
  import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
6
6
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
7
7
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
@@ -10,8 +10,8 @@ import { useContextSelector } from 'use-context-selector';
10
10
  import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
11
11
  const LayoutSettings = () => {
12
12
  const { t } = useTranslation();
13
- const displayType = useContextSelector(RecordSearchContext, ctx => ctx.displayType);
14
- const setDisplayType = useContextSelector(RecordSearchContext, ctx => ctx.setDisplayType);
13
+ const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
14
+ const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
15
15
  const [hitLayout, setHitLayout] = useMyLocalStorageItem(StorageKey.HIT_LAYOUT, false);
16
16
  const [templateFieldCount, setTemplateFieldCount] = useMyLocalStorageItem(StorageKey.TEMPLATE_FIELD_COUNT, null);
17
17
  return (_jsx(ChipPopper, { icon: _jsx(Tooltip, { title: t('search.layout.settings'), children: _jsx(Settings, {}) }), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, disablePortal: false, slotProps: { chip: { size: 'medium', 'aria-label': t('search.layout.settings') } }, placement: "bottom-end", children: _jsxs(Stack, { spacing: 1, alignItems: "start", children: [_jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "display_type", children: t('page.settings.local.hits.display_type') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.display_type.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", "aria-labelledby": "display_type", children: [_jsx(ToggleButton, { value: "list", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(List, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.list') })] }) }), _jsx(ToggleButton, { value: "grid", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(TableChart, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.grid') })] }) })] }), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "layout", children: t('page.settings.local.hits.layout') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.layout.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsxs(ToggleButtonGroup, { exclusive: true, size: "small", value: hitLayout, onChange: (_, value) => setHitLayout(value), "aria-labelledby": "layout", children: [_jsx(ToggleButton, { value: HitLayout.DENSE, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewCompact, {}), _jsx("span", { children: t('page.settings.local.hits.layout.dense') })] }) }), _jsx(ToggleButton, { value: HitLayout.NORMAL, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewModule, {}), _jsx("span", { children: t('page.settings.local.hits.layout.normal') })] }) }), _jsx(ToggleButton, { value: HitLayout.COMFY, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewComfy, {}), _jsx("span", { children: t('page.settings.local.hits.layout.comfy') })] }) })] }), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "field_count", children: t('page.settings.local.hits.field_count') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.field_count.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignSelf: "stretch", children: [_jsx(Checkbox, { checked: templateFieldCount !== null, onChange: (_, checked) => setTemplateFieldCount(checked ? 3 : null), size: "small" }), _jsx(TextField, { type: "number", size: "small", disabled: templateFieldCount === null, value: templateFieldCount ?? 3, fullWidth: true, onChange: e => {
@@ -9,7 +9,6 @@ import { useTranslation } from 'react-i18next';
9
9
  import { useContextSelector } from 'use-context-selector';
10
10
  import HitFilter from './shared/HitFilter';
11
11
  import HitSort from './shared/HitSort';
12
- import IndexPicker from './shared/IndexPicker';
13
12
  import SearchSpan from './shared/SearchSpan';
14
13
  import ViewLink from './ViewLink';
15
14
  const QuerySettings = ({ boxSx }) => {
@@ -26,6 +25,6 @@ const QuerySettings = ({ boxSx }) => {
26
25
  await fetchViews();
27
26
  addView('');
28
27
  };
29
- return (_jsx(Box, { sx: boxSx ?? { position: 'relative', maxWidth: '1200px' }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Grid, { container: true, spacing: 1, sx: theme => ({ ml: `${theme.spacing(-1)} !important`, mt: `${theme.spacing(-1)} !important` }), children: [_jsx(Grid, { item: true, children: _jsx(IndexPicker, {}) }), _jsx(Grid, { item: true, children: _jsx(HitSort, {}) }), _jsx(Grid, { item: true, children: _jsx(SearchSpan, {}) }), currentViews?.map((view, id) => (_jsx(Grid, { item: true, children: _jsx(ViewLink, { id: id, viewId: view }) }, view))), filters?.map((filter, id) => (_jsx(Grid, { item: true, children: _jsx(HitFilter, { id: id, value: filter }) }, filter)))] }), _jsx(ChipPopper, { icon: _jsx(Add, {}), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, closeOnClick: true, slotProps: { chip: { size: 'small', color: 'primary' }, paper: { sx: { p: 1 } } }, children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Button, { id: "add-filter", "aria-label": t('hit.search.filter.add'), variant: "outlined", onClick: () => addFilter('howler.assessment:*'), disabled: filters?.some(filter => filter.endsWith('*')), sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.filter.add') })] }), _jsxs(Button, { id: "add-view", "aria-label": t('hit.search.view.add'), variant: "outlined", onClick: onAddView, disabled: !allowAddViews, sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.view.add') })] })] }) })] }) }));
28
+ return (_jsx(Box, { sx: boxSx ?? { position: 'relative', maxWidth: '1200px' }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Grid, { container: true, spacing: 1, sx: theme => ({ ml: `${theme.spacing(-1)} !important`, mt: `${theme.spacing(-1)} !important` }), children: [_jsx(Grid, { item: true, children: _jsx(HitSort, {}) }), _jsx(Grid, { item: true, children: _jsx(SearchSpan, {}) }), currentViews?.map((view, id) => (_jsx(Grid, { item: true, children: _jsx(ViewLink, { id: id, viewId: view }) }, view))), filters?.map((filter, id) => (_jsx(Grid, { item: true, children: _jsx(HitFilter, { id: id, value: filter }) }, filter)))] }), _jsx(ChipPopper, { icon: _jsx(Add, {}), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, closeOnClick: true, slotProps: { chip: { size: 'small', color: 'primary' }, paper: { sx: { p: 1 } } }, children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Button, { id: "add-filter", "aria-label": t('hit.search.filter.add'), variant: "outlined", onClick: () => addFilter('howler.assessment:*'), disabled: filters?.some(filter => filter.endsWith('*')), sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.filter.add') })] }), _jsxs(Button, { id: "add-view", "aria-label": t('hit.search.view.add'), variant: "outlined", onClick: onAddView, disabled: !allowAddViews, sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.view.add') })] })] }) })] }) }));
30
29
  };
31
30
  export default memo(QuerySettings);