@cccsaurora/howler-ui 2.17.0-dev.508 → 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 (34) hide show
  1. package/api/v2/search/facet.d.ts +3 -0
  2. package/api/v2/search/facet.js +12 -0
  3. package/api/v2/search/index.d.ts +2 -0
  4. package/api/v2/search/index.js +2 -0
  5. package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
  6. package/components/elements/UserList.d.ts +4 -2
  7. package/components/elements/UserList.js +14 -5
  8. package/components/routes/analytics/AnalyticDetails.js +2 -2
  9. package/components/routes/cases/CaseViewer.js +9 -23
  10. package/components/routes/cases/detail/AlertPanel.js +4 -1
  11. package/components/routes/cases/detail/CaseDashboard.js +21 -24
  12. package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
  13. package/components/routes/cases/detail/CaseDetails.js +49 -0
  14. package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
  15. package/components/routes/cases/detail/CaseOverview.js +43 -0
  16. package/components/routes/cases/detail/CaseSidebar.js +2 -1
  17. package/components/routes/cases/detail/CaseTask.d.ts +1 -0
  18. package/components/routes/cases/detail/CaseTask.js +11 -3
  19. package/components/routes/cases/detail/RelatedCasePanel.js +4 -1
  20. package/components/routes/cases/detail/TaskPanel.d.ts +1 -1
  21. package/components/routes/cases/detail/TaskPanel.js +5 -2
  22. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +12 -0
  23. package/components/routes/cases/detail/{CaseAggregate.js → aggregates/CaseAggregate.js} +7 -18
  24. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
  25. package/components/routes/cases/detail/aggregates/SourceAggregate.js +27 -0
  26. package/components/routes/cases/hooks/useCase.d.ts +13 -0
  27. package/components/routes/cases/hooks/useCase.js +38 -0
  28. package/components/routes/overviews/OverviewViewer.js +2 -2
  29. package/locales/en/translation.json +9 -0
  30. package/locales/fr/translation.json +9 -0
  31. package/models/entities/generated/Case.d.ts +1 -0
  32. package/package.json +125 -123
  33. package/components/routes/cases/detail/CaseAggregate.d.ts +0 -10
  34. /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
@@ -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
+ };
@@ -1,4 +1,6 @@
1
1
  import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
2
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
+ import * as facet from './facet';
3
4
  export declare const uri: (indexes: string[]) => string;
4
5
  export declare const post: <T = Hit>(indexes: string | string[], request?: HowlerSearchRequest) => Promise<HowlerSearchResponse<T>>;
6
+ export { facet };
@@ -1,6 +1,7 @@
1
1
  // eslint-disable-next-line import/no-cycle
2
2
  import { hpost, joinAllUri } from '@cccsaurora/howler-ui/api';
3
3
  import { uri as parentUri } from '@cccsaurora/howler-ui/api/v2';
4
+ import * as facet from './facet';
4
5
  export const uri = (indexes) => {
5
6
  return joinAllUri(parentUri(), 'search', indexes.join(','));
6
7
  };
@@ -14,3 +15,4 @@ export const post = (indexes, request) => {
14
15
  }
15
16
  return hpost(uri(indexes), { ...(request || {}), query: request?.query || 'howler.id:*' });
16
17
  };
18
+ export { facet };
@@ -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);
@@ -2,9 +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
8
  avatarHeight?: number;
9
+ disabled?: boolean;
10
+ multiple?: boolean;
9
11
  }>;
10
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, avatarHeight = 32 }) => {
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, 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, { 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, avatarHeight = 3
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;
@@ -51,7 +51,7 @@ const AnalyticDetails = () => {
51
51
  _setFilter(detection);
52
52
  }
53
53
  }, [filter]);
54
- const onOwnerChange = useCallback(async (ownerId) => {
54
+ const onOwnerChange = useCallback(async ([ownerId]) => {
55
55
  const result = await dispatchApi(api.analytic.owner.post(analytic.analytic_id, { username: ownerId }), {
56
56
  throwError: true,
57
57
  showError: true
@@ -108,7 +108,7 @@ const AnalyticDetails = () => {
108
108
  marginTop: '0 !important',
109
109
  marginLeft: `${theme.spacing(-1)} !important`,
110
110
  marginRight: `${theme.spacing(-1)} !important`
111
- }, userId: analytic?.owner, onChange: onOwnerChange, i18nLabel: "route.analytics.set.owner" })) : (_jsx(HowlerAvatar, { userId: analytic?.owner })), _jsx(Stack, { children: users[analytic?.owner] ? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body1", children: users[analytic?.owner].name }), _jsx(Typography, { component: "a", href: `mailto:${users[analytic?.owner].email}`, variant: "caption", color: "text.secondary", children: users[analytic?.owner].email })] })) : (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "text", width: "70px" }), _jsx(Skeleton, { variant: "text", width: "60px" })] })) })] })] }), filteredContributors.length > 0 && (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('route.analytics.contributors') }), _jsx(Stack, { direction: "row", alignItems: "center", spacing: 1, children: filteredContributors.map(_user => (_jsx(HowlerAvatar, { userId: _user }, _user))) })] })), analytic?.rule_crontab && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { spacing: 1, justifyContent: "space-between", children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('rule.interval') }), editingInterval ? (_jsxs(FormControl, { sx: { minWidth: '200px' }, children: [_jsx(InputLabel, { children: t('rule.interval') }), _jsx(Select, { size: "small", label: t('rule.interval'), onChange: event => setCrontab(event.target.value), value: crontab, children: RULE_INTERVALS.map(interval => (_jsx(MenuItem, { value: interval.crontab, children: t(interval.key) }, interval.key))) })] })) : (_jsx("code", { style: {
111
+ }, userIds: [analytic?.owner], onChange: onOwnerChange, i18nLabel: "route.analytics.set.owner" })) : (_jsx(HowlerAvatar, { userId: analytic?.owner })), _jsx(Stack, { children: users[analytic?.owner] ? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body1", children: users[analytic?.owner].name }), _jsx(Typography, { component: "a", href: `mailto:${users[analytic?.owner].email}`, variant: "caption", color: "text.secondary", children: users[analytic?.owner].email })] })) : (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "text", width: "70px" }), _jsx(Skeleton, { variant: "text", width: "60px" })] })) })] })] }), filteredContributors.length > 0 && (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('route.analytics.contributors') }), _jsx(Stack, { direction: "row", alignItems: "center", spacing: 1, children: filteredContributors.map(_user => (_jsx(HowlerAvatar, { userId: _user }, _user))) })] })), analytic?.rule_crontab && (_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { spacing: 1, justifyContent: "space-between", children: [_jsx(Typography, { variant: "body1", color: "text.secondary", children: t('rule.interval') }), editingInterval ? (_jsxs(FormControl, { sx: { minWidth: '200px' }, children: [_jsx(InputLabel, { children: t('rule.interval') }), _jsx(Select, { size: "small", label: t('rule.interval'), onChange: event => setCrontab(event.target.value), value: crontab, children: RULE_INTERVALS.map(interval => (_jsx(MenuItem, { value: interval.crontab, children: t(interval.key) }, interval.key))) })] })) : (_jsx("code", { style: {
112
112
  backgroundColor: theme.palette.background.paper,
113
113
  padding: theme.spacing(0.5),
114
114
  alignSelf: 'start',
@@ -1,38 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Skeleton, Stack } from '@mui/material';
3
- import api from '@cccsaurora/howler-ui/api';
4
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
5
- import { memo, useEffect, useState } from 'react';
2
+ import { Box, Stack } from '@mui/material';
3
+ import { memo } from 'react';
6
4
  import { useParams } from 'react-router-dom';
7
5
  import NotFoundPage from '../404';
6
+ import ErrorBoundary from '../ErrorBoundary';
8
7
  import CaseDashboard from './detail/CaseDashboard';
8
+ import CaseDetails from './detail/CaseDetails';
9
9
  import CaseSidebar from './detail/CaseSidebar';
10
10
  import ItemPage from './detail/ItemPage';
11
+ import useCase from './hooks/useCase';
11
12
  const CaseViewer = () => {
12
13
  const params = useParams();
13
- const { dispatchApi } = useMyApi();
14
- const [_case, setCase] = useState();
15
- const [loading, setLoading] = useState(false);
16
- const [notFound, setNotFound] = useState(false);
17
- useEffect(() => {
18
- if (!params.id) {
19
- return;
20
- }
21
- setLoading(true);
22
- dispatchApi(api.v2.case.get(params.id))
23
- .then(_dossier => {
24
- setCase(_dossier);
25
- })
26
- .catch(() => setNotFound(true))
27
- .finally(() => setLoading(false));
28
- }, [dispatchApi, params.id]);
29
- if (notFound) {
14
+ const { case: _case, missing } = useCase({ caseId: params.id });
15
+ if (missing) {
30
16
  return _jsx(NotFoundPage, {});
31
17
  }
32
- return (_jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case }), _jsxs(Box, { sx: {
18
+ return (_jsxs(Stack, { direction: "row", height: "100%", children: [_jsx(CaseSidebar, { case: _case }), _jsx(Box, { sx: {
33
19
  maxHeight: 'calc(100vh - 64px)',
34
20
  flex: 1,
35
21
  overflow: 'auto'
36
- }, children: [loading && _jsx(Skeleton, { variant: "rounded", height: 240 }), !_case || location.pathname.endsWith(_case.case_id) ? (_jsx(CaseDashboard, { case: _case })) : (_jsx(ItemPage, { case: _case }))] })] }));
22
+ }, children: _jsx(ErrorBoundary, { children: !_case || location.pathname.endsWith(_case.case_id) ? (_jsx(CaseDashboard, { case: _case })) : (_jsx(ItemPage, { case: _case })) }) }), _jsx(CaseDetails, { case: _case })] }));
37
23
  };
