@cccsaurora/howler-ui 2.17.0-dev.502 → 2.17.0-dev.513

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 (131) hide show
  1. package/api/index.d.ts +2 -0
  2. package/api/index.js +4 -2
  3. package/api/search/case.d.ts +4 -0
  4. package/api/search/case.js +8 -0
  5. package/api/search/index.d.ts +2 -1
  6. package/api/search/index.js +2 -1
  7. package/api/v2/case/index.d.ts +6 -0
  8. package/api/v2/case/index.js +18 -0
  9. package/api/v2/index.d.ts +4 -0
  10. package/api/v2/index.js +6 -0
  11. package/api/v2/search/facet.d.ts +3 -0
  12. package/api/v2/search/facet.js +12 -0
  13. package/api/v2/search/index.d.ts +6 -0
  14. package/api/v2/search/index.js +18 -0
  15. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  16. package/components/app/App.js +14 -0
  17. package/components/app/providers/FavouritesProvider.js +2 -2
  18. package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
  19. package/components/elements/{hit/HitDetails.d.ts → ObjectDetails.d.ts} +2 -1
  20. package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +14 -14
  21. package/components/elements/PluginTypography.d.ts +2 -1
  22. package/components/elements/PluginTypography.js +3 -2
  23. package/components/elements/UserList.d.ts +5 -2
  24. package/components/elements/UserList.js +14 -5
  25. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  26. package/components/elements/display/HowlerCard.js +1 -1
  27. package/components/elements/hit/HitBanner.js +19 -31
  28. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  29. package/components/elements/view/ViewTitle.js +1 -1
  30. package/components/hooks/useHitSelection.js +1 -35
  31. package/components/hooks/useMyPreferences.js +10 -1
  32. package/components/hooks/useMySitemap.js +3 -1
  33. package/components/hooks/useMyTheme.js +9 -2
  34. package/components/routes/action/view/ActionSearch.js +1 -1
  35. package/components/routes/action/view/Integrations.js +1 -9
  36. package/components/routes/advanced/QueryBuilder.js +1 -1
  37. package/components/routes/analytics/AnalyticDetails.js +2 -2
  38. package/components/routes/analytics/AnalyticSearch.js +1 -1
  39. package/components/routes/cases/CaseCard.d.ts +8 -0
  40. package/components/routes/cases/CaseCard.js +34 -0
  41. package/components/routes/cases/CaseViewer.d.ts +2 -0
  42. package/components/routes/cases/CaseViewer.js +24 -0
  43. package/components/routes/cases/Cases.d.ts +2 -0
  44. package/components/routes/cases/Cases.js +101 -0
  45. package/components/routes/cases/constants.d.ts +5 -0
  46. package/components/routes/cases/constants.js +5 -0
  47. package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
  48. package/components/routes/cases/detail/AlertPanel.js +32 -0
  49. package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
  50. package/components/routes/cases/detail/CaseDashboard.js +46 -0
  51. package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
  52. package/components/routes/cases/detail/CaseDetails.js +49 -0
  53. package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
  54. package/components/routes/cases/detail/CaseOverview.js +43 -0
  55. package/components/routes/cases/detail/CaseSidebar.d.ts +6 -0
  56. package/components/routes/cases/detail/CaseSidebar.js +36 -0
  57. package/components/routes/cases/detail/CaseTask.d.ts +10 -0
  58. package/components/routes/cases/detail/CaseTask.js +46 -0
  59. package/components/routes/cases/detail/ItemPage.d.ts +6 -0
  60. package/components/routes/cases/detail/ItemPage.js +93 -0
  61. package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
  62. package/components/routes/cases/detail/RelatedCasePanel.js +31 -0
  63. package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
  64. package/components/routes/cases/detail/TaskPanel.js +23 -0
  65. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +12 -0
  66. package/components/routes/cases/detail/aggregates/CaseAggregate.js +19 -0
  67. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
  68. package/components/routes/cases/detail/aggregates/SourceAggregate.js +27 -0
  69. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +12 -0
  70. package/components/routes/cases/detail/sidebar/CaseFolder.js +114 -0
  71. package/components/routes/cases/detail/sidebar/types.d.ts +3 -0
  72. package/components/routes/cases/hooks/useCase.d.ts +13 -0
  73. package/components/routes/cases/hooks/useCase.js +38 -0
  74. package/components/routes/help/ApiDocumentation.js +1 -1
  75. package/components/routes/help/HitDocumentation.js +1 -3
  76. package/components/routes/hits/search/HitContextMenu.js +4 -27
  77. package/components/routes/hits/search/HitContextMenu.test.js +0 -140
  78. package/components/routes/hits/search/InformationPane.d.ts +1 -0
  79. package/components/routes/hits/search/InformationPane.js +6 -29
  80. package/components/routes/hits/search/SearchPane.js +3 -5
  81. package/components/routes/hits/search/ViewLink.js +1 -1
  82. package/components/routes/hits/search/grid/EnhancedCell.js +1 -1
  83. package/components/routes/hits/view/HitViewer.js +3 -4
  84. package/components/routes/home/ViewCard.js +1 -1
  85. package/components/routes/observables/ObservableViewer.d.ts +7 -0
  86. package/components/routes/observables/ObservableViewer.js +27 -0
  87. package/components/routes/overviews/OverviewViewer.js +2 -2
  88. package/locales/en/translation.json +422 -397
  89. package/locales/fr/translation.json +429 -406
  90. package/models/entities/generated/AttachmentsFile.d.ts +12 -0
  91. package/models/entities/generated/Case.d.ts +28 -0
  92. package/models/entities/generated/DestinationOriginal.d.ts +19 -0
  93. package/models/entities/generated/EmailAttachment.d.ts +8 -0
  94. package/models/entities/generated/EmailParent.d.ts +19 -0
  95. package/models/entities/generated/Enrichments.d.ts +7 -0
  96. package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
  97. package/models/entities/generated/Howler.d.ts +0 -4
  98. package/models/entities/generated/HttpResponse.d.ts +11 -0
  99. package/models/entities/generated/Item.d.ts +9 -0
  100. package/models/entities/generated/Observable.d.ts +84 -0
  101. package/models/entities/generated/ObservableCloud.d.ts +20 -0
  102. package/models/entities/generated/ObservableDestination.d.ts +23 -0
  103. package/models/entities/generated/ObservableEmail.d.ts +30 -0
  104. package/models/entities/generated/ObservableFile.d.ts +36 -0
  105. package/models/entities/generated/ObservableHowler.d.ts +44 -0
  106. package/models/entities/generated/ObservableHttp.d.ts +11 -0
  107. package/models/entities/generated/ObservableObserver.d.ts +21 -0
  108. package/models/entities/generated/ObservableOrganization.d.ts +7 -0
  109. package/models/entities/generated/ObservableProcess.d.ts +34 -0
  110. package/models/entities/generated/ObservableSource.d.ts +23 -0
  111. package/models/entities/generated/ObservableThreat.d.ts +21 -0
  112. package/models/entities/generated/ObservableTls.d.ts +12 -0
  113. package/models/entities/generated/ObserverIngress.d.ts +9 -0
  114. package/models/entities/generated/Rule.d.ts +2 -10
  115. package/models/entities/generated/Task.d.ts +10 -0
  116. package/models/entities/generated/Threat.d.ts +2 -2
  117. package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
  118. package/package.json +125 -114
  119. package/plugins/clue/components/ClueTypography.js +2 -2
  120. package/plugins/clue/utils.d.ts +2 -1
  121. package/components/elements/display/icons/BundleButton.d.ts +0 -6
  122. package/components/elements/display/icons/BundleButton.js +0 -32
  123. package/components/routes/action/view/markdown/integrations.en.md.js +0 -1
  124. package/components/routes/action/view/markdown/integrations.fr.md.js +0 -1
  125. package/components/routes/help/BundleDocumentation.d.ts +0 -3
  126. package/components/routes/help/BundleDocumentation.js +0 -12
  127. package/components/routes/help/markdown/en/bundles.md.js +0 -1
  128. package/components/routes/help/markdown/fr/bundles.md.js +0 -1
  129. package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
  130. package/components/routes/hits/search/BundleParentMenu.js +0 -32
  131. /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
