@cccsaurora/howler-ui 2.17.0-dev.600 → 2.17.1-dev.627

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 (191) 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 +0 -14
  7. package/components/app/providers/FavouritesProvider.js +2 -2
  8. package/components/app/providers/HitSearchProvider.d.ts +1 -0
  9. package/components/app/providers/HitSearchProvider.js +11 -6
  10. package/components/app/providers/HitSearchProvider.test.js +32 -11
  11. package/components/app/providers/LocalStorageProvider.js +1 -1
  12. package/components/app/providers/ParameterProvider.d.ts +2 -9
  13. package/components/app/providers/ParameterProvider.js +240 -165
  14. package/components/app/providers/ParameterProvider.test.js +14 -307
  15. package/components/elements/EditRow.d.ts +4 -1
  16. package/components/elements/EditRow.js +4 -4
  17. package/components/elements/PluginTypography.d.ts +1 -2
  18. package/components/elements/PluginTypography.js +2 -3
  19. package/components/elements/UserList.d.ts +2 -5
  20. package/components/elements/UserList.js +5 -14
  21. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  22. package/components/elements/display/ChipPopper.d.ts +1 -1
  23. package/components/elements/display/ChipPopper.js +1 -1
  24. package/components/elements/display/HowlerCard.js +1 -1
  25. package/components/elements/display/Modal.js +0 -1
  26. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  27. package/components/elements/display/icons/BundleButton.js +32 -0
  28. package/components/elements/hit/HitBanner.js +48 -28
  29. package/components/elements/hit/HitCard.js +1 -1
  30. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  31. package/components/elements/hit/HitOutline.js +7 -3
  32. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  33. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  34. package/components/elements/hit/HitRelated.d.ts +1 -1
  35. package/components/elements/hit/HitRelated.js +3 -30
  36. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  37. package/components/elements/hit/related/PivotLink.js +1 -1
  38. package/components/elements/hit/related/RelatedLink.d.ts +1 -0
  39. package/components/elements/hit/related/RelatedLink.js +2 -2
  40. package/components/elements/view/ViewTitle.js +1 -1
  41. package/components/hooks/useHitActions.d.ts +1 -1
  42. package/components/hooks/useHitActions.js +2 -2
  43. package/components/hooks/useHitSelection.js +24 -3
  44. package/components/hooks/useLocalStorage.d.ts +13 -0
  45. package/components/hooks/useLocalStorage.js +53 -0
  46. package/components/hooks/useLocalStorageItem.d.ts +18 -0
  47. package/components/hooks/useLocalStorageItem.js +78 -0
  48. package/components/hooks/useLocalStorageItem.test.d.ts +1 -0
  49. package/components/hooks/useLocalStorageItem.test.js +144 -0
  50. package/components/hooks/useMyLocalStorage.js +2 -2
  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/routes/action/view/ActionSearch.js +1 -1
  56. package/components/routes/advanced/QueryBuilder.js +1 -1
  57. package/components/routes/analytics/AnalyticDetails.js +2 -2
  58. package/components/routes/analytics/AnalyticSearch.js +1 -1
  59. package/components/routes/help/ApiDocumentation.js +1 -1
  60. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  61. package/components/routes/help/BundleDocumentation.js +12 -0
  62. package/components/routes/help/HitDocumentation.js +3 -1
  63. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  64. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  65. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  66. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  67. package/components/routes/hits/search/HitContextMenu.js +2 -3
  68. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  69. package/components/routes/hits/search/InformationPane.js +28 -6
  70. package/components/routes/hits/search/LayoutSettings.d.ts +3 -0
  71. package/components/routes/hits/search/LayoutSettings.js +18 -0
  72. package/components/routes/hits/search/QuerySettings.js +1 -2
  73. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  74. package/components/routes/hits/search/SearchPane.js +37 -13
  75. package/components/routes/hits/search/ViewLink.js +1 -1
  76. package/components/routes/hits/search/grid/EnhancedCell.js +1 -1
  77. package/components/routes/hits/view/HitViewer.js +4 -3
  78. package/components/routes/home/AnalyticCard.d.ts +2 -3
  79. package/components/routes/home/AnalyticCard.js +2 -2
  80. package/components/routes/home/ViewCard.js +1 -1
  81. package/components/routes/home/ViewRefresh.d.ts +23 -0
  82. package/components/routes/home/ViewRefresh.js +67 -0
  83. package/components/routes/home/index.js +9 -46
  84. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  85. package/components/routes/overviews/OverviewViewer.js +2 -2
  86. package/components/routes/settings/LocalSection.js +2 -1
  87. package/locales/en/translation.json +6 -42
  88. package/locales/fr/translation.json +4 -35
  89. package/models/WithMetadata.d.ts +1 -2
  90. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  91. package/models/entities/generated/Howler.d.ts +4 -0
  92. package/models/entities/generated/Rule.d.ts +10 -2
  93. package/models/entities/generated/Threat.d.ts +2 -2
  94. package/package.json +1 -16
  95. package/plugins/clue/components/ClueTypography.js +2 -2
  96. package/plugins/clue/utils.d.ts +1 -2
  97. package/utils/constants.d.ts +4 -3
  98. package/utils/constants.js +1 -0
  99. package/api/search/case.d.ts +0 -4
  100. package/api/search/case.js +0 -8
  101. package/api/v2/case/index.d.ts +0 -6
  102. package/api/v2/case/index.js +0 -18
  103. package/api/v2/index.d.ts +0 -4
  104. package/api/v2/index.js +0 -6
  105. package/api/v2/search/facet.d.ts +0 -3
  106. package/api/v2/search/facet.js +0 -12
  107. package/api/v2/search/index.d.ts +0 -5
  108. package/api/v2/search/index.js +0 -24
  109. package/components/elements/ObjectDetails.d.ts +0 -6
  110. package/components/elements/case/CaseCard.d.ts +0 -8
  111. package/components/elements/case/CaseCard.js +0 -39
  112. package/components/elements/case/CasePreview.d.ts +0 -6
  113. package/components/elements/case/CasePreview.js +0 -17
  114. package/components/elements/case/StatusIcon.d.ts +0 -5
  115. package/components/elements/case/StatusIcon.js +0 -13
  116. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
  117. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  118. package/components/elements/hit/related/RelatedRecords.js +0 -63
  119. package/components/elements/observable/ObservableCard.d.ts +0 -5
  120. package/components/elements/observable/ObservableCard.js +0 -7
  121. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  122. package/components/elements/observable/ObservablePreview.js +0 -12
  123. package/components/hooks/useRelatedRecords.d.ts +0 -13
  124. package/components/hooks/useRelatedRecords.js +0 -32
  125. package/components/routes/cases/CaseViewer.d.ts +0 -2
  126. package/components/routes/cases/CaseViewer.js +0 -24
  127. package/components/routes/cases/Cases.d.ts +0 -2
  128. package/components/routes/cases/Cases.js +0 -101
  129. package/components/routes/cases/constants.d.ts +0 -5
  130. package/components/routes/cases/constants.js +0 -5
  131. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  132. package/components/routes/cases/detail/AlertPanel.js +0 -32
  133. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  134. package/components/routes/cases/detail/CaseDashboard.js +0 -49
  135. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  136. package/components/routes/cases/detail/CaseDetails.js +0 -61
  137. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  138. package/components/routes/cases/detail/CaseOverview.js +0 -43
  139. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -6
  140. package/components/routes/cases/detail/CaseSidebar.js +0 -36
  141. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  142. package/components/routes/cases/detail/CaseTask.js +0 -57
  143. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  144. package/components/routes/cases/detail/ItemPage.js +0 -93
  145. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  146. package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
  147. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  148. package/components/routes/cases/detail/TaskPanel.js +0 -52
  149. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
  150. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
  151. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  152. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -27
  153. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -12
  154. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -179
  155. package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
  156. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  157. package/components/routes/cases/hooks/useCase.js +0 -38
  158. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  159. package/components/routes/cases/modals/ResolveModal.js +0 -59
  160. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  161. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  162. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  163. package/components/routes/observables/ObservableViewer.js +0 -27
  164. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  165. package/models/entities/generated/Case.d.ts +0 -28
  166. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  167. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  168. package/models/entities/generated/EmailParent.d.ts +0 -19
  169. package/models/entities/generated/Enrichments.d.ts +0 -7
  170. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  171. package/models/entities/generated/HttpResponse.d.ts +0 -11
  172. package/models/entities/generated/Item.d.ts +0 -9
  173. package/models/entities/generated/Observable.d.ts +0 -84
  174. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  175. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  176. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  177. package/models/entities/generated/ObservableFile.d.ts +0 -36
  178. package/models/entities/generated/ObservableHowler.d.ts +0 -44
  179. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  180. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  181. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  182. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  183. package/models/entities/generated/ObservableSource.d.ts +0 -23
  184. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  185. package/models/entities/generated/ObservableTls.d.ts +0 -12
  186. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  187. package/models/entities/generated/Task.d.ts +0 -10
  188. package/utils/typeUtils.d.ts +0 -7
  189. package/utils/typeUtils.js +0 -18
  190. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  191. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