38
24
  export default memo(CaseViewer);
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Divider, Pagination, Stack, Typography, useTheme } from '@mui/material';
2
+ import { Box, Divider, Pagination, Skeleton, Stack, Typography, useTheme } from '@mui/material';
3
3
  import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
4
4
  import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
5
5
  import { chunk, uniq } from 'lodash-es';
@@ -11,6 +11,9 @@ const AlertPanel = ({ case: _case }) => {
11
11
  const { t } = useTranslation();
12
12
  const [alertPage, setAlertPage] = useState(1);
13
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
+ }
14
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: {
15
18
  position: 'absolute',
16
19
  top: 0,
@@ -1,15 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Card, CardContent, CardHeader, Divider, Grid, useTheme } from '@mui/material';
2
+ import { Grid, useTheme } from '@mui/material';
3
3
  import api from '@cccsaurora/howler-ui/api';
4
- import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown';
5
4
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
6
5
  import dayjs from 'dayjs';
7
- import { useCallback, useEffect, useState } from 'react';
6
+ import { get } from 'lodash-es';
7
+ import { useEffect, useMemo, useState } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
+ import useCase from '../hooks/useCase';
10
+ import CaseAggregate from './aggregates/CaseAggregate';
9
11
  import AlertPanel from './AlertPanel';
10
- import CaseAggregate from './CaseAggregate';
12
+ import CaseOverview from './CaseOverview';
11
13
  import RelatedCasePanel from './RelatedCasePanel';
12
14
  import TaskPanel from './TaskPanel';
15
+ const AGGREGATE_FIELDS = [
16
+ ['howler.outline.threat', 'material-symbols:warning-rounded', 'warning.main', 'page.cases.dashboard.threat'],
17
+ ['howler.outline.target', 'material-symbols:group', 'primary.main', 'page.cases.dashboard.target'],
18
+ ['howler.outline.indicators', 'fluent:number-symbol-24-filled', null, 'page.cases.dashboard.indicators']
19
+ ];
13
20
  const getDuration = (case_) => {
14
21
  if (case_?.start) {
15
22
  return dayjs.duration(dayjs(case_?.end ?? new Date()).diff(dayjs(case_.start), 'minute'), 'minute');
@@ -19,31 +26,21 @@ const CaseDashboard = ({ case: providedCase, caseId }) => {
19
26
  const { t } = useTranslation();
20
27
  const { dispatchApi } = useMyApi();
21
28
  const theme = useTheme();
22
- const [_case, setCase] = useState(providedCase);
29
+ const { case: _case, updateCase } = useCase({ case: providedCase, caseId });
30
+ const [records, setRecords] = useState(null);
31
+ const ids = useMemo(() => (_case?.items ?? []).filter(item => ['hit', 'observable'].includes(item.type)).map(item => item.id), [_case?.items]);
23
32
  useEffect(() => {
24
- if (providedCase) {
25
- setCase(providedCase);
26
- }
27
- }, [providedCase]);
28
- useEffect(() => {
29
- if (caseId) {
30
- dispatchApi(api.v2.case.get(caseId), { throwError: false }).then(setCase);
31
- }
32
- }, [caseId, dispatchApi]);
33
- const updateCase = useCallback(async (_updatedCase) => {
34
- if (!_case?.case_id) {
35
- return;
36
- }
37
- try {
38
- setCase(await dispatchApi(api.v2.case.put(_case.case_id, _updatedCase)));
39
- }
40
- finally {
33
+ if (ids?.length < 1) {
41
34
  return;
42
35
  }
43
- }, [_case?.case_id, dispatchApi]);
36
+ dispatchApi(api.v2.search.post(['hit', 'observable'], {
37
+ query: `howler.id:(${ids?.join(' OR ') || '*'})`,
38
+ fl: AGGREGATE_FIELDS.map(([field]) => field).join(',')
39
+ })).then(response => setRecords(response.items));
40
+ }, [dispatchApi, ids]);
44
41
  if (!_case) {
45
42
  return null;
46
43
  }
47
- return (_jsxs(Grid, { container: true, spacing: 5, width: "100%", px: 3, children: [_jsx(Grid, { item: true, xs: 12, children: _jsxs(Card, { children: [_jsx(CardHeader, { title: _case.title, subheader: _case.summary }), _jsx(Divider, {}), _jsx(CardContent, { children: _jsx(Markdown, { md: _case.overview }) })] }) }), _jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: "material-symbols:warning-rounded", iconColor: theme.palette.warning.main, field: "howler.outline.threat", ids: _case.items.filter(item => ['hit', 'observable'].includes(item.type)).map(item => item.id), subtitle: t('page.cases.dashboard.threat') }) }), _jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: "material-symbols:group", iconColor: theme.palette.primary.main, field: "howler.outline.target", ids: _case.items.filter(item => ['hit', 'observable'].includes(item.type)).map(item => item.id), subtitle: t('page.cases.dashboard.target') }) }), _jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: "fluent:number-symbol-24-filled", field: "howler.outline.indicators", ids: _case.items.filter(item => ['hit', 'observable'].includes(item.type)).map(item => item.id), subtitle: t('page.cases.dashboard.indicators') }) }), _jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: "mingcute:heartbeat-line", iconColor: theme.palette.error.light, title: getDuration(_case).format('HH[h] mm[m]'), ids: _case.items.filter(item => ['hit', 'observable'].includes(item.type)).map(item => item.id), subtitle: t('page.cases.dashboard.duration') }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(TaskPanel, { case: _case, updateCase: updateCase }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(AlertPanel, { case: _case }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(RelatedCasePanel, { case: _case }) })] }));
44
+ return (_jsxs(Grid, { container: true, spacing: 5, width: "100%", px: 3, children: [_jsx(Grid, { item: true, xs: 12, children: _jsx(CaseOverview, { case: _case, updateCase: updateCase }) }), AGGREGATE_FIELDS.map(([field, icon, iconColor, subtitle]) => (_jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: icon, iconColor: iconColor && get(theme.palette, iconColor), field: field, records: records, subtitle: t(subtitle) }) }, field))), _jsx(Grid, { item: true, xs: 12, md: 6, xl: 3, children: _jsx(CaseAggregate, { icon: "mingcute:heartbeat-line", iconColor: theme.palette.error.light, title: getDuration(_case).format('HH[h] mm[m]'), subtitle: t('page.cases.dashboard.duration') }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(TaskPanel, { case: _case, updateCase: updateCase }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(AlertPanel, { case: _case }) }), _jsx(Grid, { item: true, xs: 12, children: _jsx(RelatedCasePanel, { case: _case }) })] }));
48
45
  };
49
46
  export default CaseDashboard;