package/api/index.d.ts CHANGED
@@ -10,6 +10,7 @@ import * as overview from '@cccsaurora/howler-ui/api/overview';
10
10
  import * as search from '@cccsaurora/howler-ui/api/search';
11
11
  import * as template from '@cccsaurora/howler-ui/api/template';
12
12
  import * as user from '@cccsaurora/howler-ui/api/user';
13
+ import * as v2 from '@cccsaurora/howler-ui/api/v2';
13
14
  import * as view from '@cccsaurora/howler-ui/api/view';
14
15
  /**
15
16
  * Defining the default export exposing all children routes of '/api/v1/'.
@@ -28,6 +29,7 @@ declare const api: {
28
29
  user: typeof user;
29
30
  view: typeof view;
30
31
  notebook: typeof notebook;
32
+ v2: typeof v2;
31
33
  };
32
34
  /**
33
35
  * The specification interface of an Howler HTTP response.
package/api/index.js CHANGED
@@ -10,6 +10,7 @@ import * as overview from '@cccsaurora/howler-ui/api/overview';
10
10
  import * as search from '@cccsaurora/howler-ui/api/search';
11
11
  import * as template from '@cccsaurora/howler-ui/api/template';
12
12
  import * as user from '@cccsaurora/howler-ui/api/user';
13
+ import * as v2 from '@cccsaurora/howler-ui/api/v2';
13
14
  import * as view from '@cccsaurora/howler-ui/api/view';
14
15
  import AxiosClient from '@cccsaurora/howler-ui/rest/AxiosClient';
15
16
  import urlJoin from 'url-join';
@@ -38,7 +39,8 @@ const api = {
38
39
  template,
39
40
  user,
40
41
  view,
41
- notebook
42
+ notebook,
43
+ v2
42
44
  };
43
45
  /**
44
46
  * The base section of the Howler API uri.
@@ -57,7 +59,7 @@ export const uri = () => {
57
59
  * @returns `string` - properly formatted howler uri.
58
60
  */
59
61
  const format = (_uri) => {
60
- return _uri.startsWith(uri()) ? _uri : `${uri()}/${_uri.replace(/\/$/, '')}`;
62
+ return _uri.startsWith('/api') ? _uri : `${uri()}/${_uri.replace(/\/$/, '')}`;
61
63
  };
