@cccsaurora/howler-ui 2.18.0-dev.648 → 2.18.0-dev.674
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.
- package/api/index.d.ts +2 -0
- package/api/index.js +4 -2
- package/api/search/case.d.ts +4 -0
- package/api/search/case.js +8 -0
- package/api/search/index.d.ts +2 -1
- package/api/search/index.js +2 -1
- package/api/v2/case/index.d.ts +6 -0
- package/api/v2/case/index.js +18 -0
- package/api/v2/index.d.ts +4 -0
- package/api/v2/index.js +6 -0
- package/api/v2/search/facet.d.ts +3 -0
- package/api/v2/search/facet.js +12 -0
- package/api/v2/search/index.d.ts +5 -0
- package/api/v2/search/index.js +24 -0
- package/commons/components/leftnav/LeftNavDrawer.js +1 -1
- package/components/app/App.js +34 -7
- package/components/app/hooks/useMatchers.js +2 -2
- package/components/app/hooks/useMatchers.test.js +22 -22
- package/components/app/hooks/useTitle.js +3 -3
- package/components/app/providers/FavouritesProvider.js +2 -2
- package/components/app/providers/ParameterProvider.d.ts +9 -2
- package/components/app/providers/ParameterProvider.js +165 -240
- package/components/app/providers/ParameterProvider.test.js +307 -14
- package/components/app/providers/RecordProvider.d.ts +23 -0
- package/components/app/providers/{HitProvider.js → RecordProvider.js} +41 -41
- package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
- package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +12 -17
- package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +51 -70
- package/components/elements/ContextMenu.d.ts +56 -0
- package/components/elements/ContextMenu.js +109 -0
- package/components/elements/ContextMenu.test.js +215 -0
- package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
- package/components/elements/ObjectDetails.d.ts +6 -0
- package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
- package/components/elements/PluginTypography.d.ts +2 -1
- package/components/elements/PluginTypography.js +3 -2
- package/components/elements/UserList.d.ts +5 -2
- package/components/elements/UserList.js +14 -5
- package/components/elements/addons/search/phrase/Phrase.js +1 -1
- package/components/elements/case/CaseCard.d.ts +8 -0
- package/components/elements/case/CaseCard.js +39 -0
- package/components/elements/case/CasePreview.d.ts +6 -0
- package/components/elements/case/CasePreview.js +17 -0
- package/components/elements/case/StatusIcon.d.ts +5 -0
- package/components/elements/case/StatusIcon.js +13 -0
- package/components/elements/display/ChipPopper.d.ts +1 -1
- package/components/elements/display/HowlerCard.js +1 -1
- package/components/elements/display/Modal.js +1 -0
- package/components/elements/hit/HitActions.js +4 -4
- package/components/elements/hit/HitBanner.js +28 -48
- package/components/elements/hit/HitCard.js +5 -5
- package/components/elements/hit/HitLabels.js +2 -2
- package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
- package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +10 -4
- package/components/elements/hit/HitSummary.d.ts +2 -1
- package/components/elements/hit/HitSummary.js +6 -5
- package/components/elements/hit/aggregate/HitGraph.js +8 -8
- package/components/elements/hit/elements/AnalyticLink.d.ts +8 -0
- package/components/elements/hit/elements/AnalyticLink.js +22 -0
- package/components/elements/hit/outlines/DefaultOutline.js +1 -1
- package/components/elements/hit/related/RelatedRecords.js +63 -0
- package/components/elements/observable/ObservableCard.d.ts +6 -0
- package/components/elements/observable/ObservableCard.js +23 -0
- package/components/elements/observable/ObservablePreview.d.ts +6 -0
- package/components/elements/observable/ObservablePreview.js +12 -0
- package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
- package/components/elements/{hit/HitComments.js → record/RecordComments.js} +29 -28
- package/components/{routes/hits/search/HitContextMenu.d.ts → elements/record/RecordContextMenu.d.ts} +3 -3
- package/components/elements/record/RecordContextMenu.js +235 -0
- package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
- package/components/{routes/hits/search/HitContextMenu.test.js → elements/record/RecordContextMenu.test.js} +39 -39
- package/components/elements/record/RecordRelated.d.ts +7 -0
- package/components/elements/record/RecordRelated.js +34 -0
- package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
- package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +15 -13
- package/components/elements/view/ViewTitle.js +1 -1
- package/components/hooks/useHitActions.d.ts +1 -1
- package/components/hooks/useHitActions.js +4 -4
- package/components/hooks/useLocalStorage.test.d.ts +1 -0
- package/components/hooks/useLocalStorage.test.js +137 -0
- package/components/hooks/useMyPreferences.js +10 -1
- package/components/hooks/useMySearch.js +2 -2
- package/components/hooks/useMySitemap.js +4 -1
- package/components/hooks/useMyTheme.js +9 -2
- package/components/hooks/useParamState.d.ts +6 -0
- package/components/hooks/useParamState.js +48 -0
- package/components/hooks/useParamState.test.d.ts +1 -0
- package/components/hooks/useParamState.test.js +166 -0
- package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
- package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
- package/components/hooks/useRelatedRecords.d.ts +13 -0
- package/components/hooks/useRelatedRecords.js +32 -0
- package/components/routes/action/edit/ActionEditor.js +2 -2
- package/components/routes/action/view/ActionSearch.js +1 -1
- package/components/routes/advanced/QueryBuilder.js +1 -1
- package/components/routes/advanced/QueryEditor.js +3 -3
- package/components/routes/advanced/historyCompletionProvider.js +3 -3
- package/components/routes/analytics/AnalyticDetails.js +2 -2
- package/components/routes/analytics/AnalyticSearch.js +1 -1
- package/components/routes/cases/CaseViewer.d.ts +2 -0
- package/components/routes/cases/CaseViewer.js +22 -0
- package/components/routes/cases/Cases.d.ts +2 -0
- package/components/routes/cases/Cases.js +101 -0
- package/components/routes/cases/constants.d.ts +5 -0
- package/components/routes/cases/constants.js +5 -0
- package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
- package/components/routes/cases/detail/AlertPanel.js +33 -0
- package/components/routes/cases/detail/CaseAssets.d.ts +12 -0
- package/components/routes/cases/detail/CaseAssets.js +101 -0
- package/components/routes/cases/detail/CaseAssets.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseAssets.test.js +163 -0
- package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
- package/components/routes/cases/detail/CaseDashboard.js +51 -0
- package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
- package/components/routes/cases/detail/CaseDetails.js +61 -0
- package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
- package/components/routes/cases/detail/CaseOverview.js +43 -0
- package/components/routes/cases/detail/CaseSidebar.d.ts +6 -0
- package/components/routes/cases/detail/CaseSidebar.js +61 -0
- package/components/routes/cases/detail/CaseTask.d.ts +11 -0
- package/components/routes/cases/detail/CaseTask.js +57 -0
- package/components/routes/cases/detail/ItemPage.d.ts +6 -0
- package/components/routes/cases/detail/ItemPage.js +99 -0
- package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
- package/components/routes/cases/detail/RelatedCasePanel.js +31 -0
- package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
- package/components/routes/cases/detail/TaskPanel.js +52 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +12 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.js +19 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.js +27 -0
- package/components/routes/cases/detail/assets/Asset.d.ts +14 -0
- package/components/routes/cases/detail/assets/Asset.js +12 -0
- package/components/routes/cases/detail/assets/Asset.test.d.ts +1 -0
- package/components/routes/cases/detail/assets/Asset.test.js +72 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +13 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.js +131 -0
- package/components/routes/cases/detail/sidebar/types.d.ts +3 -0
- package/components/routes/cases/detail/sidebar/utils.d.ts +3 -0
- package/components/routes/cases/detail/sidebar/utils.js +25 -0
- package/components/routes/cases/hooks/useCase.d.ts +13 -0
- package/components/routes/cases/hooks/useCase.js +38 -0
- package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
- package/components/routes/cases/modals/ResolveModal.js +59 -0
- package/components/routes/dossiers/DossierEditor.js +2 -2
- package/components/routes/dossiers/DossierEditor.test.js +1 -1
- package/components/routes/help/ApiDocumentation.js +1 -1
- package/components/routes/help/HitBannerDocumentation.js +1 -0
- package/components/routes/help/HitDocumentation.js +1 -3
- package/components/routes/hits/search/InformationPane.d.ts +1 -0
- package/components/routes/hits/search/InformationPane.js +47 -60
- package/components/routes/hits/search/LayoutSettings.js +3 -3
- package/components/routes/hits/search/QuerySettings.js +2 -1
- package/components/routes/hits/search/QuerySettings.test.js +14 -9
- package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +9 -9
- package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
- package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
- package/components/routes/hits/search/SearchPane.js +26 -49
- package/components/routes/hits/search/ViewLink.js +3 -3
- package/components/routes/hits/search/ViewLink.test.js +8 -8
- package/components/routes/hits/search/grid/AddColumnModal.js +5 -4
- package/components/routes/hits/search/grid/EnhancedCell.d.ts +2 -1
- package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
- package/components/routes/hits/search/grid/HitGrid.js +20 -18
- package/components/routes/hits/search/grid/{HitRow.d.ts → RecordRow.d.ts} +3 -2
- package/components/routes/hits/search/grid/{HitRow.js → RecordRow.js} +10 -8
- package/components/routes/hits/search/shared/IndexPicker.d.ts +2 -0
- package/components/routes/hits/search/shared/IndexPicker.js +20 -0
- package/components/routes/hits/view/HitViewer.js +12 -13
- package/components/routes/home/ViewCard.js +4 -4
- package/components/routes/observables/ObservableViewer.d.ts +7 -0
- package/components/routes/observables/ObservableViewer.js +27 -0
- package/components/routes/overviews/OverviewViewer.js +2 -2
- package/components/routes/views/ViewComposer.js +4 -4
- package/locales/en/translation.json +65 -3
- package/locales/fr/translation.json +63 -3
- package/models/WithMetadata.d.ts +2 -1
- package/models/entities/generated/AttachmentsFile.d.ts +12 -0
- package/models/entities/generated/Case.d.ts +28 -0
- package/models/entities/generated/DestinationOriginal.d.ts +19 -0
- package/models/entities/generated/EmailAttachment.d.ts +8 -0
- package/models/entities/generated/EmailParent.d.ts +19 -0
- package/models/entities/generated/Enrichments.d.ts +7 -0
- package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
- package/models/entities/generated/Hit.d.ts +1 -0
- package/models/entities/generated/Howler.d.ts +0 -4
- package/models/entities/generated/HttpResponse.d.ts +11 -0
- package/models/entities/generated/Item.d.ts +9 -0
- package/models/entities/generated/Observable.d.ts +85 -0
- package/models/entities/generated/ObservableCloud.d.ts +20 -0
- package/models/entities/generated/ObservableDestination.d.ts +23 -0
- package/models/entities/generated/ObservableEmail.d.ts +30 -0
- package/models/entities/generated/ObservableFile.d.ts +36 -0
- package/models/entities/generated/ObservableHowler.d.ts +43 -0
- package/models/entities/generated/ObservableHttp.d.ts +11 -0
- package/models/entities/generated/ObservableObserver.d.ts +21 -0
- package/models/entities/generated/ObservableOrganization.d.ts +7 -0
- package/models/entities/generated/ObservableProcess.d.ts +34 -0
- package/models/entities/generated/ObservableSource.d.ts +23 -0
- package/models/entities/generated/ObservableThreat.d.ts +21 -0
- package/models/entities/generated/ObservableTls.d.ts +12 -0
- package/models/entities/generated/ObserverIngress.d.ts +9 -0
- package/models/entities/generated/Rule.d.ts +2 -10
- package/models/entities/generated/Task.d.ts +10 -0
- package/models/entities/generated/Threat.d.ts +2 -2
- package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
- package/package.json +18 -1
- package/plugins/clue/components/ClueTypography.js +2 -2
- package/plugins/clue/utils.d.ts +2 -1
- package/tests/utils.d.ts +2 -0
- package/tests/utils.js +8 -0
- package/utils/constants.d.ts +3 -3
- package/utils/hitFunctions.d.ts +2 -1
- package/utils/hitFunctions.js +4 -4
- package/utils/typeUtils.d.ts +7 -0
- package/utils/typeUtils.js +27 -0
- package/components/app/providers/HitProvider.d.ts +0 -22
- package/components/elements/display/icons/BundleButton.d.ts +0 -6
- package/components/elements/display/icons/BundleButton.js +0 -32
- package/components/elements/hit/HitRelated.d.ts +0 -6
- package/components/elements/hit/HitRelated.js +0 -7
- package/components/routes/help/BundleDocumentation.d.ts +0 -3
- package/components/routes/help/BundleDocumentation.js +0 -12
- package/components/routes/help/markdown/en/bundles.md.js +0 -1
- package/components/routes/help/markdown/fr/bundles.md.js +0 -1
- package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
- package/components/routes/hits/search/BundleParentMenu.js +0 -32
- package/components/routes/hits/search/BundleScroller.d.ts +0 -2
- package/components/routes/hits/search/BundleScroller.js +0 -6
- package/components/routes/hits/search/HitContextMenu.js +0 -227
- /package/components/app/providers/{HitSearchProvider.test.d.ts → RecordSearchProvider.test.d.ts} +0 -0
- /package/components/{routes/hits/search/HitContextMenu.test.d.ts → elements/ContextMenu.test.d.ts} +0 -0
- /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
- /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
- /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
|
@@ -4,36 +4,37 @@ import { arrayMove, SortableContext, sortableKeyboardCoordinates } from '@dnd-ki
|
|
|
4
4
|
import { FormatIndentDecrease, FormatIndentIncrease, Info, List, Search, TableChart } from '@mui/icons-material';
|
|
5
5
|
import { IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow, ToggleButton, ToggleButtonGroup, Typography, useTheme } from '@mui/material';
|
|
6
6
|
import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
|
|
7
|
-
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
8
|
-
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
9
7
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
8
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
9
|
+
import { RecordSearchContext } from '@cccsaurora/howler-ui/components/app/providers/RecordSearchProvider';
|
|
10
10
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
11
11
|
import DevelopmentBanner from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentBanner';
|
|
12
|
-
import
|
|
12
|
+
import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
|
|
13
13
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
14
|
+
import useRecordSelection from '@cccsaurora/howler-ui/components/hooks/useRecordSelection';
|
|
14
15
|
import { uniq } from 'lodash-es';
|
|
15
16
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
17
|
import { useTranslation } from 'react-i18next';
|
|
17
18
|
import { useContextSelector } from 'use-context-selector';
|
|
18
19
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
19
|
-
import
|
|
20
|
-
import HitQuery from '../HitQuery';
|
|
20
|
+
import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
|
|
21
21
|
import QuerySettings from '../QuerySettings';
|
|
22
|
+
import RecordQuery from '../RecordQuery';
|
|
22
23
|
import AddColumnModal from './AddColumnModal';
|
|
23
24
|
import ColumnHeader from './ColumnHeader';
|
|
24
|
-
import
|
|
25
|
+
import RecordRow from './RecordRow';
|
|
25
26
|
const HitGrid = () => {
|
|
26
27
|
const { t } = useTranslation();
|
|
27
28
|
const theme = useTheme();
|
|
28
29
|
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
29
|
-
const { onClick } =
|
|
30
|
+
const { onClick } = useRecordSelection();
|
|
30
31
|
const { getMatchingAnalytic } = useMatchers();
|
|
31
|
-
const search = useContextSelector(
|
|
32
|
-
const displayType = useContextSelector(
|
|
33
|
-
const setDisplayType = useContextSelector(
|
|
34
|
-
const response = useContextSelector(
|
|
35
|
-
const searching = useContextSelector(
|
|
36
|
-
const selectedHits = useContextSelector(
|
|
32
|
+
const search = useContextSelector(RecordSearchContext, ctx => ctx.search);
|
|
33
|
+
const displayType = useContextSelector(RecordSearchContext, ctx => ctx.displayType);
|
|
34
|
+
const setDisplayType = useContextSelector(RecordSearchContext, ctx => ctx.setDisplayType);
|
|
35
|
+
const response = useContextSelector(RecordSearchContext, ctx => ctx.response);
|
|
36
|
+
const searching = useContextSelector(RecordSearchContext, ctx => ctx.searching);
|
|
37
|
+
const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
|
|
37
38
|
const query = useContextSelector(ParameterContext, ctx => ctx.query);
|
|
38
39
|
const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
|
|
39
40
|
const [collapseMainColumn, setCollapseMainColumn] = useMyLocalStorageItem(StorageKey.GRID_COLLAPSE_COLUMN, false);
|
|
@@ -56,10 +57,11 @@ const HitGrid = () => {
|
|
|
56
57
|
return false;
|
|
57
58
|
}, [selected, selectedHits]);
|
|
58
59
|
useEffect(() => {
|
|
59
|
-
response?.items.forEach(
|
|
60
|
-
if (!analyticIds[
|
|
61
|
-
|
|
60
|
+
response?.items.forEach(record => {
|
|
61
|
+
if (!isHit(record) || analyticIds[record.howler.analytic]) {
|
|
62
|
+
return;
|
|
62
63
|
}
|
|
64
|
+
getMatchingAnalytic(record).then(_analytic => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [record.howler.analytic]: _analytic.analytic_id })));
|
|
63
65
|
});
|
|
64
66
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
65
67
|
}, [analyticIds, response]);
|
|
@@ -117,7 +119,7 @@ const HitGrid = () => {
|
|
|
117
119
|
}
|
|
118
120
|
return selectedElement.id;
|
|
119
121
|
}, []);
|
|
120
|
-
return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), response && (_jsx(SearchTotal, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5 }, variant: "body2", offset: response.offset, pageLength: response.rows, total: response.total }))] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(
|
|
122
|
+
return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), response && (_jsx(SearchTotal, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5 }, variant: "body2", offset: response.offset, pageLength: response.rows, total: response.total }))] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(RecordQuery, { searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
|
|
121
123
|
position: 'absolute',
|
|
122
124
|
left: 0,
|
|
123
125
|
right: 0,
|
|
@@ -127,6 +129,6 @@ const HitGrid = () => {
|
|
|
127
129
|
} }))] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", children: [_jsx(ToggleButton, { value: "list", children: _jsx(List, {}) }), _jsx(ToggleButton, { value: "grid", children: _jsx(TableChart, {}) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", alignItems: "center", children: [_jsx(QuerySettings, { boxSx: { flex: 1 } }), _jsx(AddColumnModal, { columns: columns, addColumn: key => setColumns(uniq([...columns, key])) })] }), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsxs(Table, { sx: { '& td,th': { px: 1, py: 0.25, whiteSpace: 'nowrap' } }, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { sx: {
|
|
128
130
|
borderRight: 'thin solid',
|
|
129
131
|
borderRightColor: 'divider'
|
|
130
|
-
}, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(
|
|
132
|
+
}, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(RecordContextMenu, { Component: TableBody, getSelectedId: getSelectedId, children: [response?.items.map(hit => (_jsx(RecordRow, { record: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn, onClick: onClick }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
|
|
131
133
|
};
|
|
132
134
|
export default HitGrid;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
+
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
2
3
|
declare const _default: import("react").NamedExoticComponent<{
|
|
3
|
-
|
|
4
|
+
record: Hit | Observable;
|
|
4
5
|
analyticIds: Record<string, string>;
|
|
5
6
|
columns: string[];
|
|
6
7
|
columnWidths: Record<string, string>;
|
|
7
8
|
collapseMainColumn: boolean;
|
|
8
|
-
onClick: (e: React.MouseEvent<HTMLDivElement>,
|
|
9
|
+
onClick: (e: React.MouseEvent<HTMLDivElement>, record: Hit | Observable) => void;
|
|
9
10
|
}>;
|
|
10
11
|
export default _default;
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { KeyboardArrowUp } from '@mui/icons-material';
|
|
3
3
|
import { Box, Collapse, IconButton, lighten, Stack, TableCell, TableRow, Typography, useTheme } from '@mui/material';
|
|
4
|
-
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
5
4
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
5
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
6
6
|
import Assigned from '@cccsaurora/howler-ui/components/elements/hit/elements/Assigned';
|
|
7
7
|
import EscalationChip from '@cccsaurora/howler-ui/components/elements/hit/elements/EscalationChip';
|
|
8
8
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
9
9
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
10
|
+
import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
|
|
10
11
|
import { get } from 'lodash-es';
|
|
11
12
|
import { memo, useState } from 'react';
|
|
12
13
|
import { useTranslation } from 'react-i18next';
|
|
13
14
|
import { Link } from 'react-router-dom';
|
|
14
15
|
import { useContextSelector } from 'use-context-selector';
|
|
16
|
+
import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
|
|
15
17
|
import EnhancedCell from './EnhancedCell';
|
|
16
|
-
const
|
|
18
|
+
const RecordRow = ({ record, analyticIds, columns, columnWidths, collapseMainColumn, onClick }) => {
|
|
17
19
|
const theme = useTheme();
|
|
18
20
|
const { t } = useTranslation();
|
|
19
|
-
const selectedHits = useContextSelector(
|
|
21
|
+
const selectedHits = useContextSelector(RecordContext, ctx => ctx.selectedRecords);
|
|
20
22
|
const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
|
|
21
23
|
const [expandRow, setExpandRow] = useState(false);
|
|
22
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { id:
|
|
24
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { id: record.howler.id, onClick: ev => onClick(ev, record), sx: [
|
|
23
25
|
{
|
|
24
26
|
transition: theme.transitions.create('background-color'),
|
|
25
27
|
'&:hover': {
|
|
@@ -27,10 +29,10 @@ const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn, o
|
|
|
27
29
|
backgroundColor: theme.palette.background.paper
|
|
28
30
|
}
|
|
29
31
|
},
|
|
30
|
-
selectedHits.some(_hit => _hit.howler.id ===
|
|
32
|
+
selectedHits.some(_hit => _hit.howler.id === record.howler.id) && {
|
|
31
33
|
backgroundColor: lighten(theme.palette.background.paper, 0.15)
|
|
32
34
|
},
|
|
33
|
-
selected ===
|
|
35
|
+
selected === record.howler.id && {
|
|
34
36
|
backgroundColor: lighten(theme.palette.background.paper, 0.25)
|
|
35
37
|
}
|
|
36
38
|
], children: [_jsx(TableCell, { sx: {
|
|
@@ -45,6 +47,6 @@ const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn, o
|
|
|
45
47
|
e.preventDefault();
|
|
46
48
|
e.stopPropagation();
|
|
47
49
|
setExpandRow(_expanded => !_expanded);
|
|
48
|
-
}, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [_jsx(EscalationChip, { hit:
|
|
50
|
+
}, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [isHit(record) && _jsx(EscalationChip, { hit: record, layout: HitLayout.DENSE, hideLabel: true }), _jsxs(Typography, { sx: { textWrap: 'nowrap', whiteSpace: 'nowrap', fontSize: 'inherit' }, children: [analyticIds[record.howler.analytic] ? (_jsx(Link, { to: `/analytics/${analyticIds[record.howler.analytic]}`, onClick: e => e.stopPropagation(), children: record.howler.analytic })) : (record.howler.analytic), record.howler.detection && ': ', record.howler.detection] }), isHit(record) && record.howler.assignment !== 'unassigned' && (_jsx(Assigned, { hit: record, layout: HitLayout.DENSE, hideLabel: true }))] }) })] }) }), columns.map(col => (_jsx(EnhancedCell, { record: record, className: `col-${col.replaceAll('.', '-')}`, value: get(record, col) ?? t('none'), sx: columnWidths[col] ? { width: columnWidths[col] } : { width: '220px', maxWidth: '300px' }, field: col }, col)))] }, record.howler.id), _jsx(TableRow, { onClick: ev => onClick(ev, record), children: _jsx(TableCell, { colSpan: columns.length + 2, style: { paddingBottom: 0, paddingTop: 0 }, children: _jsx(Collapse, { in: expandRow, unmountOnExit: true, children: _jsx(Box, { width: "100%", maxWidth: "1200px", margin: 1, children: isHit(record) ? (_jsx(HitCard, { id: record.howler.id, layout: HitLayout.NORMAL })) : (_jsx(ObservableCard, { id: record.howler.id, observable: record })) }) }) }) })] }));
|
|
49
51
|
};
|
|
50
|
-
export default memo(
|
|
52
|
+
export default memo(RecordRow);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FilterList } from '@mui/icons-material';
|
|
3
|
+
import { Autocomplete, TextField } from '@mui/material';
|
|
4
|
+
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
5
|
+
import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { useContextSelector } from 'use-context-selector';
|
|
9
|
+
const FILTER_OPTIONS = [
|
|
10
|
+
{ label: 'hit.search.index.hit', value: 'hit' },
|
|
11
|
+
{ label: 'hit.search.index.observable', value: 'observable' }
|
|
12
|
+
];
|
|
13
|
+
const IndexPicker = () => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const indexes = useContextSelector(ParameterContext, ctx => ctx.indexes);
|
|
16
|
+
const setIndexes = useContextSelector(ParameterContext, ctx => ctx.setIndexes);
|
|
17
|
+
const selectedOptions = FILTER_OPTIONS.filter(opt => indexes.includes(opt.value));
|
|
18
|
+
return (_jsx(ChipPopper, { icon: _jsx(FilterList, { fontSize: "small" }), label: selectedOptions.map(opt => t(opt.label)).join(', '), minWidth: "225px", slotProps: { chip: { size: 'small' } }, children: _jsx(Autocomplete, { size: "small", multiple: true, options: FILTER_OPTIONS, value: selectedOptions, onChange: (_ev, values) => values.length > 0 && setIndexes(values.map(val => val.value)), isOptionEqualToValue: (opt, val) => opt.value === val.value, getOptionLabel: opt => t(opt.label), renderInput: params => _jsx(TextField, { ...params }) }) }));
|
|
19
|
+
};
|
|
20
|
+
export default memo(IndexPicker);
|
|
@@ -4,23 +4,22 @@ import { Code, Comment, DataObject, History, LinkSharp, QueryStats, ViewAgenda }
|
|
|
4
4
|
import { Badge, Box, CardContent, Collapse, IconButton, Skeleton, Stack, Tab, Tabs, Tooltip, useMediaQuery, useTheme } from '@mui/material';
|
|
5
5
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
6
6
|
import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
|
|
7
|
-
import {
|
|
7
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
8
8
|
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
9
9
|
import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
|
|
10
|
-
import BundleButton from '@cccsaurora/howler-ui/components/elements/display/icons/BundleButton';
|
|
11
10
|
import SocketBadge from '@cccsaurora/howler-ui/components/elements/display/icons/SocketBadge';
|
|
12
11
|
import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
|
|
13
12
|
import HitActions from '@cccsaurora/howler-ui/components/elements/hit/HitActions';
|
|
14
13
|
import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
|
|
15
|
-
import HitComments from '@cccsaurora/howler-ui/components/elements/hit/HitComments';
|
|
16
|
-
import HitDetails from '@cccsaurora/howler-ui/components/elements/hit/HitDetails';
|
|
17
14
|
import HitLabels from '@cccsaurora/howler-ui/components/elements/hit/HitLabels';
|
|
18
15
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
19
16
|
import HitLinks from '@cccsaurora/howler-ui/components/elements/hit/HitLinks';
|
|
20
17
|
import HitOutline from '@cccsaurora/howler-ui/components/elements/hit/HitOutline';
|
|
21
18
|
import HitOverview from '@cccsaurora/howler-ui/components/elements/hit/HitOverview';
|
|
22
|
-
import
|
|
23
|
-
import
|
|
19
|
+
import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
|
|
20
|
+
import RecordComments from '@cccsaurora/howler-ui/components/elements/record/RecordComments';
|
|
21
|
+
import RecordRelated from '@cccsaurora/howler-ui/components/elements/record/RecordRelated';
|
|
22
|
+
import RecordWorklog from '@cccsaurora/howler-ui/components/elements/record/RecordWorklog';
|
|
24
23
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
25
24
|
import useMyUserList from '@cccsaurora/howler-ui/components/hooks/useMyUserList';
|
|
26
25
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
@@ -44,8 +43,8 @@ const HitViewer = () => {
|
|
|
44
43
|
const isUnderLg = useMediaQuery(theme.breakpoints.down('lg'));
|
|
45
44
|
const [orientation, setOrientation] = useMyLocalStorageItem(StorageKey.VIEWER_ORIENTATION, Orientation.VERTICAL);
|
|
46
45
|
const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
|
|
47
|
-
const getHit = useContextSelector(
|
|
48
|
-
const hit = useContextSelector(
|
|
46
|
+
const getHit = useContextSelector(RecordContext, ctx => ctx.getRecord);
|
|
47
|
+
const hit = useContextSelector(RecordContext, ctx => ctx.records[params.id]);
|
|
49
48
|
const [userIds, setUserIds] = useState(new Set());
|
|
50
49
|
const users = useMyUserList(userIds);
|
|
51
50
|
const [tab, setTab] = useState('details');
|
|
@@ -94,12 +93,12 @@ const HitViewer = () => {
|
|
|
94
93
|
}
|
|
95
94
|
return {
|
|
96
95
|
overview: () => _jsx(HitOverview, { hit: hit }),
|
|
97
|
-
details: () => _jsx(
|
|
98
|
-
hit_comments: () => _jsx(
|
|
96
|
+
details: () => _jsx(ObjectDetails, { obj: hit }),
|
|
97
|
+
hit_comments: () => _jsx(RecordComments, { record: hit, users: users }),
|
|
99
98
|
hit_raw: () => _jsx(JSONViewer, { data: hit }),
|
|
100
99
|
hit_data: () => _jsx(JSONViewer, { data: hit?.howler?.data?.map(entry => tryParse(entry)), collapse: false }),
|
|
101
|
-
hit_worklog: () => _jsx(
|
|
102
|
-
hit_related: () => _jsx(
|
|
100
|
+
hit_worklog: () => _jsx(RecordWorklog, { record: hit, users: users }),
|
|
101
|
+
hit_related: () => _jsx(RecordRelated, { record: hit }),
|
|
103
102
|
...Object.fromEntries(hit?.howler.dossier?.map((lead, index) => ['lead:' + index, () => _jsx(LeadRenderer, { lead: lead, hit: hit })]) ?? []),
|
|
104
103
|
...Object.fromEntries(dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => [
|
|
105
104
|
`external-lead:${dossierIndex}:${leadIndex}`,
|
|
@@ -132,7 +131,7 @@ const HitViewer = () => {
|
|
|
132
131
|
position: 'absolute',
|
|
133
132
|
top: theme.spacing(2),
|
|
134
133
|
right: theme.spacing(-6)
|
|
135
|
-
}, children: [_jsx(Tooltip, { title: t('hit.panel.view.layout'), children: _jsx(IconButton, { onClick: onOrientationChange, children: _jsx(ViewAgenda, { sx: { transition: 'rotate 250ms', rotate: orientation === 'vertical' ? '90deg' : '0deg' } }) }) }), _jsx(SocketBadge, { size: "medium" }), analytic && (_jsx(Tooltip, { title: t('
|
|
134
|
+
}, children: [_jsx(Tooltip, { title: t('hit.panel.view.layout'), children: _jsx(IconButton, { onClick: onOrientationChange, children: _jsx(ViewAgenda, { sx: { transition: 'rotate 250ms', rotate: orientation === 'vertical' ? '90deg' : '0deg' } }) }) }), _jsx(SocketBadge, { size: "medium" }), analytic && (_jsx(Tooltip, { title: t('analytic.open'), children: _jsx(IconButton, { onClick: () => navigate(`/analytics/${analytic.analytic_id}`), children: _jsx(QueryStats, {}) }) }))] }))] }), _jsx(HowlerCard, { sx: [orientation === 'horizontal' && { height: '0px' }], children: _jsx(CardContent, { sx: { padding: 1, position: 'relative' }, children: _jsx(HitActions, { hit: hit, orientation: "vertical" }) }) }), _jsx(Box, { sx: { gridColumn: '1 / span 2', mb: 1 }, children: _jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: { display: 'flex', flexDirection: 'row', pr: 2, alignItems: 'center' }, children: [hasOverview && (_jsx(Tab, { label: t('hit.viewer.overview'), value: "overview", onClick: () => setTab('overview') })), _jsx(Tab, { label: t('hit.viewer.details'), value: "details", onClick: () => setTab('details') }), hit?.howler.dossier?.map((lead, index) => (_jsx(Tab
|
|
136
135
|
// eslint-disable-next-line react/no-array-index-key
|
|
137
136
|
, { label: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [lead.icon && _jsx(Icon, { icon: lead.icon }), _jsx("span", { children: i18n.language === 'en' ? lead.label.en : lead.label.fr })] }), value: 'lead:' + index, onClick: () => setTab('lead:' + index) }, 'lead:' + index))), dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => (_jsx(Tab
|
|
138
137
|
// eslint-disable-next-line react/no-array-index-key
|
|
@@ -3,12 +3,12 @@ import { OpenInNew } from '@mui/icons-material';
|
|
|
3
3
|
import { Card, CardContent, IconButton, Skeleton, Stack, Typography } from '@mui/material';
|
|
4
4
|
import api from '@cccsaurora/howler-ui/api';
|
|
5
5
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
6
|
-
import { useHitContextSelector } from '@cccsaurora/howler-ui/components/app/providers/
|
|
6
|
+
import { useHitContextSelector } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
7
7
|
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
8
8
|
import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
|
|
9
9
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
10
|
+
import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
|
|
10
11
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
11
|
-
import HitContextMenu from '@cccsaurora/howler-ui/components/routes/hits/search/HitContextMenu';
|
|
12
12
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
14
14
|
import { Link, useNavigate } from 'react-router-dom';
|
|
@@ -65,7 +65,7 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
|
|
|
65
65
|
const lastSignature = useRef('');
|
|
66
66
|
const view = useContextSelector(ViewContext, ctx => ctx.views[viewId]);
|
|
67
67
|
const fetchViews = useContextSelector(ViewContext, ctx => ctx.fetchViews);
|
|
68
|
-
const loadHits = useHitContextSelector(ctx => ctx.
|
|
68
|
+
const loadHits = useHitContextSelector(ctx => ctx.loadRecords);
|
|
69
69
|
// Subscribe to hits from HitProvider cache based on current hitIds in the view
|
|
70
70
|
// Uses memoized selector to avoid unnecessary re-renders on unrelated hit updates
|
|
71
71
|
const hits = useSelectHitsByIds(hitIds);
|
|
@@ -151,6 +151,6 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
|
|
|
151
151
|
}
|
|
152
152
|
return selectedElement.id;
|
|
153
153
|
}, []);
|
|
154
|
-
return (_jsx(Card, { variant: "outlined", sx: { height: '100%' }, children: _jsxs(Stack, { spacing: 1, sx: { p: 1, minHeight: 100 }, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h6", children: t(view?.title) || _jsx(Skeleton, { variant: "text", height: "2em", width: "100px" }) }), _jsx(IconButton, { size: "small", component: Link, disabled: !view, to: view ? buildViewUrl(view) : '', onClick: () => onClick(view.query), children: _jsx(OpenInNew, { fontSize: "small" }) })] }), loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { height: 150, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 160, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 140, width: "100%", variant: "rounded" })] })) : hits.length > 0 ? (_jsx(
|
|
154
|
+
return (_jsx(Card, { variant: "outlined", sx: { height: '100%' }, children: _jsxs(Stack, { spacing: 1, sx: { p: 1, minHeight: 100 }, children: [_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(Typography, { variant: "h6", children: t(view?.title) || _jsx(Skeleton, { variant: "text", height: "2em", width: "100px" }) }), _jsx(IconButton, { size: "small", component: Link, disabled: !view, to: view ? buildViewUrl(view) : '', onClick: () => onClick(view.query), children: _jsx(OpenInNew, { fontSize: "small" }) })] }), loading ? (_jsxs(_Fragment, { children: [_jsx(Skeleton, { height: 150, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 160, width: "100%", variant: "rounded" }), _jsx(Skeleton, { height: 140, width: "100%", variant: "rounded" })] })) : hits.length > 0 ? (_jsx(RecordContextMenu, { getSelectedId: getSelectedId, children: hits.map(h => (_jsx(Card, { id: h.howler.id, variant: "outlined", sx: { cursor: 'pointer' }, onClick: () => navigate(`/hits/${h.howler.id}`), children: _jsx(CardContent, { children: _jsx(HitBanner, { layout: HitLayout.DENSE, hit: h }) }) }, h.howler.id))) })) : (_jsx(AppListEmpty, {}))] }) }));
|
|
155
155
|
};
|
|
156
156
|
export default ViewCard;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Skeleton } from '@mui/material';
|
|
3
|
+
import api from '@cccsaurora/howler-ui/api';
|
|
4
|
+
import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
|
|
5
|
+
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
const ObservableViewer = ({ observable: provided, observableId }) => {
|
|
8
|
+
const { dispatchApi } = useMyApi();
|
|
9
|
+
const [observable, setObservable] = useState(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (provided) {
|
|
12
|
+
setObservable(provided);
|
|
13
|
+
}
|
|
14
|
+
}, [provided]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (observableId) {
|
|
17
|
+
dispatchApi(api.v2.search.post('observable', { query: `howler.id:${observableId}`, rows: 1 }), {
|
|
18
|
+
throwError: false
|
|
19
|
+
}).then(res => setObservable(res.items[0]));
|
|
20
|
+
}
|
|
21
|
+
}, [dispatchApi, observableId]);
|
|
22
|
+
if (!observable) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
return _jsx(Box, { p: 1, children: observable ? _jsx(ObjectDetails, { obj: observable }) : _jsx(Skeleton, { height: 120 }) });
|
|
26
|
+
};
|
|
27
|
+
export default ObservableViewer;
|
|
@@ -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
|
|
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(
|
|
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,
|
|
@@ -6,8 +6,8 @@ import { Alert, Checkbox, CircularProgress, LinearProgress, Stack, TextField, To
|
|
|
6
6
|
import api from '@cccsaurora/howler-ui/api';
|
|
7
7
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
8
8
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
9
|
-
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
10
9
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
10
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
11
11
|
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
12
12
|
import CustomButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomButton';
|
|
13
13
|
import FlexPort from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexPort';
|
|
@@ -26,7 +26,7 @@ import { DEFAULT_QUERY, StorageKey } from '@cccsaurora/howler-ui/utils/constants
|
|
|
26
26
|
import { convertDateToLucene } from '@cccsaurora/howler-ui/utils/utils';
|
|
27
27
|
import { buildViewUrl } from '@cccsaurora/howler-ui/utils/viewUtils';
|
|
28
28
|
import ErrorBoundary from '../ErrorBoundary';
|
|
29
|
-
import
|
|
29
|
+
import RecordQuery from '../hits/search/RecordQuery';
|
|
30
30
|
import HitSort from '../hits/search/shared/HitSort';
|
|
31
31
|
import SearchSpan from '../hits/search/shared/SearchSpan';
|
|
32
32
|
const ViewComposer = () => {
|
|
@@ -39,7 +39,7 @@ const ViewComposer = () => {
|
|
|
39
39
|
const editView = useContextSelector(ViewContext, ctx => ctx.editView);
|
|
40
40
|
const getCurrentViews = useContextSelector(ViewContext, ctx => ctx.getCurrentViews);
|
|
41
41
|
const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
|
|
42
|
-
const loadHits = useContextSelector(
|
|
42
|
+
const loadHits = useContextSelector(RecordContext, ctx => ctx.loadRecords);
|
|
43
43
|
// view state
|
|
44
44
|
const [title, setTitle] = useState('');
|
|
45
45
|
const [type, setType] = useState('global');
|
|
@@ -172,6 +172,6 @@ const ViewComposer = () => {
|
|
|
172
172
|
fontSize: '0.9em',
|
|
173
173
|
fontStyle: 'italic',
|
|
174
174
|
mb: 0.5
|
|
175
|
-
}), variant: "body2", children: t('hit.search.prompt') }), _jsx(
|
|
175
|
+
}), variant: "body2", children: t('hit.search.prompt') }), _jsx(RecordQuery, { triggerSearch: search, searching: searching, onChange: (_query, isDirty) => setIsSearchDirty(isDirty) }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(HitSort, {}), _jsx(SearchSpan, { omitCustom: true }), _jsx("div", { style: { flex: 1 } }), _jsxs(Stack, { spacing: 1, direction: "row", alignItems: "center", sx: { flex: '0 !important', minWidth: '300px' }, children: [_jsx(Typography, { component: "span", children: t('view.settings.advance_on_triage') }), _jsx(Tooltip, { title: t('view.settings.advance_on_triage.description'), children: _jsx(HelpOutline, { sx: { fontSize: '16px' } }) }), _jsx(Checkbox, { size: "small", checked: advanceOnTriage, onChange: (_event, checked) => setAdvanceOnTriage(checked) })] })] }), response?.total ? (_jsx(SearchTotal, { total: response.total, pageLength: response.items.length, offset: response.offset, sx: theme => ({ color: theme.palette.text.secondary, fontSize: '0.9em', fontStyle: 'italic' }) })) : null, _jsx(LinearProgress, { sx: [!searching && { opacity: 0 }] })] }) }), _jsx(VSBoxContent, { children: _jsxs(Stack, { spacing: 1, children: [!response?.total && _jsx(AppListEmpty, {}), response?.items.map(hit => (_jsx(HitCard, { id: hit.howler.id, layout: HitLayout.DENSE }, hit.howler.id)))] }) })] }) }) }) }));
|
|
176
176
|
};
|
|
177
177
|
export default ViewComposer;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"*": "All values",
|
|
3
|
+
"Protected B": "Protected B",
|
|
4
|
+
"Unclassified//Official Use Only": "Unclassified//Official Use Only",
|
|
3
5
|
"actions.running": "Action \"{{action}}\" is executing.",
|
|
4
6
|
"actions.succeeded": "Action \"{{action}}\" completed successfully.",
|
|
5
7
|
"add": "Add",
|
|
@@ -11,6 +13,7 @@
|
|
|
11
13
|
"analytic.notebook.error.minFields": "Name and link are required.",
|
|
12
14
|
"analytic.notebook.link": "Link",
|
|
13
15
|
"analytic.notebook.name": "Name",
|
|
16
|
+
"analytic.open": "Open Analytic",
|
|
14
17
|
"any": "Any",
|
|
15
18
|
"api.user.apikey.removed": "API key removed successfully.",
|
|
16
19
|
"api.user.apikey.updated": "New API key added successfully.",
|
|
@@ -79,6 +82,8 @@
|
|
|
79
82
|
"comments.edit.stop": "Stop Editing",
|
|
80
83
|
"comments.edited": "Edited",
|
|
81
84
|
"comments.quote": "Quote Reply",
|
|
85
|
+
"complete": "Complete",
|
|
86
|
+
"crisis": "Crisis",
|
|
82
87
|
"custom": "Custom",
|
|
83
88
|
"date.range.1.day": "The last day",
|
|
84
89
|
"date.range.1.month": "The last month",
|
|
@@ -104,8 +109,11 @@
|
|
|
104
109
|
"drawer.expand": "Expand Menu",
|
|
105
110
|
"duplicates.omitted": "Some duplicate entries have been omitted.",
|
|
106
111
|
"edit": "Edit",
|
|
112
|
+
"event.module": "Event Module",
|
|
113
|
+
"event.type": "Event Type",
|
|
107
114
|
"features.warning.description": "This feature is undergoing active development, and is not yet in a finished state. You may encounter bugs or instability.",
|
|
108
115
|
"features.warning.title": "Feature In Active Development",
|
|
116
|
+
"focus": "Main focus",
|
|
109
117
|
"global": "Global",
|
|
110
118
|
"help.actions.introduction": "Introduction to Actions",
|
|
111
119
|
"help.hit.banner.description": "See the below hit banner example for the hit keys necessary to properly populate it. If you have any additional questions, ask in the HOWLER support channel.",
|
|
@@ -162,16 +170,20 @@
|
|
|
162
170
|
"hit.header.assignment": "Assignment: {{user}}",
|
|
163
171
|
"hit.header.assignment.add": "Assign to a user",
|
|
164
172
|
"hit.header.assignment.change": "Change assignment",
|
|
165
|
-
"hit.header.bundlesize": "{{hits}} hits",
|
|
166
173
|
"hit.header.escalation": "Escalation Level: ",
|
|
167
174
|
"hit.header.indicators": "Indicators",
|
|
168
175
|
"hit.header.rationale": "Rationale",
|
|
176
|
+
"hit.header.related": "{{count}} related records",
|
|
169
177
|
"hit.header.scrutiny": "Scrutiny: ",
|
|
170
178
|
"hit.header.status": "Status: ",
|
|
171
179
|
"hit.header.summary": "Summary",
|
|
172
180
|
"hit.header.target": "Target",
|
|
173
181
|
"hit.header.threat": "Threat",
|
|
182
|
+
"hit.header.view.case": "View case {{id}}",
|
|
183
|
+
"hit.header.view.hit": "View hit {{id}}",
|
|
184
|
+
"hit.header.view.observable": "View observable {{id}}",
|
|
174
185
|
"hit.header.votes": "Votes: ",
|
|
186
|
+
"hit.howler.related": "{{count}} related records",
|
|
175
187
|
"hit.label": "Labels",
|
|
176
188
|
"hit.label.category.assignments": "Category: Assignments - Specifies what analyst or team of analysts is assigned to triaging this hit.",
|
|
177
189
|
"hit.label.category.campaign": "Catergory: Campaign - Identifies ongoing campaigns from specific malware families or phishing initiatives. They should only be considered correct attributions if the hit is promoted to evidence.",
|
|
@@ -187,15 +199,16 @@
|
|
|
187
199
|
"hit.label.edit.add.error.duplicate": "Duplicated label not allowed",
|
|
188
200
|
"hit.label.edit.add.error.empty": "Can't add an empty label",
|
|
189
201
|
"hit.label.edit.add.label": "New label value",
|
|
202
|
+
"hit.label.edit.desc": "Add or remove labels",
|
|
190
203
|
"hit.notebook.confirm.dialog": "A notebook with that name already exists in your environment, do you wish to overwrite it?",
|
|
191
204
|
"hit.notebook.confirm.title": "Overwrite existing notebook?",
|
|
192
205
|
"hit.notebook.error.failToPost": "Failed to send notebook to Jupyterhub, make sure your user environment is running.",
|
|
193
206
|
"hit.notebook.goTo": "Go to Jupyterhub",
|
|
194
207
|
"hit.notebook.select": "Please Select a notebook",
|
|
195
208
|
"hit.notebook.tooltip": "Open in Jupyterhub",
|
|
209
|
+
"hit.open": "Open Hit",
|
|
196
210
|
"hit.overview.missing": "No overview has been created for this hit. In order to create an overview, press the add button to the right.",
|
|
197
211
|
"hit.panel.aggregation.run": "Create Summary",
|
|
198
|
-
"hit.panel.analytic.open": "Open Analytic",
|
|
199
212
|
"hit.panel.bundles.open": "Parent Bundles",
|
|
200
213
|
"hit.panel.bundles.open.prompt": "Open Parent Bundle",
|
|
201
214
|
"hit.panel.close": "Close",
|
|
@@ -208,9 +221,12 @@
|
|
|
208
221
|
"hit.panel.exclude": "Exclude By",
|
|
209
222
|
"hit.panel.hit.noselection": "No hit has been selected",
|
|
210
223
|
"hit.panel.include": "Include By",
|
|
211
|
-
"hit.panel.open": "Open Hit Viewer",
|
|
212
224
|
"hit.panel.view.layout": "Change View Panel",
|
|
213
225
|
"hit.quicksearch": "Search by assignment, analytic, detection or status",
|
|
226
|
+
"hit.related.tab.case": "Cases",
|
|
227
|
+
"hit.related.tab.hit": "Hits",
|
|
228
|
+
"hit.related.tab.links": "Links",
|
|
229
|
+
"hit.related.tab.observable": "Observables",
|
|
214
230
|
"hit.search.aggregate.button": "Create Summary",
|
|
215
231
|
"hit.search.button": "Perform search",
|
|
216
232
|
"hit.search.custom": "Custom Sort",
|
|
@@ -219,6 +235,8 @@
|
|
|
219
235
|
"hit.search.filter.fields": "Fields",
|
|
220
236
|
"hit.search.filter.label": "Lookup Filters",
|
|
221
237
|
"hit.search.filter.values": "Values",
|
|
238
|
+
"hit.search.index.hit": "Hits",
|
|
239
|
+
"hit.search.index.observable": "Observables",
|
|
222
240
|
"hit.search.invalid": "Invalid Query",
|
|
223
241
|
"hit.search.keyboard": "Keyboard shortcuts",
|
|
224
242
|
"hit.search.keyboard.no_shortcuts": "No shortcuts",
|
|
@@ -256,6 +274,7 @@
|
|
|
256
274
|
"hit.summary.subtitle": "Limited to a maximum of 10 000 hits.",
|
|
257
275
|
"hit.summary.title": "Summary of Hits Over Time",
|
|
258
276
|
"hit.summary.zoom.reset": "Reset Zoom",
|
|
277
|
+
"hit.view.overview": "Overview",
|
|
259
278
|
"hit.viewer.aggregate": "Summary",
|
|
260
279
|
"hit.viewer.comments": "Comments",
|
|
261
280
|
"hit.viewer.data": "Raw Data",
|
|
@@ -291,6 +310,8 @@
|
|
|
291
310
|
"modal.action.empty": "Action Name cannot be empty.",
|
|
292
311
|
"modal.action.label": "Action Name",
|
|
293
312
|
"modal.action.title": "Save Action",
|
|
313
|
+
"modal.cases.resolve": "Resolve Case",
|
|
314
|
+
"modal.cases.resolve.description": "When resolving a case, you must either assess all open alerts, or add an assessment to the alerts.",
|
|
294
315
|
"modal.confirm.delete.description": "Are you sure you want to delete this item?",
|
|
295
316
|
"modal.confirm.delete.title": "Confirm Deletion",
|
|
296
317
|
"modal.rationale.description": "Provide a rationale that succinctly explains to other analysts why you are confident in this assessment.",
|
|
@@ -312,6 +333,8 @@
|
|
|
312
333
|
"modal.rule.title": "Create a New Rule",
|
|
313
334
|
"no.data": "No Data",
|
|
314
335
|
"none": "None",
|
|
336
|
+
"normal": "Normal Priority",
|
|
337
|
+
"observable.open": "Open Observable",
|
|
315
338
|
"on": "on",
|
|
316
339
|
"open": "Open",
|
|
317
340
|
"operations.add_label": "Add Label",
|
|
@@ -331,6 +354,33 @@
|
|
|
331
354
|
"owner": "Owner",
|
|
332
355
|
"page.404.description": "The page you are looking for cannot be found...",
|
|
333
356
|
"page.404.title": "404: Not found",
|
|
357
|
+
"page.cases.assets": "Assets",
|
|
358
|
+
"page.cases.assets.empty": "No assets found.",
|
|
359
|
+
"page.cases.assets.filter_by_type": "Filter by type:",
|
|
360
|
+
"page.cases.assets.seen_in": "Seen in",
|
|
361
|
+
"page.cases.assets.type.hash": "Hash",
|
|
362
|
+
"page.cases.assets.type.hosts": "Host",
|
|
363
|
+
"page.cases.assets.type.id": "ID",
|
|
364
|
+
"page.cases.assets.type.ids": "ID",
|
|
365
|
+
"page.cases.assets.type.ip": "IP",
|
|
366
|
+
"page.cases.assets.type.signature": "Signature",
|
|
367
|
+
"page.cases.assets.type.uri": "URI",
|
|
368
|
+
"page.cases.assets.type.user": "User",
|
|
369
|
+
"page.cases.created": "Created",
|
|
370
|
+
"page.cases.dashboard": "Dashboard",
|
|
371
|
+
"page.cases.dashboard.alerts": "Alerts",
|
|
372
|
+
"page.cases.dashboard.cases": "Related Cases",
|
|
373
|
+
"page.cases.dashboard.duration": "Duration",
|
|
374
|
+
"page.cases.dashboard.indicators": "Indicators",
|
|
375
|
+
"page.cases.dashboard.target": "Targets",
|
|
376
|
+
"page.cases.dashboard.tasks": "Tasks",
|
|
377
|
+
"page.cases.dashboard.threat": "Threats",
|
|
378
|
+
"page.cases.detail.participants": "Participants",
|
|
379
|
+
"page.cases.detail.properties": "Properties",
|
|
380
|
+
"page.cases.detail.status": "Status",
|
|
381
|
+
"page.cases.escalation": "Escalation",
|
|
382
|
+
"page.cases.sources": "Sources",
|
|
383
|
+
"page.cases.updated": "Updated",
|
|
334
384
|
"page.dashboard.settings.edit": "Edit Dashboard",
|
|
335
385
|
"page.dashboard.settings.refreshRate": "Refresh Rate",
|
|
336
386
|
"page.dashboard.title": "Dashboard",
|
|
@@ -355,6 +405,7 @@
|
|
|
355
405
|
"page.help": "Help",
|
|
356
406
|
"page.help.title": "Help Dashboard",
|
|
357
407
|
"page.login.button": "Sign in",
|
|
408
|
+
"page.login.error": "Just type anything in the username and password fields...",
|
|
358
409
|
"page.login.password": "Password",
|
|
359
410
|
"page.login.username": "Username",
|
|
360
411
|
"page.logout": "Logging out current user ... ",
|
|
@@ -401,6 +452,8 @@
|
|
|
401
452
|
"page.user.search.column.groups": "Groups",
|
|
402
453
|
"page.user.search.column.username": "Username",
|
|
403
454
|
"page.user.search.prompt": "Search by username, fullname, email or group",
|
|
455
|
+
"pages.cases.detail.participants": "Participants",
|
|
456
|
+
"pages.cases.detail.status": "Status",
|
|
404
457
|
"password": "New Password",
|
|
405
458
|
"password.confirm": "Confirm Password",
|
|
406
459
|
"password.match": "Password and Confirm Password must match",
|
|
@@ -414,6 +467,7 @@
|
|
|
414
467
|
"personalization.showbreadcrumbs": "Show Breadcrumbs",
|
|
415
468
|
"personalization.sticky": "Sticky Topbar",
|
|
416
469
|
"query": "Query",
|
|
470
|
+
"query.invalid": "Invalid query",
|
|
417
471
|
"quicksearch.aria": "search",
|
|
418
472
|
"quicksearch.placeholder": "Search ...",
|
|
419
473
|
"rationale.default": "Hit assessed as {{assessment}}",
|
|
@@ -529,6 +583,11 @@
|
|
|
529
583
|
"route.analytics.triage.title": "Triage Settings",
|
|
530
584
|
"route.analytics.updated": "Analytic Updated!",
|
|
531
585
|
"route.analytics.view": "View Analytic",
|
|
586
|
+
"route.cases": "Cases",
|
|
587
|
+
"route.cases.create": "Create Case",
|
|
588
|
+
"route.cases.manager.search": "Search Cases",
|
|
589
|
+
"route.cases.search.prompt": "Search Cases via title, summary or indicators",
|
|
590
|
+
"route.cases.view": "View Case",
|
|
532
591
|
"route.clear": "Clear query",
|
|
533
592
|
"route.dossiers": "Dossiers",
|
|
534
593
|
"route.dossiers.create": "New Dossier",
|
|
@@ -591,8 +650,10 @@
|
|
|
591
650
|
"route.help.actions": "Action Documentation",
|
|
592
651
|
"route.help.api": "API Documentation",
|
|
593
652
|
"route.help.auth": "Authentication",
|
|
653
|
+
"route.help.bundles": "Hit Bundles",
|
|
594
654
|
"route.help.client": "Howler Client",
|
|
595
655
|
"route.help.hit": "Hit Documentation",
|
|
656
|
+
"route.help.hit.banner": "Hit Banner Documentation",
|
|
596
657
|
"route.help.main": "Dashboard",
|
|
597
658
|
"route.help.notebook": "Notebook Documentation",
|
|
598
659
|
"route.help.overviews": "Overviews",
|
|
@@ -705,6 +766,7 @@
|
|
|
705
766
|
"search.result.showing": "Showing {{offset}} to {{length}} of {{total}} results",
|
|
706
767
|
"search.result.showing.single": "No results",
|
|
707
768
|
"search.total": "There are a total of {{count}} hits matching this query.",
|
|
769
|
+
"started": "Started",
|
|
708
770
|
"templates.edit.analytic": "Edit analytic template",
|
|
709
771
|
"templates.edit.detection": "Edit detection template",
|
|
710
772
|
"to": "to",
|