@@ -0,0 +1,6 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ import { type FC } from 'react';
3
+ declare const CaseDetails: FC<{
4
+ case: Case;
5
+ }>;
6
+ export default CaseDetails;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Check, FormatListBulleted, HourglassBottom, Pause, People, WarningRounded } from '@mui/icons-material';
3
+ import { Autocomplete, Card, Chip, Divider, LinearProgress, Skeleton, Stack, Table, TableBody, TableCell, TableRow, TextField, Typography } from '@mui/material';
4
+ import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
5
+ import UserList from '@cccsaurora/howler-ui/components/elements/UserList';
6
+ import dayjs from 'dayjs';
7
+ import { useContext, useState } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import useCase from '../hooks/useCase';
10
+ import SourceAggregate from './aggregates/SourceAggregate';
11
+ const CaseDetails = ({ case: providedCase }) => {
12
+ const { t } = useTranslation();
13
+ const { case: _case, updateCase } = useCase({ case: providedCase });
14
+ const { config } = useContext(ApiConfigContext);
15
+ const [loading, setLoading] = useState(false);
16
+ const wrappedUpdate = async (subset) => {
17
+ try {
18
+ setLoading(true);
19
+ await updateCase(subset);
20
+ }
21
+ finally {
22
+ setLoading(false);
23
+ }
24
+ };
25
+ if (!_case) {
26
+ return (_jsx(Card, { sx: {
27
+ borderRadius: 0,
28
+ width: '300px',
29
+ maxHeight: 'calc(100vh - 64px)',
30
+ display: 'flex',
31
+ flexDirection: 'column',
32
+ p: 1
33
+ }, children: _jsx(Skeleton, { variant: "rounded", height: 50 }) }));
34
+ }
35
+ return (_jsxs(Card, { elevation: 1, sx: {
36
+ borderRadius: 0,
37
+ width: '300px',
38
+ maxHeight: 'calc(100vh - 64px)',
39
+ display: 'flex',
40
+ flexDirection: 'column',
41
+ p: 1,
42
+ position: 'relative'
43
+ }, children: [_jsx(LinearProgress, { sx: { opacity: +loading, position: 'absolute', top: 0, left: 0, right: 0 } }), _jsxs(Stack, { spacing: 2, children: [_jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [{
44
+ 'in-progress': _jsx(HourglassBottom, { color: "warning" }),
45
+ closed: _jsx(Check, { color: "success" }),
46
+ 'on-hold': _jsx(Pause, { color: "disabled" })
47
+ }[_case.status] ?? _jsx(WarningRounded, { fontSize: "small" }), _jsx(Typography, { variant: "body1", children: t('page.cases.detail.status') })] }), _jsx(Autocomplete, { size: "small", disabled: loading, value: _case.status, options: config.lookups['howler.status'], renderInput: params => _jsx(TextField, { ...params, size: "small" }), onChange: (_ev, status) => wrappedUpdate({ status }) })] }), _jsx(Divider, {}), _jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(People, {}), _jsx(Typography, { variant: "body1", children: t('page.cases.detail.participants') })] }), _jsx(UserList, { buttonSx: { alignSelf: 'start' }, multiple: true, i18nLabel: "page.cases.detail.assignment", userIds: _case.participants ?? [], onChange: participants => wrappedUpdate({ participants }), disabled: loading })] }), _jsx(Divider, {}), _jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(FormatListBulleted, {}), _jsx(Typography, { variant: "body1", children: t('page.cases.detail.properties') })] }), _jsx(Table, { sx: { '& td': { p: 1 } }, children: _jsxs(TableBody, { children: [_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: t('page.cases.escalation') }) }), _jsx(TableCell, { children: _jsx(Chip, { size: "small", label: _case.escalation }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: t('page.cases.created') }) }), _jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: dayjs(_case.created).toString() }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: t('page.cases.updated') }) }), _jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: dayjs(_case.updated).toString() }) })] }), _jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: t('page.cases.sources') }) }), _jsx(TableCell, { children: _jsx(Typography, { variant: "caption", children: _jsx(SourceAggregate, { case: _case }) }) })] })] }) })] })] })] }));
48
+ };
49
+ export default CaseDetails;
@@ -0,0 +1,7 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ import { type FC } from 'react';
3
+ declare const CaseOverview: FC<{
4
+ case: Case;
5
+ updateCase: (_case: Partial<Case>) => Promise<void>;
6
+ }>;
7
+ export default CaseOverview;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Clear, Edit, Save } from '@mui/icons-material';
3
+ import { Box, Card, CardContent, CardHeader, Divider, IconButton, LinearProgress, Skeleton, Stack, useTheme } from '@mui/material';
4
+ import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown';
5
+ import MarkdownEditor from '@cccsaurora/howler-ui/components/elements/MarkdownEditor';
6
+ import { useEffect, useState } from 'react';
7
+ const CaseOverview = ({ case: _case, updateCase }) => {
8
+ const theme = useTheme();
9
+ const [editing, setEditing] = useState(false);
10
+ const [loading, setLoading] = useState(false);
11
+ const [overview, setOverview] = useState(_case?.overview);
12
+ useEffect(() => {
13
+ if (!editing && _case?.overview) {
14
+ setOverview(_case.overview);
15
+ }
16
+ }, [_case?.overview, editing]);
17
+ if (!_case) {
18
+ return _jsx(Skeleton, { height: 370 });
19
+ }
20
+ return (_jsxs(Card, { children: [_jsx(CardHeader, { title: _case.title, subheader: _case.summary }), _jsxs(Stack, { children: [_jsx(Divider, {}), _jsx(LinearProgress, { sx: { opacity: +loading } })] }), _jsx(CardContent, { sx: { position: 'relative' }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Box, { flex: 1, sx: {
21
+ '& > :first-child': {
22
+ marginTop: '0 !important'
23
+ },
24
+ '& > h1,h2,h3,h4,h5': {
25
+ fontSize: theme.typography.h5.fontSize
26
+ }
27
+ }, children: editing ? (_jsx(MarkdownEditor, { height: "40vh", content: overview, setContent: _content => setOverview(_content) })) : (_jsx(Markdown, { md: _case.overview })) }), _jsxs(Stack, { spacing: 1, children: [_jsx(IconButton, { size: "small", disabled: loading, onClick: async () => {
28
+ if (editing) {
29
+ try {
30
+ setLoading(true);
31
+ await updateCase({ overview });
32
+ }
33
+ finally {
34
+ setEditing(false);
35
+ setLoading(false);
36
+ }
37
+ }
38
+ else {
39
+ setEditing(true);
40
+ }
41
+ }, children: editing ? _jsx(Save, { color: loading ? 'disabled' : 'success', fontSize: "small" }) : _jsx(Edit, { fontSize: "small" }) }), editing && (_jsx(IconButton, { size: "small", disabled: loading, onClick: () => setEditing(false), children: _jsx(Clear, { color: loading ? 'disabled' : 'error', fontSize: "small" }) }))] })] }) })] }));
42
+ };
43
+ export default CaseOverview;
@@ -11,7 +11,8 @@ const CaseSidebar = ({ case: _case }) => {
11
11
  const { t } = useTranslation();
12
12
  const theme = useTheme();
13
13
  return (_jsxs(Box, { sx: {
14
- width: '350px',
14
+ flex: 1,
15
+ maxWidth: '350px',
15
16
  maxHeight: 'calc(100vh - 64px)',
16
17
  display: 'flex',
17
18
  flexDirection: 'column'
@@ -5,5 +5,6 @@ declare const CaseTask: FC<{
5
5
  paths: string[];
6
6
  onDelete: () => void;
7
7
  onEdit: (task: Partial<Task>) => Promise<void>;
8
+ loading?: boolean;
8
9
  }>;
9
10
  export default CaseTask;
@@ -12,7 +12,7 @@ const CaseTask = ({ task, onEdit, onDelete, paths }) => {
12
12
  const [summary, setSummary] = useState(task.summary);
13
13
  const [path, setPath] = useState(task.path);
14
14
  const dirty = summary !== task.summary || path !== task.path;
15
- const onOwnerChange = async (assignment) => {
15
+ const onOwnerChange = async ([assignment]) => {
16
16
  setLoading(true);
17
17
  await onEdit({
18
18
  assignment
@@ -26,13 +26,21 @@ const CaseTask = ({ task, onEdit, onDelete, paths }) => {
26
26
  setLoading(false);
27
27
  }
28
28
  };
29
- return (_jsxs(Card, { sx: { pl: 0.5, pr: 1, py: 0.5, position: 'relative' }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Checkbox, { color: "success", checked: task.complete, size: "small", onChange: (_ev, complete) => onEdit({ complete }) }), editing ? (_jsx(TextField, { value: summary, onChange: e => setSummary(e.target.value), size: "small", fullWidth: true, sx: { minWidth: '40%' } })) : (_jsx(Typography, { sx: [task.complete && { textDecoration: 'line-through' }], children: task.summary })), task.path && !editing && _jsx(Chip, { clickable: true, component: Link, to: task.path, label: task.path }), editing && (_jsx(Autocomplete, { value: path, options: paths, onChange: (_ev, value) => setPath(value), fullWidth: true, renderInput: params => _jsx(TextField, { ...params, size: "small" }) })), task.assignment && (_jsx(UserList, { userId: task.assignment, onChange: onOwnerChange, i18nLabel: "route.cases.task.set.assignment", avatarHeight: 24 })), _jsx("div", { style: { flex: 1 } }), editing && (_jsx(Tooltip, { title: t('route.cases.task.delete'), children: _jsx(IconButton, { size: "small", color: "error", onClick: onDelete, children: _jsx(Delete, { fontSize: "small" }) }) })), _jsx(Tooltip, { title: t(editing ? 'route.cases.task.edit.save' : 'route.cases.task.edit'), children: _jsx(IconButton, { size: "small", color: editing ? 'success' : 'default', onClick: () => {
29
+ return (_jsxs(Card, { sx: { pl: 0.5, pr: 1, py: 0.5, position: 'relative' }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Checkbox, { disabled: loading, color: "success", checked: task.complete, size: "small", onChange: async (_ev, complete) => {
30
+ try {
31
+ setLoading(true);
32
+ await onEdit({ complete });
33
+ }
34
+ finally {
35
+ setLoading(false);
36
+ }
37
+ } }), editing ? (_jsx(TextField, { disabled: loading, value: summary, onChange: e => setSummary(e.target.value), size: "small", fullWidth: true, sx: { minWidth: '40%' } })) : (_jsx(Typography, { sx: [task.complete && { textDecoration: 'line-through' }], children: task.summary })), task.path && !editing && _jsx(Chip, { clickable: true, component: Link, to: task.path, label: task.path }), editing && (_jsx(Autocomplete, { disabled: loading, value: path, options: paths, onChange: (_ev, value) => setPath(value), fullWidth: true, renderInput: params => _jsx(TextField, { ...params, size: "small" }) })), task.assignment && (_jsx(UserList, { disabled: loading, userIds: [task.assignment], onChange: onOwnerChange, i18nLabel: "route.cases.task.set.assignment", avatarHeight: 24 })), _jsx("div", { style: { flex: 1 } }), editing && (_jsx(Tooltip, { title: t('route.cases.task.delete'), children: _jsx(IconButton, { size: "small", color: "error", onClick: onDelete, children: _jsx(Delete, { fontSize: "small" }) }) })), _jsx(Tooltip, { title: t(editing ? 'route.cases.task.edit.save' : 'route.cases.task.edit'), children: _jsx(IconButton, { size: "small", color: editing ? 'success' : 'default', onClick: () => {
30
38
  if (!editing) {
31
39
  setEditing(true);
32
40
  return;
33
41
  }
34
42
  setEditing(false);
35
43
  onSubmit();
36
- }, disabled: !dirty && editing, children: editing ? _jsx(Check, { fontSize: "small" }) : _jsx(Edit, { fontSize: "small" }) }) }), editing && (_jsx(Tooltip, { title: t('route.cases.task.edit.cancel'), children: _jsx(IconButton, { size: "small", onClick: () => setEditing(false), children: _jsx(Close, { fontSize: "small" }) }) }))] }), loading && _jsx(LinearProgress, { sx: { left: 0, bottom: 0, right: 0, position: 'absolute' } })] }, task.id));
44
+ }, disabled: (!dirty && editing) || loading, children: editing ? _jsx(Check, { fontSize: "small" }) : _jsx(Edit, { fontSize: "small" }) }) }), editing && (_jsx(Tooltip, { title: t('route.cases.task.edit.cancel'), children: _jsx(IconButton, { size: "small", onClick: () => setEditing(false), disabled: loading, children: _jsx(Close, { fontSize: "small" }) }) }))] }), loading && _jsx(LinearProgress, { sx: { left: 0, bottom: 0, right: 0, position: 'absolute' } })] }, task.id));
37
45
  };