62
64
  /**
63
65
  * Append series of search parameters to the specified uri.
@@ -0,0 +1,4 @@
1
+ import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
3
+ export declare const uri: () => string;
4
+ export declare const post: (request?: HowlerSearchRequest) => Promise<HowlerSearchResponse<Case>>;
@@ -0,0 +1,8 @@
1
+ import { hpost, joinUri } from '@cccsaurora/howler-ui/api';
2
+ import { uri as parentUri } from '@cccsaurora/howler-ui/api/search';
3
+ export const uri = () => {
4
+ return joinUri(parentUri(), 'case');
5
+ };
6
+ export const post = (request) => {
7
+ return hpost(uri(), { ...(request || {}), query: request?.query || 'case_id:*' });
8
+ };
@@ -1,5 +1,6 @@
1
1
  import * as action from '@cccsaurora/howler-ui/api/search/action';
2
2
  import * as analytic from '@cccsaurora/howler-ui/api/search/analytic';
3
+ import * as case_ from '@cccsaurora/howler-ui/api/search/case';
3
4
  import * as count from '@cccsaurora/howler-ui/api/search/count';
4
5
  import * as dossier from '@cccsaurora/howler-ui/api/search/dossier';
5
6
  import * as facet from '@cccsaurora/howler-ui/api/search/facet';
@@ -60,4 +61,4 @@ export type HowlerExplainSearchResponse = {
60
61
  explanation: string;
61
62
  }[];
62
63
  };
63
- export { action, analytic, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
64
+ export { action, analytic, case_ as case, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
@@ -1,6 +1,7 @@
1
1
  import { joinUri, uri as parentUri } from '@cccsaurora/howler-ui/api';
2
2
  import * as action from '@cccsaurora/howler-ui/api/search/action';
3
3
  import * as analytic from '@cccsaurora/howler-ui/api/search/analytic';
4
+ import * as case_ from '@cccsaurora/howler-ui/api/search/case';
4
5
  import * as count from '@cccsaurora/howler-ui/api/search/count';
5
6
  import * as dossier from '@cccsaurora/howler-ui/api/search/dossier';
6
7
  import * as facet from '@cccsaurora/howler-ui/api/search/facet';
@@ -15,4 +16,4 @@ import * as view from '@cccsaurora/howler-ui/api/search/view';
15
16
  export const uri = () => {
16
17
  return joinUri(parentUri(), 'search');
17
18
  };
18
- export { action, analytic, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
19
+ export { action, analytic, case_ as case, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
@@ -0,0 +1,6 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ export declare const uri: (id?: string) => string;
3
+ export declare const get: (id: string) => Promise<Case>;
4
+ export declare const post: (newData: Partial<Case>) => Promise<Case>;
5
+ export declare const put: (id: string, _case: Partial<Case>) => Promise<Case>;
6
+ export declare const del: (id: string) => Promise<void>;
@@ -0,0 +1,18 @@
1
+ // eslint-disable-next-line import/no-cycle
2
+ import { hdelete, hget, hpost, hput, joinAllUri, joinUri } from '@cccsaurora/howler-ui/api';
3
+ import { uri as parentUri } from '@cccsaurora/howler-ui/api/v2';
4
+ export const uri = (id) => {
5
+ return id ? joinAllUri(parentUri(), 'case', id) : joinUri(parentUri(), 'case');
6
+ };
7
+ export const get = (id) => {
8
+ return hget(uri(id));
9
+ };
10
+ export const post = (newData) => {
11
+ return hpost(uri(), newData);
12
+ };
13
+ export const put = (id, _case) => {
14
+ return hput(uri(id), _case);
15
+ };
16
+ export const del = (id) => {
17
+ return hdelete(uri(id));
18
+ };
@@ -0,0 +1,4 @@
1
+ import * as case_ from '@cccsaurora/howler-ui/api/v2/case';
2
+ import * as search from '@cccsaurora/howler-ui/api/v2/search';
3
+ export declare const uri: () => string;
4
+ export { case_ as case, search };
@@ -0,0 +1,6 @@
1
+ import * as case_ from '@cccsaurora/howler-ui/api/v2/case';
2
+ import * as search from '@cccsaurora/howler-ui/api/v2/search';
3
+ export const uri = () => {
4
+ return '/api/v2';
5
+ };
6
+ export { case_ as case, search };
@@ -0,0 +1,3 @@
1
+ import type { HowlerFacetSearchRequest, HowlerFacetSearchResponse } from '@cccsaurora/howler-ui/api/search/facet';
2
+ export declare const uri: (indexes: string[]) => string;
3
+ export declare const post: (indexes: string | string[], request?: HowlerFacetSearchRequest) => Promise<HowlerFacetSearchResponse>;
@@ -0,0 +1,12 @@
1
+ // eslint-disable-next-line import/no-cycle
2
+ import { hpost, joinAllUri } from '@cccsaurora/howler-ui/api';
3
+ import { uri as parentUri } from '@cccsaurora/howler-ui/api/v2';
4
+ export const uri = (indexes) => {
5
+ return joinAllUri(parentUri(), 'search', 'facet', indexes.join(','));
6
+ };
7
+ export const post = (indexes, request) => {
8
+ if (typeof indexes === 'string') {
9
+ indexes = indexes.split(',');
10
+ }
11
+ return hpost(uri(indexes), { ...(request || {}), query: request?.query || 'howler.id:*' });
12
+ };
@@ -0,0 +1,6 @@
1
+ import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
+ import * as facet from './facet';
4
+ export declare const uri: (indexes: string[]) => string;
5
+ export declare const post: <T = Hit>(indexes: string | string[], request?: HowlerSearchRequest) => Promise<HowlerSearchResponse<T>>;
6
+ export { facet };
@@ -0,0 +1,18 @@
1
+ // eslint-disable-next-line import/no-cycle
2
+ import { hpost, joinAllUri } from '@cccsaurora/howler-ui/api';
3
+ import { uri as parentUri } from '@cccsaurora/howler-ui/api/v2';
4
+ import * as facet from './facet';
5
+ export const uri = (indexes) => {
6
+ return joinAllUri(parentUri(), 'search', indexes.join(','));
7
+ };
8
+ export const post = (indexes, request) => {
9
+ if (typeof indexes === 'string') {
10
+ indexes = indexes.split(',');
11
+ }
12
+ if (indexes.some(index => !['hit', 'observable'].includes(index))) {
13
+ // eslint-disable-next-line no-console
14
+ console.error('Only hit and observable indexes should be used currently.');
15
+ }
16
+ return hpost(uri(indexes), { ...(request || {}), query: request?.query || 'howler.id:*' });
17
+ };
18
+ export { facet };
@@ -75,7 +75,7 @@ const LeftNavDrawer = () => {
75
75
  }
76
76
  }, children: _jsx(AppName, {}) }), !isTopLayout && _jsx(Divider, {})] }));
77
77
  const hide = (_jsx(List, { disablePadding: true, children: _jsxs(ListItemButton, { onClick: leftnav.toggle, children: [_jsx(ListItemIcon, { children: leftnav.open ? _jsx(ChevronLeftIcon, {}) : _jsx(ChevronRightIcon, {}) }), _jsx(ListItemText, { primary: t('drawer.collapse') })] }, "chevron") }));
78
- return (_jsx(ClickAwayListener, { mouseEvent: "onMouseDown", touchEvent: "onTouchStart", onClickAway: onCloseDrawerIfOpen, children: _jsxs(StyledDrawer, { PaperProps: { elevation: 1 }, variant: "permanent", style: { height: '100%' }, width: preferences.leftnav.width, open: leftnav.open, children: [leftnav.open ? (header) : (_jsx(Tooltip, { title: preferences.appName, "aria-label": preferences.appName, placement: "right", children: header })), _jsx(List, { disablePadding: true, children: leftnav.elements.map((e, i) => {
78
+ return (_jsx(ClickAwayListener, { mouseEvent: "onMouseDown", touchEvent: "onTouchStart", onClickAway: onCloseDrawerIfOpen, children: _jsxs(StyledDrawer, { variant: "permanent", style: { height: '100%' }, width: preferences.leftnav.width, open: leftnav.open, children: [leftnav.open ? (header) : (_jsx(Tooltip, { title: preferences.appName, "aria-label": preferences.appName, placement: "right", children: header })), _jsx(List, { disablePadding: true, children: leftnav.elements.map((e, i) => {
79
79
  if (e.type === 'item') {
80
80
  const item = e.element;
81
81
  return _jsx(LeftNavItem, { item: item, onClick: isSmDown && onCloseDrawerIfOpen }, item.id);
@@ -25,6 +25,8 @@ import UserSearchProvider from '@cccsaurora/howler-ui/components/routes/admin/us
25
25
  import QueryBuilder from '@cccsaurora/howler-ui/components/routes/advanced/QueryBuilder';
26
26
  import AnalyticDetails from '@cccsaurora/howler-ui/components/routes/analytics/AnalyticDetails';
27
27
  import AnalyticSearch from '@cccsaurora/howler-ui/components/routes/analytics/AnalyticSearch';
28
+ import CaseViewer from '@cccsaurora/howler-ui/components/routes/cases/CaseViewer';
29
+ import Cases from '@cccsaurora/howler-ui/components/routes/cases/Cases';
28
30
  import DossierEditor from '@cccsaurora/howler-ui/components/routes/dossiers/DossierEditor';
29
31
  import Dossiers from '@cccsaurora/howler-ui/components/routes/dossiers/Dossiers';
30
32
  import ActionDocumentation from '@cccsaurora/howler-ui/components/routes/help/ActionDocumentation';
@@ -185,6 +187,18 @@ const router = createBrowserRouter([
185
187
  path: 'bundles/:id',
186
188
  element: _jsx(HitBrowser, {})
187
189
  },
190
+ {
191
+ path: 'cases',
192
+ element: _jsx(Cases, {})
193
+ },
194
+ {
195
+ path: 'cases/:id',
196
+ element: _jsx(CaseViewer, {})
197
+ },
198
+ {
199
+ path: 'cases/:id/*',
200
+ element: _jsx(CaseViewer, {})
201
+ },
188
202
  {
189
203
  path: 'templates',
190
204
  element: _jsx(Templates, {})
@@ -120,11 +120,11 @@ const FavouriteProvider = ({ children }) => {
120
120
  (async () => {
121
121
  const analyticElement = processAnalyticElement();
122
122
  if (analyticElement) {
123
- newElements.splice(1, 0, analyticElement);
123
+ newElements.splice(2, 0, analyticElement);
124
124
  }
125
125
  const viewElement = await processViewElement();
126
126
  if (viewElement) {
127
- newElements.splice(1, 0, viewElement);
127
+ newElements.splice(2, 0, viewElement);
128
128
  }
129
129
  leftNav.setElements(newElements);
130
130
  })();
@@ -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 './markdownExtendedTokenProvider';
8
- const OverviewEditor = ({ content, setContent, onMount, fontSize = 16, height = '100%', width = '100%', editorOptions = {} }) => {
7
+ import { conf, language } from '../routes/overviews/markdownExtendedTokenProvider';
8
+ const MarkdownEditor = ({ 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 OverviewEditor = ({ 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(OverviewEditor);
56
+ export default memo(MarkdownEditor);
@@ -1,5 +1,6 @@
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';
2
3
  declare const _default: import("react").NamedExoticComponent<{
3
- hit: Hit;
4
+ obj: Hit | Observable;
4
5
  }>;
5
6
  export default _default;
@@ -9,8 +9,8 @@ import { capitalize, groupBy, isArray, isEmpty, isNull, isObject, isPlainObject,
9
9
  import { memo, useEffect, useMemo, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import Throttler from '@cccsaurora/howler-ui/utils/Throttler';
12
- import PluginTypography from '../PluginTypography';
13
- const ListRenderer = memo(({ hit, objKey: key, entries, maxKeyLength }) => {
12
+ import PluginTypography from './PluginTypography';
13
+ const ListRenderer = memo(({ obj, objKey: key, entries, maxKeyLength }) => {
14
14
  const theme = useTheme();
15
15
  const { t } = useTranslation();
16
16
  const allPrimitives = useMemo(() => entries.every(entry => !isObject(entry)), [entries]);
@@ -36,15 +36,15 @@ const ListRenderer = memo(({ hit, objKey: key, entries, maxKeyLength }) => {
36
36
  marginBottom: allPrimitives ? 0 : theme.spacing(1)
37
37
  }, children: allPrimitives ? key.padStart(maxKeyLength ?? key.length) : key }) }), _jsxs(Grid, { container: true, spacing: allPrimitives ? 1 : 4, ml: allPrimitives ? -1 : -4, overflow: "hidden", maxWidth: "100%", children: [uniqueEntries.map((entry, index) => {
38
38
  if (Array.isArray(entry)) {
39
- return (_jsx(Grid, { item: true, xs: "auto", maxWidth: "100%", children: _jsx(ListRenderer, { hit: hit, objKey: `${key}.${index}`, entries: entry }) }, index));
39
+ return (_jsx(Grid, { item: true, xs: "auto", maxWidth: "100%", children: _jsx(ListRenderer, { obj: obj, objKey: `${key}.${index}`, entries: entry }) }, index));
40
40
  }
41
41
  if (isPlainObject(entry)) {
42
42
  return (_jsx(Grid, { item: true, xs: 'auto', maxWidth: "100%", minWidth: "350px", children: _jsx(ObjectRenderer, { parentKey: `${key}.${index}`, indent: true, data: entry }) }, index));
43
43
  }
44
- return (_jsxs(Grid, { item: true, maxWidth: "100%", className: `${key}_${index}`.replace(/\./g, '_'), component: "code", display: "flex", flexDirection: "row", children: [_jsx(PluginTypography, { context: "details", component: "code", style: { maxWidth: '100%', font: 'inherit' }, value: entry, field: key.replace(/\.[0-9]+/g, ''), hit: hit, children: entry }), allPrimitives && index < uniqueEntries.length - 1 && _jsx("span", { children: "," })] }, entry));
44
+ return (_jsxs(Grid, { item: true, maxWidth: "100%", className: `${key}_${index}`.replace(/\./g, '_'), component: "code", display: "flex", flexDirection: "row", children: [_jsx(PluginTypography, { context: "details", component: "code", style: { maxWidth: '100%', font: 'inherit' }, value: entry, field: key.replace(/\.[0-9]+/g, ''), obj: obj, children: entry }), allPrimitives && index < uniqueEntries.length - 1 && _jsx("span", { children: "," })] }, entry));
45
45
  }), omittedDuplicates && (_jsx(Grid, { item: true, display: "flex", alignItems: "center", children: _jsx(Tooltip, { title: t('duplicates.omitted'), children: _jsx(InfoOutlined, { sx: { fontSize: '20px', ml: 1 }, color: "disabled" }) }) }))] })] }));
46
46
  });
47
- const ObjectRenderer = memo(({ hit, data, parentKey, indent = false }) => {
47
+ const ObjectRenderer = memo(({ obj: obj, data, parentKey, indent = false }) => {
48
48
  const theme = useTheme();
49
49
  const entries = useMemo(() => {
50
50
  const unsorted = Object.entries(flatten(data, { safe: true })).map(([key, val]) => [key, val]);
@@ -59,7 +59,7 @@ const ObjectRenderer = memo(({ hit, data, parentKey, indent = false }) => {
59
59
  .filter(([__, val]) => !isNull(val) && !isUndefined(val) && !isEmpty(val))
60
60
  .map(([key, val]) => {
61
61
  if (Array.isArray(val)) {
62
- return _jsx(ListRenderer, { hit: hit, maxKeyLength: longestKey, objKey: key, entries: val }, key);
62
+ return _jsx(ListRenderer, { obj: obj, maxKeyLength: longestKey, objKey: key, entries: val }, key);
63
63
  }
64
64
  return (_jsxs("code", { className: (parentKey ? `${parentKey}.${key}` : key).replace(/\./g, '_'), style: {
65
65
  display: 'grid',
@@ -75,10 +75,10 @@ const ObjectRenderer = memo(({ hit, data, parentKey, indent = false }) => {
75
75
  paddingRight: theme.spacing(1),
76
76
  height: '100%',
77
77
  wordWrap: 'break-word'
78
- }, children: _jsx("code", { style: { maxWidth: '100%' }, children: key }) }), _jsx(Box, { display: "flex", alignItems: "start", children: _jsx(PluginTypography, { context: "details", component: "code", style: { maxWidth: '100%', font: 'inherit' }, value: val, field: (parentKey ? parentKey.concat('.', key) : key).replace(/\.[0-9]+/g, ''), hit: hit, children: val }) })] }, key));
78
+ }, children: _jsx("code", { style: { maxWidth: '100%' }, children: key }) }), _jsx(Box, { display: "flex", alignItems: "start", children: _jsx(PluginTypography, { context: "details", component: "code", style: { maxWidth: '100%', font: 'inherit' }, value: val, field: (parentKey ? parentKey.concat('.', key) : key).replace(/\.[0-9]+/g, ''), obj: obj, children: val }) })] }, key));
79
79
  }) })] }));
80
80
  });
81
- const Collapsible = memo(({ hit, title, data, query }) => {
81
+ const Collapsible = memo(({ obj, title, data, query }) => {
82
82
  const throttler = useMemo(() => new Throttler(400), []);
83
83
  const [scores, setScores] = useState([]);
84
84
  const [results, setResults] = useState({});
@@ -109,20 +109,20 @@ const Collapsible = memo(({ hit, title, data, query }) => {
109
109
  if (isEmpty(results)) {
110
110
  return null;
111
111
  }
112
- return (_jsxs(Accordion, { defaultExpanded: true, children: [_jsx(AccordionSummary, { expandIcon: _jsx(ArrowDropDown, {}), children: _jsx(Typography, { children: title }) }), _jsx(AccordionDetails, { children: _jsx(Stack, { spacing: 1, justifyContent: "stretch", sx: styles, children: _jsx(ObjectRenderer, { hit: hit, showParentKey: true, data: results }) }) })] }));
112
+ return (_jsxs(Accordion, { defaultExpanded: true, children: [_jsx(AccordionSummary, { expandIcon: _jsx(ArrowDropDown, {}), sx: { my: 0 }, children: _jsx(Typography, { children: title }) }), _jsx(AccordionDetails, { children: _jsx(Stack, { spacing: 0.5, justifyContent: "stretch", sx: styles, children: _jsx(ObjectRenderer, { obj: obj, showParentKey: true, data: results }) }) })] }));
113
113
  });
114
- const HitDetails = ({ hit }) => {
114
+ const ObjectDetails = ({ obj }) => {
115
115
  const { t } = useTranslation();
116
116
  const [query, setQuery] = useState('');
117
- const groups = useMemo(() => groupBy(Object.entries(flatten(hit ?? {}, { safe: true })).filter(([key, value]) => !key.startsWith('__') &&
117
+ const groups = useMemo(() => groupBy(Object.entries(flatten(obj ?? {}, { safe: true })).filter(([key, value]) => !key.startsWith('__') &&
118
118
  key.includes('.') &&
119
119
  ['howler', 'labels'].every(prefix => !key.startsWith(prefix)) &&
120
- !isEmpty(value)), ([key]) => key.split('.')[0]), [hit]);
120
+ !isEmpty(value)), ([key]) => key.split('.')[0]), [obj]);
121
121
  return (_jsxs(Stack, { spacing: 1, children: [_jsx(TextField, { value: query, onChange: event => setQuery(event.target.value), label: t('overview.search') }), Object.entries(groups).map(([section, entries]) => {
122
- return (_jsx(Collapsible, { hit: hit, query: query, title: section
122
+ return (_jsx(Collapsible, { obj: obj, query: query, title: section
123
123
  .split('_')
124
124
  .map(word => capitalize(word))
125
125
  .join(' '), data: Object.fromEntries(entries) }, section));
126
126
  })] }));
127
127
  };
128
- export default memo(HitDetails);
128
+ export default memo(ObjectDetails);
@@ -1,10 +1,11 @@
1
1
  import { type TypographyProps } 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';
3
4
  export type PluginTypographyProps = TypographyProps & {
4
5
  value: string;
5
6
  context: string;
6
7
  field?: string;
7
- hit?: Hit;
8
+ obj?: Hit | Observable;
8
9
  };
9
10
  declare const _default: import("react").NamedExoticComponent<PluginTypographyProps>;
10
11
  export default _default;
@@ -3,7 +3,7 @@ import { Typography } from '@mui/material';
3
3
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
4
4
  import { memo } from 'react';
5
5
  import { usePluginStore } from 'react-pluggable';
6
- const PluginTypography = ({ children, value, context, field, hit, ...props }) => {
6
+ const PluginTypography = ({ children, value, context, field, obj, ...props }) => {
7
7
  const pluginStore = usePluginStore();
8
8
  for (const plugin of howlerPluginStore.plugins) {
9
9
  const component = pluginStore.executeFunction(`${plugin}.typography`, {
@@ -11,7 +11,8 @@ const PluginTypography = ({ children, value, context, field, hit, ...props }) =>
11
11
  value,
12
12
  context,
13
13
  field,
14
- hit,
14
+ hit: obj,
15
+ obj,
15
16
  ...props
16
17
  });
17
18
  if (component) {
@@ -2,8 +2,11 @@ import type { SxProps, Theme } from '@mui/material';
2
2
  import type { FC } from 'react';
3
3
  declare const UserList: FC<{
4
4
  buttonSx?: SxProps<Theme>;
5
- userId: string;
6
- onChange: (userId: string) => void;
5
+ userIds: string[];
6
+ onChange: (userIds: string[]) => void;
7
7
  i18nLabel: string;
8
+ avatarHeight?: number;
9
+ disabled?: boolean;
10
+ multiple?: boolean;
8
11
  }>;
9
12
  export default UserList;
@@ -1,18 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Autocomplete, Box, IconButton, Popover, TextField, Typography } from '@mui/material';
2
+ import { Add } from '@mui/icons-material';
3
+ import { Autocomplete, AvatarGroup, Box, IconButton, Popover, Stack, TextField, Typography } from '@mui/material';
3
4
  import { UserListContext } from '@cccsaurora/howler-ui/components/app/providers/UserListProvider';
5
+ import { uniq } from 'lodash-es';
4
6
  import { useContext, useEffect, useMemo, useState } from 'react';
5
7
  import { useTranslation } from 'react-i18next';
6
8
  import HowlerAvatar from './display/HowlerAvatar';
7
- const UserList = ({ buttonSx = {}, userId, onChange, i18nLabel }) => {
9
+ const UserList = ({ buttonSx = {}, userIds, onChange, i18nLabel, avatarHeight = 32, multiple = false, disabled = false }) => {
8
10
  const { t } = useTranslation();
9
11
  const [anchorEl, setAnchorEl] = useState(null);
10
12
  const { users, searchUsers } = useContext(UserListContext);
11
- const userIds = useMemo(() => Object.keys(users), [users]);
13
+ const allUserIds = useMemo(() => Object.keys(users), [users]);
12
14
  useEffect(() => {
13
15
  searchUsers('uname:*');
14
16
  }, [searchUsers]);
15
- return (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: buttonSx, onClick: e => setAnchorEl(e.currentTarget), children: _jsx(HowlerAvatar, { userId: userId }) }), _jsx(Popover, { open: !!anchorEl, onClose: () => setAnchorEl(null), anchorEl: anchorEl, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, children: _jsx(Box, { sx: { p: 2 }, children: _jsx(Autocomplete, { sx: { minWidth: '300px' }, options: userIds, renderInput: params => _jsx(TextField, { ...params, label: t(i18nLabel), size: "small" }), renderOption: (props, _userId) => {
17
+ return (_jsxs(_Fragment, { children: [multiple ? (_jsxs(Stack, { direction: "row", spacing: 0.25, alignItems: "center", children: [_jsx(AvatarGroup, { children: uniq(userIds ?? [null]).map(userId => (_jsx(HowlerAvatar, { userId: userId, sx: { height: avatarHeight, width: avatarHeight } }, userId))) }), _jsx(IconButton, { size: "small", sx: buttonSx, disabled: disabled, onClick: e => setAnchorEl(e.currentTarget), children: _jsx(Add, {}) })] })) : (_jsx(IconButton, { sx: buttonSx, disabled: disabled, onClick: e => setAnchorEl(e.currentTarget), children: _jsx(HowlerAvatar, { userId: userIds[0], sx: { height: avatarHeight, width: avatarHeight } }) })), _jsx(Popover, { open: !!anchorEl, onClose: () => setAnchorEl(null), anchorEl: anchorEl, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, children: _jsx(Box, { sx: { p: 2 }, children: _jsx(Autocomplete, { disabled: disabled, multiple: multiple, sx: { minWidth: '300px' }, options: allUserIds, renderInput: params => _jsx(TextField, { ...params, label: t(i18nLabel), size: "small" }), renderOption: (props, _userId) => {
16
18
  const user = users[_userId];
17
19
  return (_jsx("li", { ...props, children: _jsxs(Box, { sx: {
18
20
  display: 'grid',
@@ -21,6 +23,13 @@ const UserList = ({ buttonSx = {}, userId, onChange, i18nLabel }) => {
21
23
  gridTemplateAreas: `"profile name"\n"profile email"`,
22
24
  columnGap: 1.5
23
25
  }, children: [_jsx(HowlerAvatar, { sx: { gridArea: 'profile', alignSelf: 'center', height: '32px', width: '32px' }, userId: user.username }), _jsx(Typography, { sx: { gridArea: 'name' }, variant: "body1", children: user.name }), _jsx(Typography, { sx: { gridArea: 'email' }, variant: "caption", children: user.email })] }) }));
24
- }, value: userId, onChange: (__, option) => onChange(option) }) }) })] }));
26
+ }, value: userIds, onChange: (__, options) => {
27
+ if (multiple) {
28
+ onChange(Array.isArray(options) ? options : [options]);
29
+ }
30
+ else {
31
+ onChange([Array.isArray(options) ? (options[0] ?? null) : options]);
32
+ }
33
+ } }) }) })] }));
25
34
  };
26
35
  export default UserList;
@@ -80,7 +80,7 @@ const Phrase = ({ value = '', variant = 'outlined', suggestions = [], lexer, sug
80
80
  onSelectCapture: _onSelectCapture,
81
81
  startAdornment: startAdornment && _jsx(InputAdornment, { position: "start", children: startAdornment }),
82
82
  endAdornment: endAdornment && _jsx(InputAdornment, { position: "end", children: endAdornment })
83
- } }), _jsx(Popper, { anchorEl: containerRef.current, style: { width: '100%', zIndex: 100 }, open: optionsOpen && (options.length > 0 || (debug && analysisRef.current?.tokens.length > 0)), disablePortal: true, children: _jsx(Paper, { elevation: 2, sx: { maxHeight: 200, overflow: 'auto', borderTopRightRadius: 0, borderTopLeftRadius: 0 }, children: _jsx(MenuList, { ref: menuRef, onKeyDown: _onMenuKeyDown, sx: {
83
+ } }), _jsx(Popper, { anchorEl: containerRef.current, style: { width: '100%', zIndex: 100 }, open: optionsOpen && (options.length > 0 || (debug && analysisRef.current?.tokens.length > 0)), disablePortal: true, children: _jsx(Paper, { elevation: 1, sx: { maxHeight: 200, overflow: 'auto', borderTopRightRadius: 0, borderTopLeftRadius: 0 }, children: _jsx(MenuList, { ref: menuRef, onKeyDown: _onMenuKeyDown, sx: {
84
84
  '&:focus': {
85
85
  outline: 'none'
86
86
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Card } from '@mui/material';
3
3
  import { memo } from 'react';
4
- const HowlerCard = props => (_jsx(Card, { style: { outline: 'none' }, elevation: props.variant !== 'outlined' ? 4 : 0, ...props }));
4
+ const HowlerCard = props => _jsx(Card, { elevation: props.variant !== 'outlined' ? 1 : 0, ...props });
5
5
  export default memo(HowlerCard);
@@ -52,35 +52,23 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
52
52
  }
53
53
  return `/api/static/mitre/${mitreId}.svg`;
54
54
  }, [mitreId]);
55
- const leftBox = useMemo(() => {
56
- if (hit.howler.is_bundle) {
57
- return (_jsx(Box, { sx: {
58
- alignSelf: 'stretch',
59
- backgroundColor: providerColor,
60
- borderRadius: theme.shape.borderRadius,
61
- minWidth: '15px'
62
- } }));
63
- }
64
- else {
65
- return (_jsx(HitBannerTooltip, { hit: hit, children: _jsxs(Box, { sx: {
66
- gridColumn: { xs: 'span 3', sm: 'span 1' },
67
- minWidth: '90px',
68
- backgroundColor: providerColor,
69
- color: theme.palette.getContrastText(providerColor),
70
- alignSelf: 'start',
71
- borderRadius: theme.shape.borderRadius,
72
- p: compressed ? 0.5 : 1,
73
- pt: 2,
74
- pl: 1
75
- }, display: "flex", flexDirection: "column", children: [_jsx(Typography, { variant: compressed ? 'caption' : 'body1', style: { wordBreak: 'break-all' }, children: hit.organization?.name ?? _jsx(Trans, { i18nKey: "unknown" }) }), iconUrl && (_jsx(Box, { sx: {
76
- width: '40px',
77
- height: '40px',
78
- mask: `url("${iconUrl}")`,
79
- maskSize: 'cover',
80
- background: theme.palette.getContrastText(providerColor)
81
- } }))] }) }));
82
- }
83
- }, [compressed, hit, iconUrl, providerColor, theme.palette, theme.shape.borderRadius]);
55
+ const leftBox = useMemo(() => (_jsx(HitBannerTooltip, { hit: hit, children: _jsxs(Box, { sx: {
56
+ gridColumn: { xs: 'span 3', sm: 'span 1' },
57
+ minWidth: '90px',
58
+ backgroundColor: providerColor,
59
+ color: theme.palette.getContrastText(providerColor),
60
+ alignSelf: 'start',
61
+ borderRadius: theme.shape.borderRadius,
62
+ p: compressed ? 0.5 : 1,
63
+ pt: 2,
64
+ pl: 1
65
+ }, display: "flex", flexDirection: "column", children: [_jsx(Typography, { variant: compressed ? 'caption' : 'body1', style: { wordBreak: 'break-all' }, children: hit.organization?.name ?? _jsx(Trans, { i18nKey: "unknown" }) }), iconUrl && (_jsx(Box, { sx: {
66
+ width: '40px',
67
+ height: '40px',
68
+ mask: `url("${iconUrl}")`,
69
+ maskSize: 'cover',
70
+ background: theme.palette.getContrastText(providerColor)
71
+ } }))] }) })), [compressed, hit, iconUrl, providerColor, theme.palette, theme.shape.borderRadius]);
84
72
  /**
85
73
  * The tooltips are necessary only when in the most compressed format
86
74
  */