@@ -5,9 +5,9 @@ export declare const VERSION: any;
5
5
  export declare const MY_LOCAL_STORAGE_PREFIX = "howler.ui";
6
6
  export declare const MY_SESSION_STORAGE_PREFIX = "howler.ui.cache";
7
7
  export declare const ESCALATION_COLORS: {
8
- alert: "warning";
9
- evidence: "error";
10
- hit: "primary";
8
+ alert: string;
9
+ evidence: string;
10
+ hit: string;
11
11
  };
12
12
  export declare const STATUS_COLORS: {
13
13
  open: string;
@@ -53,6 +53,7 @@ export declare enum StorageKey {
53
53
  ONLY_RULES = "only_rules",
54
54
  PAGE_COUNT = "page_count",
55
55
  SEARCH_PANE_WIDTH = "search_pane_width",
56
+ TEMPLATE_FIELD_COUNT = "template_field_count",
56
57
  GRID_COLLAPSE_COLUMN = "grid_collapse_column",
57
58
  QUERY_HISTORY = "query_history",
58
59
  LOGIN_NONCE = "login_nonce",
@@ -58,6 +58,7 @@ export var StorageKey;
58
58
  StorageKey["ONLY_RULES"] = "only_rules";
59
59
  StorageKey["PAGE_COUNT"] = "page_count";
60
60
  StorageKey["SEARCH_PANE_WIDTH"] = "search_pane_width";
61
+ StorageKey["TEMPLATE_FIELD_COUNT"] = "template_field_count";
61
62
  StorageKey["GRID_COLLAPSE_COLUMN"] = "grid_collapse_column";
62
63
  StorageKey["QUERY_HISTORY"] = "query_history";
63
64
  StorageKey["LOGIN_NONCE"] = "login_nonce";
@@ -1,4 +0,0 @@
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>>;
@@ -1,8 +0,0 @@
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,6 +0,0 @@
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>;
@@ -1,18 +0,0 @@
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
- };
package/api/v2/index.d.ts DELETED
@@ -1,4 +0,0 @@
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 };
package/api/v2/index.js DELETED
@@ -1,6 +0,0 @@
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 };
@@ -1,3 +0,0 @@
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>;
@@ -1,12 +0,0 @@
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
- };
@@ -1,5 +0,0 @@
1
- import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
- import * as facet from './facet';
3
- export declare const uri: (indexes: string[]) => string;
4
- export declare const post: <T = any>(indexes: string | string[], request?: HowlerSearchRequest) => Promise<HowlerSearchResponse<T>>;
5
- export { facet };
@@ -1,24 +0,0 @@
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 { identity, isNil } from 'lodash-es';
5
- import * as facet from './facet';
6
- export const uri = (indexes) => {
7
- return joinAllUri(parentUri(), 'search', indexes.join(','));
8
- };
9
- export const post = (indexes, request) => {
10
- if (isNil(indexes)) {
11
- throw new Error('Indexes cannot be null or undefined.');
12
- }
13
- if (typeof indexes === 'string') {
14
- indexes = indexes.split(',').filter(identity);
15
- }
16
- if (indexes.some(index => !['hit', 'observable', 'case'].includes(index))) {
17
- throw new Error('Only hit, case and observable indexes should be used currently.');
18
- }
19
- if (indexes.length < 1) {
20
- throw new Error('indexes must have length of at least 1.');
21
- }
22
- return hpost(uri(indexes), { ...(request || {}), query: request?.query || 'howler.id:*' });
23
- };
24
- export { facet };
@@ -1,6 +0,0 @@
1
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
3
- declare const _default: import("react").NamedExoticComponent<{
4
- obj: Hit | Observable;
5
- }>;
6
- export default _default;
@@ -1,8 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import { type FC } from 'react';
3
- declare const CaseCard: FC<{
4
- case?: Case;
5
- caseId?: string;
6
- className?: string;
7
- }>;
8
- export default CaseCard;
@@ -1,39 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { CheckCircleOutline, HourglassBottom, RadioButtonUnchecked, UpdateOutlined } from '@mui/icons-material';
3
- import { Card, Chip, Divider, Grid, Skeleton, Stack, Tooltip, Typography, useTheme } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import StatusIcon from '@cccsaurora/howler-ui/components/elements/case/StatusIcon';
6
- import HowlerAvatar from '@cccsaurora/howler-ui/components/elements/display/HowlerAvatar';
7
- import PluginChip from '@cccsaurora/howler-ui/components/elements/PluginChip';
8
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
9
- import dayjs from 'dayjs';
10
- import { countBy } from 'lodash-es';
11
- import { useEffect, useState } from 'react';
12
- import { useTranslation } from 'react-i18next';
13
- import { twitterShort } from '@cccsaurora/howler-ui/utils/utils';
14
- const STATUS_COLORS = {
15
- resolved: 'success'
16
- };
17
- const CaseCard = ({ case: providedCase, caseId, className }) => {
18
- const { t } = useTranslation();
19
- const { dispatchApi } = useMyApi();
20
- const theme = useTheme();
21
- const [_case, setCase] = useState(providedCase);
22
- useEffect(() => {
23
- if (providedCase) {
24
- setCase(providedCase);
25
- }
26
- }, [providedCase]);
27
- useEffect(() => {
28
- if (caseId) {
29
- dispatchApi(api.v2.case.get(caseId), { throwError: false }).then(setCase);
30
- }
31
- }, [caseId, dispatchApi]);
32
- if (!_case) {
33
- return _jsx(Skeleton, { variant: "rounded", height: 250, sx: { mb: 1 }, className: className });
34
- }
35
- return (_jsx(Card, { variant: "outlined", sx: { p: 1, mb: 1, borderColor: theme.palette[STATUS_COLORS[_case.status]]?.main }, className: className, children: _jsx(Stack, { direction: "row", alignItems: "start", spacing: 1, children: _jsxs(Stack, { sx: { flex: 1 }, spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h6", display: "flex", alignItems: "start", children: _case.title }), _jsx(StatusIcon, { status: _case.status }), _jsx("div", { style: { flex: 1 } }), _case.start && _case.end && (_jsx(Tooltip, { title: dayjs(_case.updated).toString(), children: _jsx(Chip, { icon: _jsx(HourglassBottom, { fontSize: "small" }), size: "small", label: twitterShort(_case.start) + ' - ' + twitterShort(_case.end) }) })), _jsx(Tooltip, { title: dayjs(_case.updated).toString(), children: _jsx(Chip, { icon: _jsx(UpdateOutlined, { fontSize: "small" }), size: "small", label: twitterShort(_case.updated) }) })] }), _jsx(Typography, { variant: "caption", color: "textSecondary", children: _case.summary.trim().split('\n')[0] }), _case.participants?.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Divider, { flexItem: true }), _jsx(Stack, { direction: "row", spacing: 1, children: _case.participants?.map(participant => (_jsx(HowlerAvatar, { sx: { height: '20px', width: '20px' }, userId: participant }, participant))) })] })), _jsx(Divider, { flexItem: true }), _jsxs(Grid, { container: true, spacing: 1, children: [_case.targets?.map(indicator => (_jsx(Grid, { item: true, children: _jsx(PluginChip, { size: "small", color: "primary", context: "casecard", variant: "outlined", value: indicator, label: indicator }) }, indicator))), _case.targets?.length > 0 && (_case.indicators?.length > 0 || _case.threats?.length > 0) && (_jsx(Grid, { item: true, children: _jsx(Divider, { orientation: "vertical" }) })), _case.indicators?.map(indicator => (_jsx(Grid, { item: true, children: _jsx(PluginChip, { variant: "outlined", context: "casecard", value: indicator, label: indicator }) }, indicator))), _case.indicators?.length > 0 && _case.threats?.length > 0 && (_jsx(Grid, { item: true, children: _jsx(Divider, { orientation: "vertical" }) })), _case.threats?.map(indicator => (_jsx(Grid, { item: true, children: _jsx(PluginChip, { size: "small", color: "warning", variant: "outlined", context: "casecard", value: indicator, label: indicator }) }, indicator)))] }), _case.tasks?.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Divider, { flexItem: true }), _jsxs(Stack, { spacing: 0.5, alignItems: "start", children: [_case.tasks.some(task => task.complete) && (_jsx(Chip, { size: "small", color: "success", icon: _jsx(CheckCircleOutline, {}), label: `${countBy(_case.tasks, task => task.complete).true} ${t('complete')}` })), _case.tasks
36
- .filter(task => !task.complete)
37
- .map(task => (_jsx(Chip, { icon: _jsx(RadioButtonUnchecked, {}), label: task.summary }, task.id)))] })] }))] }) }) }, _case.case_id));
38
- };
39
- export default CaseCard;
@@ -1,6 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- type PreviewProps = {
3
- case: Case;
4
- };
5
- declare const _default: import("react").NamedExoticComponent<PreviewProps>;
6
- export default _default;
@@ -1,17 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { CheckCircleOutline, HourglassBottom, RadioButtonUnchecked, UpdateOutlined } from '@mui/icons-material';
3
- import { Chip, Stack, Tooltip, Typography, useTheme } from '@mui/material';
4
- import dayjs from 'dayjs';
5
- import { countBy } from 'lodash-es';
6
- import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { twitterShort } from '@cccsaurora/howler-ui/utils/utils';
9
- import HowlerAvatar from '../display/HowlerAvatar';
10
- import StatusIcon from './StatusIcon';
11
- const CasePreview = ({ case: _case }) => {
12
- const { t } = useTranslation();
13
- const theme = useTheme();
14
- const taskCounts = countBy(_case.tasks, task => task.complete);
15
- return (_jsxs(Stack, { flex: 1, spacing: 1, sx: { overflow: 'hidden', borderBottom: `thin solid ${theme.palette.divider}`, pb: 1, mb: 0 }, children: [_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "body1", fontWeight: "bold", children: _case.title }), _jsx(Typography, { variant: "caption", color: "textSecondary", children: _case.summary.trim().split('\n')[0] })] }), _jsx(StatusIcon, { status: _case.status }), _jsx("div", { style: { flex: 1 } }), _jsxs(Stack, { spacing: 1, alignItems: "end", children: [_case.start && _case.end && (_jsx(Tooltip, { title: dayjs(_case.updated).toString(), children: _jsx(Chip, { icon: _jsx(HourglassBottom, { fontSize: "small" }), size: "small", label: twitterShort(_case.start) + ' - ' + twitterShort(_case.end) }) })), _jsx(Tooltip, { title: dayjs(_case.updated).toString(), children: _jsx(Chip, { icon: _jsx(UpdateOutlined, { fontSize: "small" }), size: "small", label: twitterShort(_case.updated) }) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_case.participants?.length > 0 && (_jsx(Stack, { direction: "row", spacing: 1, children: _case.participants?.map(participant => (_jsx(HowlerAvatar, { sx: { height: '24px', width: '24px' }, userId: participant }, participant))) })), _jsx(Chip, { color: "success", icon: _jsx(CheckCircleOutline, {}), label: `${taskCounts.true ?? 0} ${t('complete')}` }), _jsx(Chip, { icon: _jsx(RadioButtonUnchecked, {}), label: `${taskCounts.false ?? 0} ${t('incomplete')}` })] })] }));
16
- };
17
- export default memo(CasePreview);
@@ -1,5 +0,0 @@
1
- import type { FC } from 'react';
2
- declare const StatusIcon: FC<{
3
- status: string;
4
- }>;
5
- export default StatusIcon;
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Check, HourglassBottom, Pause, Troubleshoot } from '@mui/icons-material';
3
- import { Tooltip } from '@mui/material';
4
- import { useTranslation } from 'react-i18next';
5
- const StatusIcon = ({ status }) => {
6
- const { t } = useTranslation();
7
- return (_jsx(Tooltip, { title: t(`page.cases.status.${status}`), children: {
8
- 'in-progress': _jsx(HourglassBottom, { color: "warning" }),
9
- 'on-hold': _jsx(Pause, { color: "disabled" }),
10
- resolved: _jsx(Check, { color: "success" })
11
- }[status] ?? _jsx(Troubleshoot, { color: "primary" }) }));
12
- };
13
- export default StatusIcon;
@@ -1,8 +0,0 @@
1
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
- import { type FC } from 'react';
3
- declare const AnalyticLink: FC<{
4
- hit: Hit;
5
- compressed?: boolean;
6
- alignSelf?: string;
7
- }>;
8
- export default AnalyticLink;
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Typography } from '@mui/material';
3
- import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
4
- import { useEffect, useState } from 'react';
5
- import { Link } from 'react-router-dom';
6
- const AnalyticLink = ({ hit, compressed, alignSelf = 'start' }) => {
7
- const { getMatchingAnalytic } = useMatchers();
8
- const [analyticId, setAnalyticId] = useState();
9
- useEffect(() => {
10
- if (!hit?.howler.analytic) {
11
- return;
12
- }
13
- getMatchingAnalytic(hit).then(analytic => setAnalyticId(analytic?.analytic_id));
14
- // eslint-disable-next-line react-hooks/exhaustive-deps
15
- }, [hit?.howler.analytic]);
16
- return (_jsxs(Typography, { variant: compressed ? 'body1' : 'h6', fontWeight: compressed && 'bold', sx: { alignSelf, '& a': { color: 'text.primary' } }, children: [analyticId ? (_jsx(Link, { to: `/analytics/${analyticId}`, onAuxClick: e => {
17
- e.stopPropagation();
18
- }, onClick: e => {
19
- e.stopPropagation();
20
- }, children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }));
21
- };
22
- export default AnalyticLink;
@@ -1,63 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link as LinkIcon } from '@mui/icons-material';
3
- import { alpha, Box, Chip, Divider, Stack, useTheme } from '@mui/material';
4
- import CasePreview from '@cccsaurora/howler-ui/components/elements/case/CasePreview';
5
- import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
6
- import ObservablePreview from '@cccsaurora/howler-ui/components/elements/observable/ObservablePreview';
7
- import useRelatedRecords from '@cccsaurora/howler-ui/components/hooks/useRelatedRecords';
8
- import { identity, uniq } from 'lodash-es';
9
- import { memo, useMemo, useState } from 'react';
10
- import { useTranslation } from 'react-i18next';
11
- import { Link } from 'react-router-dom';
12
- import { isCase, isHit, isObservable } from '@cccsaurora/howler-ui/utils/typeUtils';
13
- import HitPreview from '../HitPreview';
14
- const RecordLink = ({ to, ariaLabel, children }) => {
15
- const theme = useTheme();
16
- return (_jsxs(Box, { p: 1, flex: 1, position: "relative", sx: {
17
- '& > a': {
18
- backgroundColor: 'transparent',
19
- transition: theme.transitions.create('background-color', {
20
- duration: theme.transitions.duration.shortest
21
- }),
22
- '&:hover': {
23
- backgroundColor: alpha('#555', 0.5)
24
- }
25
- }
26
- }, children: [_jsx(Link, { to: to, style: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel }), children] }));
27
- };
28
- const RelatedRecords = ({ hit }) => {
29
- const { t } = useTranslation();
30
- const [open, setOpen] = useState(false);
31
- const [filter, setFilter] = useState(null);
32
- const related = useMemo(() => hit?.howler.related ?? [], [hit?.howler.related]);
33
- const records = useRelatedRecords(related, open);
34
- return (_jsxs(ChipPopper
35
- // eslint-disable-next-line jsx-a11y/anchor-is-valid
36
- , {
37
- // eslint-disable-next-line jsx-a11y/anchor-is-valid
38
- icon: _jsx(LinkIcon, {}), label: _jsx("span", { style: { cursor: 'pointer' }, children: t('hit.header.related', { count: related.length }) }), slotProps: {
39
- chip: { disabled: related.length < 1 },
40
- paper: {
41
- elevation: 4,
42
- onAuxClick: ev => ev.stopPropagation()
43
- }
44
- }, disablePortal: false, placement: "bottom-end", onToggle: _open => setOpen(_open), children: [_jsx(Stack, { direction: "row", spacing: 1, mb: 1, justifyContent: "end", children: uniq(records.map(record => record.__index))
45
- .filter(identity)
46
- .map(_type => (_jsx(Chip, { color: _type === filter ? 'primary' : 'default', label: _type, onClick: () => setFilter(current => (current === _type ? null : _type)) }, _type))) }), _jsxs(Stack, { maxWidth: "40vw", maxHeight: "70vh", sx: { overflowY: 'auto' }, children: [_jsx(Divider, {}), records
47
- .filter(record => !filter || record.__index === filter)
48
- .map(entry => {
49
- if (isHit(entry)) {
50
- const key = entry.howler.id;
51
- return (_jsx(RecordLink, { to: `/hits/${key}`, ariaLabel: t('hit.header.view.hit', { id: key }), children: _jsx(HitPreview, { hit: entry }) }, key));
52
- }
53
- else if (isCase(entry)) {
54
- const key = entry.case_id;
55
- return (_jsx(RecordLink, { to: `/cases/${key}`, ariaLabel: t('hit.header.view.case', { id: key }), children: _jsx(CasePreview, { case: entry }) }, key));
56
- }
57
- else if (isObservable(entry)) {
58
- const key = entry.howler.id;
59
- return (_jsx(RecordLink, { to: `/observables/${key}`, ariaLabel: t('hit.header.view.observable', { id: key }), children: _jsx(ObservablePreview, { observable: entry }) }, key));
60
- }
61
- })] })] }));
62
- };
63
- export default memo(RelatedRecords);
@@ -1,5 +0,0 @@
1
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
2
- declare const _default: import("react").NamedExoticComponent<{
3
- observable: Observable;
4
- }>;
5
- export default _default;
@@ -1,7 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { CardContent } from '@mui/material';
3
- import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
4
- import { memo } from 'react';
5
- import ObservablePreview from './ObservablePreview';
6
- const ObservableCard = ({ observable }) => (_jsx(HowlerCard, { sx: { position: 'relative' }, children: _jsx(CardContent, { children: _jsx(ObservablePreview, { observable: observable }) }) }));
7
- export default memo(ObservableCard);
@@ -1,6 +0,0 @@
1
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
2
- type PreviewProps = {
3
- observable: Observable;
4
- };
5
- declare const _default: import("react").NamedExoticComponent<PreviewProps>;
6
- export default _default;
@@ -1,12 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { OpenInNew } from '@mui/icons-material';
3
- import { Chip, IconButton, Stack, Typography, useTheme } from '@mui/material';
4
- import { memo } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Link } from 'react-router-dom';
7
- const ObservablePreview = ({ observable }) => {
8
- const { t } = useTranslation();
9
- const theme = useTheme();
10
- return (_jsx(Stack, { flex: 1, spacing: 1, sx: { overflow: 'hidden', borderBottom: `thin solid ${theme.palette.divider}`, pb: 1, mb: 0 }, children: _jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "body1", fontWeight: "bold", children: observable.event.provider }), _jsx("div", { style: { flex: 1 } }), _jsx(Chip, { label: observable.event.kind }), observable.event.reference && (_jsx(IconButton, { size: "small", component: Link, to: observable.event.reference, sx: { opacity: 1 }, target: "_blank", rel: "noopener noreferrer", children: _jsx(OpenInNew, { fontSize: "small" }) }))] }), observable.event.type && (_jsxs(Typography, { variant: "caption", children: [t('event.type'), " - ", observable.event.type.join(', ')] })), observable.event.module && (_jsxs(Typography, { variant: "caption", children: [t('event.module'), " - ", observable.event.module] }))] }) }));
11
- };
12
- export default memo(ObservablePreview);
@@ -1,13 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
4
- import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
5
- type MixedRecords = Hit | Observable | Case;
6
- /**
7
- * Fetches records matching the provided IDs from the hit, observable, and case indexes.
8
- *
9
- * @param ids - List of howler.id / case_id values to look up.
10
- * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
11
- */
12
- declare const useRelatedRecords: <T = MixedRecords>(ids: string[], enabled?: boolean) => WithMetadata<T>[];
13
- export default useRelatedRecords;
@@ -1,32 +0,0 @@
1
- import api from '@cccsaurora/howler-ui/api';
2
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
- import { useEffect, useState } from 'react';
4
- /**
5
- * Fetches records matching the provided IDs from the hit, observable, and case indexes.
6
- *
7
- * @param ids - List of howler.id / case_id values to look up.
8
- * @param enabled - When false the fetch is skipped (e.g. while a panel is closed).
9
- */
10
- const useRelatedRecords = (ids, enabled = true) => {
11
- const { dispatchApi } = useMyApi();
12
- const [records, setRecords] = useState([]);
13
- useEffect(() => {
14
- if (!enabled || ids.length === 0) {
15
- if (records.length > 0) {
16
- setRecords([]);
17
- }
18
- return;
19
- }
20
- (async () => {
21
- const joined = ids.join(' OR ');
22
- const result = await dispatchApi(api.v2.search.post('hit,observable,case', {
23
- query: `howler.id:(${joined}) OR case_id:(${joined})`
24
- }), { throwError: false, showError: true });
25
- if (result) {
26
- setRecords(result.items);
27
- }
28
- })();
29
- }, [dispatchApi, enabled, ids, records.length]);
30
- return records;
31
- };
32
- export default useRelatedRecords;
@@ -1,2 +0,0 @@
1
- declare const _default: import("react").NamedExoticComponent<{}>;
2
- export default _default;
@@ -1,24 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Stack } from '@mui/material';
3
- import { memo } from 'react';
4
- import { useParams } from 'react-router-dom';
5
- import NotFoundPage from '../404';
6
- import ErrorBoundary from '../ErrorBoundary';
7
- import CaseDashboard from './detail/CaseDashboard';
8
- import CaseDetails from './detail/CaseDetails';
9
- import CaseSidebar from './detail/CaseSidebar';
10
- import ItemPage from './detail/ItemPage';
11
- import useCase from './hooks/useCase';
12
- const CaseViewer = () => {
13
- const params = useParams();
14
- const { case: _case, missing } = useCase({ caseId: params.id });
15
- if (missing) {
16
- return _jsx(NotFoundPage, {});
17
- }
18
- return (_jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case }), _jsx(Box, { sx: {
19
- maxHeight: 'calc(100vh - 64px)',
20
- flex: 1,
21
- overflow: 'auto'
22
- }, children: _jsx(ErrorBoundary, { children: !_case || location.pathname.endsWith(_case.case_id) ? (_jsx(CaseDashboard, { case: _case })) : (_jsx(ItemPage, { case: _case })) }) }), _jsx(CaseDetails, { case: _case })] }));
23
- };
24
- export default memo(CaseViewer);
@@ -1,2 +0,0 @@
1
- declare const Cases: () => import("react/jsx-runtime").JSX.Element;
2
- export default Cases;
@@ -1,101 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Topic } from '@mui/icons-material';
3
- import { Typography } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import { TuiListProvider } from '@cccsaurora/howler-ui/components/elements/addons/lists';
6
- import { TuiListMethodContext } from '@cccsaurora/howler-ui/components/elements/addons/lists/TuiListProvider';
7
- import ItemManager from '@cccsaurora/howler-ui/components/elements/display/ItemManager';
8
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
9
- import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
10
- import { useCallback, useContext, useEffect, useState } from 'react';
11
- import { useTranslation } from 'react-i18next';
12
- import { useNavigate, useSearchParams } from 'react-router-dom';
13
- import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
14
- import CaseCard from '../../elements/case/CaseCard';
15
- const CasesBase = () => {
16
- const { t } = useTranslation();
17
- const navigate = useNavigate();
18
- const { dispatchApi } = useMyApi();
19
- const [searchParams, setSearchParams] = useSearchParams();
20
- const { load } = useContext(TuiListMethodContext);
21
- const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
22
- const [phrase, setPhrase] = useState('');
23
- const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
24
- const [response, setResponse] = useState(null);
25
- const [hasError, setHasError] = useState(false);
26
- const [loading, setLoading] = useState(false);
27
- const onSearch = useCallback(async () => {
28
- try {
29
- setLoading(true);
30
- setHasError(false);
31
- if (phrase) {
32
- searchParams.set('phrase', phrase);
33
- }
34
- else {
35
- searchParams.delete('phrase');
36
- }
37
- setSearchParams(searchParams, { replace: true });
38
- // Check for the actual search query
39
- const query = phrase ? `*:*${phrase}*` : '*:*';
40
- // Ensure the overview should be visible and/or matches the type we are filtering for
41
- setResponse(await dispatchApi(api.search.case.post({
42
- query,
43
- rows: pageCount,
44
- offset
45
- })));
46
- }
47
- catch (e) {
48
- setHasError(true);
49
- }
50
- finally {
51
- setLoading(false);
52
- }
53
- }, [phrase, setSearchParams, searchParams, dispatchApi, pageCount, offset]);
54
- // Load the items into list when response changes.
55
- // This hook should only trigger when the 'response' changes.
56
- useEffect(() => {
57
- if (response) {
58
- load(response.items.map((item) => ({
59
- id: item.case_id,
60
- item,
61
- selected: false,
62
- cursor: false
63
- })));
64
- }
65
- // eslint-disable-next-line react-hooks/exhaustive-deps
66
- }, [response, load]);
67
- const onPageChange = useCallback((_offset) => {
68
- if (_offset !== offset) {
69
- searchParams.set('offset', _offset.toString());
70
- setSearchParams(searchParams, { replace: true });
71
- setOffset(_offset);
72
- }
73
- }, [offset, searchParams, setSearchParams]);
74
- useEffect(() => {
75
- onSearch();
76
- if (!searchParams.has('offset')) {
77
- searchParams.set('offset', '0');
78
- setSearchParams(searchParams, { replace: true });
79
- }
80
- // eslint-disable-next-line react-hooks/exhaustive-deps
81
- }, []);
82
- useEffect(() => {
83
- if (response?.total <= offset) {
84
- setOffset(0);
85
- searchParams.set('offset', '0');
86
- setSearchParams(searchParams, { replace: true });
87
- }
88
- }, [offset, response?.total, searchParams, setSearchParams]);
89
- useEffect(() => {
90
- if (!loading) {
91
- onSearch();
92
- }
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
- }, [offset]);
95
- const renderer = useCallback((item, className) => _jsx(CaseCard, { case: item, className: className }), []);
96
- return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: loading, aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.cases.search.prompt') }), renderer: ({ item }, classRenderer) => renderer(item.item, classRenderer()), response: response, onSelect: (item) => navigate(`/cases/${item.id}`), onCreate: () => navigate('/cases/create'), createPrompt: "route.cases.create", searchPrompt: "route.cases.manager.search", createIcon: _jsx(Topic, { sx: { mr: 1 } }) }));
97
- };
98
- const Cases = () => {
99
- return (_jsx(TuiListProvider, { children: _jsx(CasesBase, {}) }));
100
- };
101
- export default Cases;
@@ -1,5 +0,0 @@
1
- export declare const ESCALATION_COLOR_MAP: {
2
- normal: string;
3
- focus: string;
4
- crisis: string;
5
- };
@@ -1,5 +0,0 @@
1
- export const ESCALATION_COLOR_MAP = {
2
- normal: 'default',
3
- focus: 'warning',
4
- crisis: 'error'
5
- };
@@ -1,6 +0,0 @@
1
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import { type FC } from 'react';
3
- declare const AlertPanel: FC<{
4
- case: Case;
5
- }>;
6
- export default AlertPanel;
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Divider, Pagination, Skeleton, Stack, Typography, useTheme } from '@mui/material';
3
- import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
4
- import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
5
- import { chunk, uniq } from 'lodash-es';
6
- import { useMemo, useState } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Link } from 'react-router-dom';
9
- const AlertPanel = ({ case: _case }) => {
10
- const theme = useTheme();
11
- const { t } = useTranslation();
12
- const [alertPage, setAlertPage] = useState(1);
13
- const alertPages = useMemo(() => chunk(uniq((_case?.items ?? []).filter(item => item.type === 'hit')), 5), [_case?.items]);
14
- if (!_case) {
15
- return _jsx(Skeleton, { height: 240 });
16
- }
17
- return (_jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.alerts') }), _jsx(Pagination, { count: alertPages.length, page: alertPage, onChange: (_, page) => setAlertPage(page) })] }), _jsx(Divider, {}), alertPages[alertPage - 1].map(item => (_jsxs(Box, { position: "relative", children: [_jsx(HitCard, { layout: HitLayout.DENSE, id: item.id }), _jsx(Box, { component: Link, to: item.path, sx: {
18
- position: 'absolute',
19
- top: 0,
20
- left: 0,
21
- width: '100%',
22
- height: '100%',
23
- cursor: 'pointer',
24
- zIndex: 100,
25
- borderRadius: '4px',
26
- '&:hover': {
27
- background: theme.palette.divider,
28
- border: `thin solid ${theme.palette.primary.light}`
29
- }
30
- } })] }, item.id)))] }));
31
- };
32
- export default AlertPanel;