38
46
  export default CaseTask;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Divider, Pagination, Stack, Typography, useTheme } from '@mui/material';
2
+ import { Box, Divider, Pagination, Skeleton, Stack, Typography, useTheme } from '@mui/material';
3
3
  import { chunk, uniq } from 'lodash-es';
4
4
  import { useMemo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
@@ -10,6 +10,9 @@ const RelatedCasePanel = ({ case: _case }) => {
10
10
  const theme = useTheme();
11
11
  const [casePage, setCasePage] = useState(1);
12
12
  const casePages = useMemo(() => chunk(uniq((_case?.items ?? []).filter(item => item.type === 'case')), 5), [_case?.items]);
13
+ if (!_case) {
14
+ return _jsx(Skeleton, { height: 240 });
15
+ }
13
16
  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: casePages.length, page: casePage, onChange: (_, page) => setCasePage(page) })] }), _jsx(Divider, {}), casePages[casePage - 1]?.map(item => (_jsxs(Box, { position: "relative", children: [_jsx(CaseCard, { caseId: item.id }), _jsx(Box, { component: Link, to: item.path, sx: {
14
17
  position: 'absolute',
15
18
  top: 0,
@@ -1,5 +1,5 @@
1
1
  import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
- import type { FC } from 'react';
2
+ import { type FC } from 'react';
3
3
  declare const TaskPanel: FC<{
4
4
  case: Case;
5
5
  updateCase: (_case: Partial<Case>) => Promise<void>;
@@ -1,10 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Divider, Stack, Typography } from '@mui/material';
2
+ import { Divider, Skeleton, Stack, Typography } from '@mui/material';
3
+ import {} from 'react';
3
4
  import { useTranslation } from 'react-i18next';
4
5
  import CaseTask from './CaseTask';
5
6
  const TaskPanel = ({ case: _case, updateCase }) => {
6
7
  const { t } = useTranslation();
7
- // TODO: Implement adding tasks, checking tasks off, etc.
8
+ if (!_case) {
9
+ return _jsx(Skeleton, { height: 240 });
10
+ }
8
11
  return (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.tasks') }), _jsx(Divider, {}), _case.tasks.map(task => (_jsx(CaseTask, { task: task, paths: _case.items.map(item => item.path), onEdit: newTask => updateCase({
9
12
  tasks: _case.tasks.map(_task => {
10
13
  if (_task.id !== task.id) {
@@ -0,0 +1,12 @@
1
+ import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
+ import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
3
+ import { type FC } from 'react';
4
+ declare const CaseAggregate: FC<{
5
+ icon?: string;
6
+ iconColor?: string;
7
+ field?: string;
8
+ records?: Partial<Hit | Observable>[];
9
+ title?: string;
10
+ subtitle?: string;
11
+ }>;
12
+ export default CaseAggregate;
@@ -1,30 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Icon } from '@iconify/react';
3
- import { Card, CardContent, Stack, styled, Tooltip, tooltipClasses, Typography, useTheme } from '@mui/material';
4
- import api from '@cccsaurora/howler-ui/api';
5
- import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
+ import { Card, CardContent, Skeleton, Stack, styled, Tooltip, tooltipClasses, Typography, useTheme } from '@mui/material';
6
4
  import { get, isEmpty, uniq } from 'lodash-es';
7
- import { useEffect, useState } from 'react';
5
+ import {} from 'react';
8
6
  const NoMaxWidthTooltip = styled(({ className, ...props }) => (_jsx(Tooltip, { ...props, classes: { popper: className } })))({
9
7
  [`& .${tooltipClasses.tooltip}`]: {
10
8
  maxWidth: 'none'
11
9
  }
12
10
  });
13
- const CaseAggregate = ({ icon, iconColor, field, ids, title, subtitle }) => {
14
- const { dispatchApi } = useMyApi();
11
+ const CaseAggregate = ({ icon, iconColor, field, records, title, subtitle }) => {
15
12
  const theme = useTheme();
16
- const [values, setValues] = useState([]);
17
- useEffect(() => {
18
- if (ids?.length < 1 || !field) {
19
- return;
20
- }
21
- dispatchApi(api.v2.search.post(['hit', 'observable'], {
22
- query: `howler.id:(${ids?.join(' OR ') || '*'})`,
23
- fl: field
24
- })).then(response => {
25
- setValues(uniq(response.items.map(entry => get(entry, field)).flat()));
26
- });
27
- }, [dispatchApi, field, ids]);
13
+ if (!title && (!records || !field)) {
14
+ return _jsx(Skeleton, { height: 120 });
15
+ }
16
+ const values = uniq(records?.map(_record => get(_record, field)).flat());
28
17
  return (_jsx(Card, { sx: { height: '100%' }, children: _jsx(CardContent, { children: _jsxs(Stack, { alignItems: "center", spacing: 1, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [icon && _jsx(Icon, { fontSize: "96px", icon: icon, color: iconColor || theme.palette.grey[700] }), _jsx(NoMaxWidthTooltip, { title: !isEmpty(values) && (_jsx(Stack, { spacing: 0.5, children: values.map(value => (_jsx("span", { children: value }, value))) })), children: _jsxs(Typography, { variant: "h3", children: [values.length, !isEmpty(values) && !!title && ' - ', title] }) })] }), _jsx(Typography, { color: "textSecondary", children: subtitle })] }) }) }));
29
18
  };
30
19
  export default CaseAggregate;
@@ -0,0 +1,6 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ import { type FC } from 'react';
3
+ declare const SourceAggregate: FC<{
4
+ case: Case;
5
+ }>;
6
+ export default SourceAggregate;
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Chip, Grid, Skeleton } from '@mui/material';
3
+ import api from '@cccsaurora/howler-ui/api';
4
+ import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
5
+ import { uniq } from 'lodash-es';
6
+ import { useEffect, useMemo, useState } from 'react';
7
+ import useCase from '../../hooks/useCase';
8
+ const SourceAggregate = ({ case: providedCase }) => {
9
+ const { dispatchApi } = useMyApi();
10
+ const { case: _case } = useCase({ case: providedCase });
11
+ const [analytics, setAnalytics] = useState([]);
12
+ const hitIds = useMemo(() => _case?.items.filter(item => item.type === 'hit').map(item => item.id), [_case?.items]);
13
+ useEffect(() => {
14
+ dispatchApi(api.v2.search.post('hit', { query: `howler.id:(${hitIds.join(' OR ')})`, fl: 'howler.analytic' }))
15
+ .then(response => response?.items.map(hit => hit.howler.analytic) ?? [])
16
+ .then(_analytics => setAnalytics(uniq(_analytics)));
17
+ api.v2.search.facet.post(['hit', 'observable'], {
18
+ query: `howler.id:(${hitIds.join(' OR ')})`,
19
+ fields: ['howler.analytic']
20
+ });
21
+ }, [dispatchApi, hitIds]);
22
+ if (!_case) {
23
+ return _jsx(Skeleton, { height: 12, variant: "rounded" });
24
+ }
25
+ return (_jsx(Grid, { container: true, spacing: 1, children: analytics.map(_analytic => (_jsx(Grid, { item: true, children: _jsx(Chip, { size: "small", label: _analytic }) }, _analytic))) }));
26
+ };
27
+ export default SourceAggregate;
@@ -0,0 +1,13 @@
1
+ import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
2
+ interface CaseArguments {
3
+ case?: Case;
4
+ caseId?: string;
5
+ }
6
+ interface CaseResult {
7
+ case: Case;
8
+ updateCase: (update: Partial<Case>) => Promise<void>;
9
+ loading: boolean;
10
+ missing: boolean;
11
+ }
12
+ declare const useCase: (args: CaseArguments) => CaseResult;
13
+ export default useCase;
@@ -0,0 +1,38 @@
1
+ import api from '@cccsaurora/howler-ui/api';
2
+ import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ const useCase = ({ caseId, case: providedCase }) => {
5
+ const { dispatchApi } = useMyApi();
6
+ const [loading, setLoading] = useState(false);
7
+ const [missing, setMissing] = useState(false);
8
+ const [_case, setCase] = useState(providedCase);
9
+ useEffect(() => {
10
+ if (providedCase) {
11
+ setCase(providedCase);
12
+ }
13
+ }, [providedCase]);
14
+ useEffect(() => {
15
+ if (caseId) {
16
+ setLoading(true);
17
+ dispatchApi(api.v2.case.get(caseId), { throwError: false })
18
+ .then(setCase)
19
+ .finally(() => setLoading(false));
20
+ }
21
+ }, [caseId, dispatchApi]);
22
+ const updateCase = useCallback(async (_updatedCase) => {
23
+ if (!_case?.case_id) {
24
+ return;
25
+ }
26
+ try {
27
+ setCase(await dispatchApi(api.v2.case.put(_case.case_id, _updatedCase)));
28
+ }
29
+ catch (e) {
30
+ setMissing(true);
31
+ }
32
+ finally {
33
+ return;
34
+ }
35
+ }, [_case?.case_id, dispatchApi]);
36
+ return { case: _case, updateCase, loading, missing };
37
+ };
38
+ export default useCase;
@@ -15,7 +15,7 @@ import useMyTheme from '@cccsaurora/howler-ui/components/hooks/useMyTheme';
15
15
  import { useSearchParams } from 'react-router-dom';
16
16
  import hitsData from '@cccsaurora/howler-ui/utils/hit.json';
17
17
  import { sanitizeLuceneQuery } from '@cccsaurora/howler-ui/utils/stringUtils';
18
- import OverviewEditor from './OverviewEditor';
18
+ import MarkdownEditor from '../../elements/MarkdownEditor';
19
19
  import { useStartingTemplate } from './startingTemplate';
20
20
  const OverviewViewer = () => {
21
21
  const theme = useTheme();
@@ -188,7 +188,7 @@ const OverviewViewer = () => {
188
188
  right: `calc(50% + 7px - ${x}px)`,
189
189
  mr: -2.4,
190
190
  pr: 1.5
191
- }, children: _jsx(OverviewEditor, { height: "100%", content: content, setContent: setContent }) }) }), _jsx(Box, { sx: {
191
+ }, children: _jsx(MarkdownEditor, { height: "100%", content: content, setContent: setContent }) }) }), _jsx(Box, { sx: {
192
192
  position: 'absolute',
193
193
  top: 0,
194
194
  bottom: 0,
@@ -333,6 +333,7 @@
333
333
  "owner": "Owner",
334
334
  "page.404.description": "The page you are looking for cannot be found...",
335
335
  "page.404.title": "404: Not found",
336
+ "page.cases.created": "Created",
336
337
  "page.cases.dashboard": "Dashboard",
337
338
  "page.cases.dashboard.alerts": "Alerts",
338
339
  "page.cases.dashboard.duration": "Duration",
@@ -340,6 +341,12 @@
340
341
  "page.cases.dashboard.target": "Targets",
341
342
  "page.cases.dashboard.tasks": "Tasks",
342
343
  "page.cases.dashboard.threat": "Threats",
344
+ "page.cases.detail.participants": "Participants",
345
+ "page.cases.detail.properties": "Properties",
346
+ "page.cases.detail.status": "Status",
347
+ "page.cases.escalation": "Escalation",
348
+ "page.cases.sources": "Sources",
349
+ "page.cases.updated": "Updated",
343
350
  "page.dashboard.title": "Dashboard",
344
351
  "page.documentation.categories": "API Categories",
345
352
  "page.documentation.categories.category": "Category",
@@ -406,6 +413,8 @@
406
413
  "page.user.search.column.groups": "Groups",
407
414
  "page.user.search.column.username": "Username",
408
415
  "page.user.search.prompt": "Search by username, fullname, email or group",
416
+ "pages.cases.detail.participants": "Participants",
417
+ "pages.cases.detail.status": "Status",
409
418
  "password": "New Password",
410
419
  "password.confirm": "Confirm Password",
411
420
  "password.match": "Password and Confirm Password must match",
@@ -337,6 +337,7 @@
337
337
  "owner": "Propriétaire",
338
338
  "page.404.description": "La page que vous recherchez est introuvable ...",
339
339
  "page.404.title": "404: Introuvable",
340
+ "page.cases.created": "Créé",
340
341
  "page.cases.dashboard": "Tableau de bord",
341
342
  "page.cases.dashboard.alerts": "Alertes",
342
343
  "page.cases.dashboard.duration": "Durée",
@@ -344,6 +345,12 @@
344
345
  "page.cases.dashboard.target": "Cibles",
345
346
  "page.cases.dashboard.tasks": "Tâches",
346
347
  "page.cases.dashboard.threat": "Menaces",
348
+ "page.cases.detail.participants": "Participants",
349
+ "page.cases.detail.properties": "Propriétés",
350
+ "page.cases.detail.status": "Statut",
351
+ "page.cases.escalation": "Escalade",
352
+ "page.cases.sources": "Sources",
353
+ "page.cases.updated": "Mis à jour",
347
354
  "page.dashboard.title": "Tableau de bord",
348
355
  "page.documentation.categories": "Catégories d'API",
349
356
  "page.documentation.categories.category": "Catégorie",
@@ -411,6 +418,8 @@
411
418
  "page.user.search.column.groups": "Groupes",
412
419
  "page.user.search.column.username": "Nom d'utilisateur",
413
420
  "page.user.search.prompt": "Rechercher par nom d'utilisateur, nom complet, e-mail ou groupe",
421
+ "pages.cases.detail.participants": "Participants",
422
+ "pages.cases.detail.status": "Statut",
414
423
  "password": "Nouveau mot de passe",
415
424
  "password.confirm": "Confirmer le mot de passe",
416
425
  "password.match": "Le mot de passe et le mot de passe de confirmation doivent correspondre",
@@ -17,6 +17,7 @@ export interface Case {
17
17
  overview?: string;
18
18
  participants?: string[];
19
19
  rules?: Rule[];
20
+ status?: string;
20
21
  start?: string;
21
22
  summary?: string;
22
23
  targets?: string[];
package/package.json CHANGED
@@ -101,140 +101,83 @@
101
101
  "internal-slot": "1.0.7"
102
102
  },
103
103
  "type": "module",
104
- "version": "2.17.0-dev.508",
104
+ "version": "2.17.0-dev.513",
105
105
  "exports": {
106
106
  "./i18n": "./i18n.js",
107
107
  "./index.css": "./index.css",
108
- "./api/*": "./api/*.js",
109
- "./api": "./api/index.js",
110
- "./models/*": "./models/*.js",
111
- "./locales/*.json": "./locales/*.json",
108
+ "./components/*": "./components/*.js",
112
109
  "./branding/*": "./branding/*.js",
110
+ "./tests/*": "./tests/*.js",
111
+ "./commons/*": "./commons/*.js",
113
112
  "./utils/*": "./utils/*.js",
114
113
  "./utils/*.json": "./utils/*.json",
114
+ "./locales/*.json": "./locales/*.json",
115
+ "./api/*": "./api/*.js",
116
+ "./api": "./api/index.js",
117
+ "./plugins/*": "./plugins/*.js",
118
+ "./models/*": "./models/*.js",
115
119
  "./rest/*": "./rest/*.js",
116
120
  "./rest": "./rest/index.js",
117
- "./components/*": "./components/*.js",
118
- "./commons/*": "./commons/*.js",
119
- "./plugins/*": "./plugins/*.js",
120
- "./tests/*": "./tests/*.js",
121
- "./api/analytic/*": "./api/analytic/*.js",
122
- "./api/analytic": "./api/analytic/index.js",
123
- "./api/template/*": "./api/template/*.js",
124
- "./api/template": "./api/template/index.js",
125
- "./api/hit/*": "./api/hit/*.js",
126
- "./api/hit": "./api/hit/index.js",
127
- "./api/notebook/*": "./api/notebook/*.js",
128
- "./api/notebook": "./api/notebook/index.js",
129
- "./api/auth/*": "./api/auth/*.js",
130
- "./api/auth": "./api/auth/index.js",
131
- "./api/search/*": "./api/search/*.js",
132
- "./api/search": "./api/search/index.js",
133
- "./api/dossier/*": "./api/dossier/*.js",
134
- "./api/dossier": "./api/dossier/index.js",
135
- "./api/view/*": "./api/view/*.js",
136
- "./api/view": "./api/view/index.js",
137
- "./api/overview/*": "./api/overview/*.js",
138
- "./api/overview": "./api/overview/index.js",
139
- "./api/v2/*": "./api/v2/*.js",
140
- "./api/v2": "./api/v2/index.js",
141
- "./api/user/*": "./api/user/*.js",
142
- "./api/user": "./api/user/index.js",
143
- "./api/action/*": "./api/action/*.js",
144
- "./api/action": "./api/action/index.js",
145
- "./api/configs/*": "./api/configs/*.js",
146
- "./api/configs": "./api/configs/index.js",
147
- "./api/analytic/comments/*": "./api/analytic/comments/*.js",
148
- "./api/analytic/comments": "./api/analytic/comments/index.js",
149
- "./api/analytic/notebooks/*": "./api/analytic/notebooks/*.js",
150
- "./api/analytic/notebooks": "./api/analytic/notebooks/index.js",
151
- "./api/hit/comments/*": "./api/hit/comments/*.js",
152
- "./api/hit/comments": "./api/hit/comments/index.js",
153
- "./api/search/facet/*": "./api/search/facet/*.js",
154
- "./api/search/facet": "./api/search/facet/index.js",
155
- "./api/search/explain/*": "./api/search/explain/*.js",
156
- "./api/search/count/*": "./api/search/count/*.js",
157
- "./api/search/count": "./api/search/count/index.js",
158
- "./api/search/fields/*": "./api/search/fields/*.js",
159
- "./api/search/fields": "./api/search/fields/index.js",
160
- "./api/search/eql/*": "./api/search/eql/*.js",
161
- "./api/search/sigma/*": "./api/search/sigma/*.js",
162
- "./api/search/grouped/*": "./api/search/grouped/*.js",
163
- "./api/search/grouped": "./api/search/grouped/index.js",
164
- "./api/search/histogram/*": "./api/search/histogram/*.js",
165
- "./api/search/histogram": "./api/search/histogram/index.js",
166
- "./api/v2/case/*": "./api/v2/case/*.js",
167
- "./api/v2/case": "./api/v2/case/index.js",
168
- "./api/v2/search/*": "./api/v2/search/*.js",
169
- "./api/v2/search": "./api/v2/search/index.js",
170
- "./api/user/avatar/*": "./api/user/avatar/*.js",
171
- "./api/user/avatar": "./api/user/avatar/index.js",
172
- "./models/socket/*": "./models/socket/*.js",
173
- "./models/entities/*": "./models/entities/*.js",
174
- "./models/entities/generated/*": "./models/entities/generated/*.js",
175
- "./locales/en/*.json": "./locales/en/*.json",
176
- "./locales/fr/*.json": "./locales/fr/*.json",
177
- "./locales/en/help/*.json": "./locales/en/help/*.json",
178
- "./locales/fr/help/*.json": "./locales/fr/help/*.json",
179
- "./components/hooks/*": "./components/hooks/*.js",
180
- "./components/app/*": "./components/app/*.js",
181
121
  "./components/logins/*": "./components/logins/*.js",
122
+ "./components/app/*": "./components/app/*.js",
182
123
  "./components/elements/*": "./components/elements/*.js",
124
+ "./components/hooks/*": "./components/hooks/*.js",
183
125
  "./components/routes/*": "./components/routes/*.js",
184
- "./components/app/hooks/*": "./components/app/hooks/*.js",
185
- "./components/app/providers/*": "./components/app/providers/*.js",
186
- "./components/app/drawers/*": "./components/app/drawers/*.js",
187
- "./components/logins/hooks/*": "./components/logins/hooks/*.js",
188
126
  "./components/logins/auth/*": "./components/logins/auth/*.js",
189
- "./components/elements/addons/*": "./components/elements/addons/*.js",
127
+ "./components/logins/hooks/*": "./components/logins/hooks/*.js",
128
+ "./components/app/drawers/*": "./components/app/drawers/*.js",
129
+ "./components/app/providers/*": "./components/app/providers/*.js",
130
+ "./components/app/hooks/*": "./components/app/hooks/*.js",
131
+ "./components/elements/display/*": "./components/elements/display/*.js",
190
132
  "./components/elements/hit/*": "./components/elements/hit/*.js",
191
133
  "./components/elements/view/*": "./components/elements/view/*.js",
192
- "./components/elements/display/*": "./components/elements/display/*.js",
134
+ "./components/elements/addons/*": "./components/elements/addons/*.js",
135
+ "./components/elements/display/handlebars/*": "./components/elements/display/handlebars/*.js",
136
+ "./components/elements/display/modals/*": "./components/elements/display/modals/*.js",
137
+ "./components/elements/display/features/*": "./components/elements/display/features/*.js",
138
+ "./components/elements/display/icons/*": "./components/elements/display/icons/*.js",
139
+ "./components/elements/display/json/*": "./components/elements/display/json/*.js",
140
+ "./components/elements/display/markdownPlugins/*.md": "./components/elements/display/markdownPlugins/*.md.js",
141
+ "./components/elements/display/icons/svg/*": "./components/elements/display/icons/svg/*.js",
142
+ "./components/elements/hit/actions/*": "./components/elements/hit/actions/*.js",
143
+ "./components/elements/hit/related/*": "./components/elements/hit/related/*.js",
144
+ "./components/elements/hit/elements/*": "./components/elements/hit/elements/*.js",
145
+ "./components/elements/hit/outlines/*": "./components/elements/hit/outlines/*.js",
146
+ "./components/elements/hit/aggregate/*": "./components/elements/hit/aggregate/*.js",
147
+ "./components/elements/hit/outlines/al/*": "./components/elements/hit/outlines/al/*.js",
193
148
  "./components/elements/addons/buttons/*": "./components/elements/addons/buttons/*.js",
194
149
  "./components/elements/addons/buttons": "./components/elements/addons/buttons/index.js",
195
- "./components/elements/addons/search/*": "./components/elements/addons/search/*.js",
196
- "./components/elements/addons/layout/*": "./components/elements/addons/layout/*.js",
197
150
  "./components/elements/addons/lists/*": "./components/elements/addons/lists/*.js",
198
151
  "./components/elements/addons/lists": "./components/elements/addons/lists/index.js",
152
+ "./components/elements/addons/search/*": "./components/elements/addons/search/*.js",
153
+ "./components/elements/addons/layout/*": "./components/elements/addons/layout/*.js",
154
+ "./components/elements/addons/lists/table/*": "./components/elements/addons/lists/table/*.js",
155
+ "./components/elements/addons/lists/table": "./components/elements/addons/lists/table/index.js",
156
+ "./components/elements/addons/lists/hooks/*": "./components/elements/addons/lists/hooks/*.js",
199
157
  "./components/elements/addons/search/phrase/*": "./components/elements/addons/search/phrase/*.js",
200
158
  "./components/elements/addons/search/phrase": "./components/elements/addons/search/phrase/index.js",
201
159
  "./components/elements/addons/search/phrase/word/*": "./components/elements/addons/search/phrase/word/*.js",
202
160
  "./components/elements/addons/search/phrase/word/consumers/*": "./components/elements/addons/search/phrase/word/consumers/*.js",
203
161
  "./components/elements/addons/layout/vsbox/*": "./components/elements/addons/layout/vsbox/*.js",
204
- "./components/elements/addons/lists/hooks/*": "./components/elements/addons/lists/hooks/*.js",
205
- "./components/elements/addons/lists/table/*": "./components/elements/addons/lists/table/*.js",
206
- "./components/elements/addons/lists/table": "./components/elements/addons/lists/table/index.js",
207
- "./components/elements/hit/aggregate/*": "./components/elements/hit/aggregate/*.js",
208
- "./components/elements/hit/outlines/*": "./components/elements/hit/outlines/*.js",
209
- "./components/elements/hit/related/*": "./components/elements/hit/related/*.js",
210
- "./components/elements/hit/elements/*": "./components/elements/hit/elements/*.js",
211
- "./components/elements/hit/actions/*": "./components/elements/hit/actions/*.js",
212
- "./components/elements/hit/outlines/al/*": "./components/elements/hit/outlines/al/*.js",
213
- "./components/elements/display/icons/*": "./components/elements/display/icons/*.js",
214
- "./components/elements/display/modals/*": "./components/elements/display/modals/*.js",
215
- "./components/elements/display/markdownPlugins/*.md": "./components/elements/display/markdownPlugins/*.md.js",
216
- "./components/elements/display/json/*": "./components/elements/display/json/*.js",
217
- "./components/elements/display/handlebars/*": "./components/elements/display/handlebars/*.js",
218
- "./components/elements/display/features/*": "./components/elements/display/features/*.js",
219
- "./components/elements/display/icons/svg/*": "./components/elements/display/icons/svg/*.js",
220
- "./components/routes/admin/*": "./components/routes/admin/*.js",
221
- "./components/routes/dossiers/*": "./components/routes/dossiers/*.js",
222
- "./components/routes/cases/*": "./components/routes/cases/*.js",
223
- "./components/routes/advanced/*": "./components/routes/advanced/*.js",
224
- "./components/routes/observables/*": "./components/routes/observables/*.js",
225
- "./components/routes/hits/*": "./components/routes/hits/*.js",
226
- "./components/routes/settings/*": "./components/routes/settings/*.js",
227
162
  "./components/routes/home/*": "./components/routes/home/*.js",
228
163
  "./components/routes/home": "./components/routes/home/index.js",
229
- "./components/routes/analytics/*": "./components/routes/analytics/*.js",
230
- "./components/routes/help/*": "./components/routes/help/*.js",
231
164
  "./components/routes/action/*": "./components/routes/action/*.js",
232
- "./components/routes/views/*": "./components/routes/views/*.js",
233
- "./components/routes/overviews/*": "./components/routes/overviews/*.js",
234
165
  "./components/routes/templates/*": "./components/routes/templates/*.js",
235
- "./components/routes/admin/users/*": "./components/routes/admin/users/*.js",
236
- "./components/routes/cases/detail/*": "./components/routes/cases/detail/*.js",
237
- "./components/routes/cases/detail/sidebar/*": "./components/routes/cases/detail/sidebar/*.js",
166
+ "./components/routes/dossiers/*": "./components/routes/dossiers/*.js",
167
+ "./components/routes/overviews/*": "./components/routes/overviews/*.js",
168
+ "./components/routes/views/*": "./components/routes/views/*.js",
169
+ "./components/routes/hits/*": "./components/routes/hits/*.js",
170
+ "./components/routes/analytics/*": "./components/routes/analytics/*.js",
171
+ "./components/routes/advanced/*": "./components/routes/advanced/*.js",
172
+ "./components/routes/help/*": "./components/routes/help/*.js",
173
+ "./components/routes/admin/*": "./components/routes/admin/*.js",
174
+ "./components/routes/settings/*": "./components/routes/settings/*.js",
175
+ "./components/routes/observables/*": "./components/routes/observables/*.js",
176
+ "./components/routes/cases/*": "./components/routes/cases/*.js",
177
+ "./components/routes/action/edit/*": "./components/routes/action/edit/*.js",
178
+ "./components/routes/action/view/*": "./components/routes/action/view/*.js",
179
+ "./components/routes/action/shared/*": "./components/routes/action/shared/*.js",
180
+ "./components/routes/overviews/template/*": "./components/routes/overviews/template/*.js",
238
181
  "./components/routes/hits/search/*": "./components/routes/hits/search/*.js",
239
182
  "./components/routes/hits/view/*": "./components/routes/hits/view/*.js",
240
183
  "./components/routes/hits/search/grid/*": "./components/routes/hits/search/grid/*.js",
@@ -242,34 +185,93 @@
242
185
  "./components/routes/analytics/widgets/*": "./components/routes/analytics/widgets/*.js",
243
186
  "./components/routes/help/components/*": "./components/routes/help/components/*.js",
244
187
  "./components/routes/help/markdown/*.md": "./components/routes/help/markdown/*.md.js",
245
- "./components/routes/help/markdown/en/*.md": "./components/routes/help/markdown/en/*.md.js",
246
188
  "./components/routes/help/markdown/fr/*.md": "./components/routes/help/markdown/fr/*.md.js",
247
- "./components/routes/action/view/*": "./components/routes/action/view/*.js",
248
- "./components/routes/action/shared/*": "./components/routes/action/shared/*.js",
249
- "./components/routes/action/edit/*": "./components/routes/action/edit/*.js",
250
- "./components/routes/overviews/template/*": "./components/routes/overviews/template/*.js",
189
+ "./components/routes/help/markdown/en/*.md": "./components/routes/help/markdown/en/*.md.js",
190
+ "./components/routes/admin/users/*": "./components/routes/admin/users/*.js",
191
+ "./components/routes/cases/hooks/*": "./components/routes/cases/hooks/*.js",
192
+ "./components/routes/cases/detail/*": "./components/routes/cases/detail/*.js",
193
+ "./components/routes/cases/detail/sidebar/*": "./components/routes/cases/detail/sidebar/*.js",
194
+ "./components/routes/cases/detail/aggregates/*": "./components/routes/cases/detail/aggregates/*.js",
251
195
  "./commons/components/*": "./commons/components/*.js",
196
+ "./commons/components/breadcrumbs/*": "./commons/components/breadcrumbs/*.js",
197
+ "./commons/components/app/*": "./commons/components/app/*.js",
252
198
  "./commons/components/utils/*": "./commons/components/utils/*.js",
199
+ "./commons/components/notification/*": "./commons/components/notification/*.js",
200
+ "./commons/components/notification": "./commons/components/notification/index.js",
201
+ "./commons/components/display/*": "./commons/components/display/*.js",
253
202
  "./commons/components/leftnav/*": "./commons/components/leftnav/*.js",
203
+ "./commons/components/search/*": "./commons/components/search/*.js",
254
204
  "./commons/components/pages/*": "./commons/components/pages/*.js",
255
205
  "./commons/components/topnav/*": "./commons/components/topnav/*.js",
256
- "./commons/components/app/*": "./commons/components/app/*.js",
257
- "./commons/components/search/*": "./commons/components/search/*.js",
258
- "./commons/components/breadcrumbs/*": "./commons/components/breadcrumbs/*.js",
259
- "./commons/components/display/*": "./commons/components/display/*.js",
260
- "./commons/components/notification/*": "./commons/components/notification/*.js",
261
- "./commons/components/notification": "./commons/components/notification/index.js",
262
- "./commons/components/utils/hooks/*": "./commons/components/utils/hooks/*.js",
263
- "./commons/components/pages/hooks/*": "./commons/components/pages/hooks/*.js",
206
+ "./commons/components/app/providers/*": "./commons/components/app/providers/*.js",
264
207
  "./commons/components/app/hooks/*": "./commons/components/app/hooks/*.js",
265
208
  "./commons/components/app/hooks": "./commons/components/app/hooks/index.js",
266
- "./commons/components/app/providers/*": "./commons/components/app/providers/*.js",
267
- "./commons/components/display/hooks/*": "./commons/components/display/hooks/*.js",
209
+ "./commons/components/utils/hooks/*": "./commons/components/utils/hooks/*.js",
268
210
  "./commons/components/notification/elements/*": "./commons/components/notification/elements/*.js",
269
211
  "./commons/components/notification/elements/item/*": "./commons/components/notification/elements/item/*.js",
212
+ "./commons/components/display/hooks/*": "./commons/components/display/hooks/*.js",
213
+ "./commons/components/pages/hooks/*": "./commons/components/pages/hooks/*.js",
214
+ "./locales/fr/*.json": "./locales/fr/*.json",
215
+ "./locales/en/*.json": "./locales/en/*.json",
216
+ "./locales/fr/help/*.json": "./locales/fr/help/*.json",
217
+ "./locales/en/help/*.json": "./locales/en/help/*.json",
218
+ "./api/overview/*": "./api/overview/*.js",
219
+ "./api/overview": "./api/overview/index.js",
220
+ "./api/v2/*": "./api/v2/*.js",
221
+ "./api/v2": "./api/v2/index.js",
222
+ "./api/action/*": "./api/action/*.js",
223
+ "./api/action": "./api/action/index.js",
224
+ "./api/auth/*": "./api/auth/*.js",
225
+ "./api/auth": "./api/auth/index.js",
226
+ "./api/notebook/*": "./api/notebook/*.js",
227
+ "./api/notebook": "./api/notebook/index.js",
228
+ "./api/template/*": "./api/template/*.js",
229
+ "./api/template": "./api/template/index.js",
230
+ "./api/analytic/*": "./api/analytic/*.js",
231
+ "./api/analytic": "./api/analytic/index.js",
232
+ "./api/user/*": "./api/user/*.js",
233
+ "./api/user": "./api/user/index.js",
234
+ "./api/dossier/*": "./api/dossier/*.js",
235
+ "./api/dossier": "./api/dossier/index.js",
236
+ "./api/search/*": "./api/search/*.js",
237
+ "./api/search": "./api/search/index.js",
238
+ "./api/configs/*": "./api/configs/*.js",
239
+ "./api/configs": "./api/configs/index.js",
240
+ "./api/hit/*": "./api/hit/*.js",
241
+ "./api/hit": "./api/hit/index.js",
242
+ "./api/view/*": "./api/view/*.js",
243
+ "./api/view": "./api/view/index.js",
244
+ "./api/v2/search/*": "./api/v2/search/*.js",
245
+ "./api/v2/search": "./api/v2/search/index.js",
246
+ "./api/v2/case/*": "./api/v2/case/*.js",
247
+ "./api/v2/case": "./api/v2/case/index.js",
248
+ "./api/analytic/comments/*": "./api/analytic/comments/*.js",
249
+ "./api/analytic/comments": "./api/analytic/comments/index.js",
250
+ "./api/analytic/notebooks/*": "./api/analytic/notebooks/*.js",
251
+ "./api/analytic/notebooks": "./api/analytic/notebooks/index.js",
252
+ "./api/user/avatar/*": "./api/user/avatar/*.js",
253
+ "./api/user/avatar": "./api/user/avatar/index.js",
254
+ "./api/search/eql/*": "./api/search/eql/*.js",
255
+ "./api/search/histogram/*": "./api/search/histogram/*.js",
256
+ "./api/search/histogram": "./api/search/histogram/index.js",
257
+ "./api/search/facet/*": "./api/search/facet/*.js",
258
+ "./api/search/facet": "./api/search/facet/index.js",
259
+ "./api/search/fields/*": "./api/search/fields/*.js",
260
+ "./api/search/fields": "./api/search/fields/index.js",
261
+ "./api/search/grouped/*": "./api/search/grouped/*.js",
262
+ "./api/search/grouped": "./api/search/grouped/index.js",
263
+ "./api/search/sigma/*": "./api/search/sigma/*.js",
264
+ "./api/search/explain/*": "./api/search/explain/*.js",
265
+ "./api/search/count/*": "./api/search/count/*.js",
266
+ "./api/search/count": "./api/search/count/index.js",
267
+ "./api/hit/comments/*": "./api/hit/comments/*.js",
268
+ "./api/hit/comments": "./api/hit/comments/index.js",
270
269
  "./plugins/clue/*": "./plugins/clue/*.js",
271
270
  "./plugins/clue": "./plugins/clue/index.js",
271
+ "./plugins/clue/components/*": "./plugins/clue/components/*.js",
272
272
  "./plugins/clue/locales/*": "./plugins/clue/locales/*.js",
273
- "./plugins/clue/components/*": "./plugins/clue/components/*.js"
273
+ "./models/socket/*": "./models/socket/*.js",
274
+ "./models/entities/*": "./models/entities/*.js",
275
+ "./models/entities/generated/*": "./models/entities/generated/*.js"
274
276
  }
275
277
  }
@@ -1,10 +0,0 @@
1
- import { type FC } from 'react';
2
- declare const CaseAggregate: FC<{
3
- icon?: string;
4
- iconColor?: string;
5
- field?: string;
6
- ids?: string[];
7
- title?: string;
8
- subtitle?: string;
9
- }>;
10
- export default CaseAggregate;