@@ -88,7 +76,7 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
88
76
  const _children = (_jsxs(Stack, { direction: "row", spacing: 1, flex: 1, children: [_jsxs(Typography, { variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, sx: [
89
77
  { display: 'flex', flexDirection: 'row' },
90
78
  ...(Array.isArray(typographyProps?.sx) ? typographyProps?.sx : [typographyProps?.sx])
91
- ], children: [t(i18nKey), ":"] }), (Array.isArray(value) ? value : [value]).map(val => (_jsx(PluginTypography, { component: "span", context: "banner", variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, value: val, field: field, hit: hit }, val)))] }));
79
+ ], children: [t(i18nKey), ":"] }), (Array.isArray(value) ? value : [value]).map(val => (_jsx(PluginTypography, { component: "span", context: "banner", variant: textVariant, noWrap: compressed, textOverflow: compressed ? 'ellipsis' : 'wrap', ...typographyProps, value: val, field: field, obj: hit }, val)))] }));
92
80
  return compressed ? (_jsx(Tooltip, { title: Array.isArray(value) ? (_jsx("div", { children: value.map(_indicator => (_jsx("p", { style: { margin: 0, padding: 0 }, children: _indicator }, _indicator))) })) : (value), children: _children })) : (_children);
93
81
  }, [compressed, hit, t, textVariant]);
94
82
  return (_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(0, auto) minmax(0, 1fr) minmax(0, auto)", alignItems: "stretch", sx: { width: '100%', ml: 0, overflow: 'hidden' }, children: [leftBox, _jsxs(Stack, { sx: {
@@ -116,6 +104,6 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
116
104
  width: theme.spacing(3)
117
105
  }
118
106
  }
119
- ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), showAssigned && _jsx(Assigned, { hit: hit, layout: layout }), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: layout !== HitLayout.COMFY ? 'small' : 'medium', color: "primary" })), hit.howler.is_bundle && (_jsx(Chip, { size: layout !== HitLayout.COMFY ? 'small' : 'medium', label: t('hit.header.bundlesize', { hits: hit.howler.hits.length }) }))] }), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
107
+ ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), showAssigned && _jsx(Assigned, { hit: hit, layout: layout }), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: layout !== HitLayout.COMFY ? 'small' : 'medium', color: "primary" })), hit.howler.related && (_jsx(Chip, { size: layout !== HitLayout.COMFY ? 'small' : 'medium', label: t('hit.header.related', { count: hit.howler.related.length }) }))] }), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
120
108
  };
121
109
  export default HitBanner;
@@ -41,7 +41,7 @@ const DefaultOutline = ({ hit, fields, template, layout = HitLayout.NORMAL, read
41
41
  if (!displayedData) {
42
42
  return null;
43
43
  }
44
- return (_jsxs(React.Fragment, { children: [_jsx(Tooltip, { title: (config.indexes.hit[field]?.description ?? t('none')).split('\n')[0], children: _jsxs(Typography, { variant: layout !== HitLayout.COMFY ? 'caption' : 'body1', fontWeight: "bold", children: [field, ":"] }) }), _jsx(PluginTypography, { context: "outline", variant: layout !== HitLayout.COMFY ? 'caption' : 'body1', whiteSpace: "normal", sx: { width: '100%', wordBreak: 'break-all' }, value: displayedData, field: field, hit: hit, children: displayedData })] }, field));
44
+ return (_jsxs(React.Fragment, { children: [_jsx(Tooltip, { title: (config.indexes.hit[field]?.description ?? t('none')).split('\n')[0], children: _jsxs(Typography, { variant: layout !== HitLayout.COMFY ? 'caption' : 'body1', fontWeight: "bold", children: [field, ":"] }) }), _jsx(PluginTypography, { context: "outline", variant: layout !== HitLayout.COMFY ? 'caption' : 'body1', whiteSpace: "normal", sx: { width: '100%', wordBreak: 'break-all' }, value: displayedData, field: field, obj: hit, children: displayedData })] }, field));
45
45
  })] }));
46
46
  };
47
47
  export default memo(DefaultOutline);
@@ -21,5 +21,5 @@ export const ViewTitle = ({ title, type, query, sort, span }) => {
21
21
  readonly: _jsx(Lock, { fontSize: "small" }),
22
22
  global: _jsx(Language, { fontSize: "small" }),
23
23
  personal: _jsx(Person, { fontSize: "small" })
24
- }[type] }), _jsx(Typography, { variant: "body1", children: t(title) })] }), _jsx(Typography, { variant: "caption", children: _jsx("code", { children: query }) }), (sort || span) && (_jsxs(Stack, { direction: "row", sx: { mt: 1 }, spacing: 1, children: [sort?.split(',').map(_sort => (_jsx(Chip, { size: "small", label: _sort.split(' ')[0], icon: _sort.endsWith('desc') ? _jsx(ArrowDownward, {}) : _jsx(ArrowUpward, {}) }, _sort.split(' ')[0]))), spanLabel && _jsx(Chip, { size: "small", label: spanLabel })] }))] }));
24
+ }[type] }), _jsx(Typography, { variant: "body1", children: t(title) })] }), _jsx(Typography, { variant: "caption", children: _jsx("code", { children: query }) }), (sort || span) && (_jsxs(Stack, { direction: "row", sx: { mt: 1 }, spacing: 1, children: [sort?.split(',').map(_sort => (_jsx(Chip, { size: "small", label: _sort.split(' ')[0], icon: _sort.endsWith('desc') ? _jsx(ArrowDownward, {}) : _jsx(ArrowUpward, {}) }, _sort.split(' ')[0]))), spanLabel && _jsx(Chip, { label: spanLabel })] }))] }));
25
25
  };
@@ -1,21 +1,12 @@
1
- import { useAppBreadcrumbs } from '@cccsaurora/howler-ui/commons/components/app/hooks';
2
1
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
3
2
  import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
4
- import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
5
- import useMySitemap from '@cccsaurora/howler-ui/components/hooks/useMySitemap';
6
3
  import { useCallback, useState } from 'react';
7
- import { useNavigate } from 'react-router-dom';
8
4
  import { useContextSelector } from 'use-context-selector';
9
5
  const useHitSelection = () => {
10
- const navigate = useNavigate();
11
- const { setItems } = useAppBreadcrumbs();
12
- const { routes } = useMySitemap();
13
6
  const response = useContextSelector(HitSearchContext, ctx => ctx.response);
14
7
  const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
15
8
  const addHitToSelection = useContextSelector(HitContext, ctx => ctx.addHitToSelection);
16
9
  const removeHitFromSelection = useContextSelector(HitContext, ctx => ctx.removeHitFromSelection);
17
- const clearSelectedHits = useContextSelector(HitContext, ctx => ctx.clearSelectedHits);
18
- const setSelected = useContextSelector(ParameterContext, ctx => ctx.setSelected);
19
10
  const [lastSelected, setLastSelected] = useState(null);
20
11
  const onClick = useCallback((e, hit) => {
21
12
  setLastSelected(hit.howler.id);
@@ -47,32 +38,7 @@ const useHitSelection = () => {
47
38
  e.stopPropagation();
48
39
  return;
49
40
  }
50
- if (hit.howler.is_bundle) {
51
- const searchRoute = routes.find(_route => _route.path.startsWith(location.pathname.replace(/^(\/.*)\/.+/, '$1')));
52
- const newBreadcrumb = {
53
- ...searchRoute,
54
- path: location.pathname + location.search
55
- };
56
- setItems([{ route: newBreadcrumb, matcher: null }]);
57
- navigate(`/bundles/${hit.howler.id}?span=date.range.all&query=howler.id%3A*&offset=0`);
58
- clearSelectedHits(hit.howler.id);
59
- }
60
- else {
61
- clearSelectedHits(hit.howler.id);
62
- setSelected(hit.howler.id);
63
- }
64
- }, [
65
- addHitToSelection,
66
- clearSelectedHits,
67
- lastSelected,
68
- navigate,
69
- removeHitFromSelection,
70
- response,
71
- routes,
72
- selectedHits,
73
- setItems,
74
- setSelected
75
- ]);
41
+ }, [addHitToSelection, lastSelected, removeHitFromSelection, response, selectedHits]);
76
42
  return { lastSelected, setLastSelected, onClick };
77
43
  };
78
44
  export default useHitSelection;