@cccsaurora/howler-ui 2.19.0-cases.1065 → 2.19.0-cases.1097
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/v2/search/index.d.ts +2 -1
- package/api/v2/search/index.js +2 -2
- package/components/app/providers/ParameterProvider.d.ts +1 -1
- package/components/app/providers/ParameterProvider.test.js +25 -25
- package/components/app/providers/RecordProvider.d.ts +6 -6
- package/components/app/providers/RecordProvider.js +5 -1
- package/components/app/providers/RecordSearchProvider.d.ts +2 -2
- package/components/elements/ObjectDetails.d.ts +2 -2
- package/components/elements/PluginTypography.d.ts +2 -2
- package/components/elements/event/EventCard.d.ts +6 -0
- package/components/elements/{observable/ObservableCard.js → event/EventCard.js} +7 -7
- package/components/elements/{observable/ObservablePreview.d.ts → event/EventPreview.d.ts} +2 -2
- package/components/elements/event/EventPreview.js +12 -0
- package/components/elements/hit/HitCard.d.ts +2 -0
- package/components/elements/hit/HitCard.js +2 -2
- package/components/elements/hit/HitSummary.d.ts +2 -2
- package/components/elements/hit/related/RelatedRecords.js +4 -4
- package/components/elements/record/RecordComments.d.ts +2 -2
- package/components/elements/record/RecordRelated.d.ts +2 -2
- package/components/elements/record/RecordRelated.js +4 -4
- package/components/elements/record/RecordWorklog.d.ts +2 -2
- package/components/hooks/useRelatedRecords.d.ts +3 -3
- package/components/hooks/useRelatedRecords.js +2 -2
- package/components/routes/cases/detail/AlertPanel.test.d.ts +1 -0
- package/components/routes/cases/detail/AlertPanel.test.js +110 -0
- package/components/routes/cases/detail/CaseAssets.d.ts +2 -2
- package/components/routes/cases/detail/CaseAssets.js +2 -2
- package/components/routes/cases/detail/CaseAssets.test.js +4 -4
- package/components/routes/cases/detail/CaseDashboard.js +2 -2
- package/components/routes/cases/detail/CaseDashboard.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseDashboard.test.js +218 -0
- package/components/routes/cases/detail/CaseRules.test.js +2 -2
- package/components/routes/cases/detail/CaseTimeline.js +5 -5
- package/components/routes/cases/detail/CaseTimeline.test.js +7 -7
- package/components/routes/cases/detail/CreateRuleDialog.js +4 -4
- package/components/routes/cases/detail/CreateRuleDialog.test.js +5 -5
- package/components/routes/cases/detail/ItemPage.js +1 -1
- package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +2 -2
- package/components/routes/cases/detail/assets/Asset.d.ts +1 -1
- package/components/routes/cases/detail/assets/Asset.test.js +2 -2
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +1 -1
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +3 -3
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +8 -8
- package/components/routes/cases/detail/sidebar/FolderEntry.js +1 -1
- package/components/routes/cases/modals/AddToCaseModal.d.ts +2 -2
- package/components/routes/cases/modals/AddToCaseModal.test.js +11 -11
- package/components/routes/cases/modals/CaseRecordRow.js +1 -1
- package/components/routes/cases/modals/CreateCaseModal.d.ts +2 -2
- package/components/routes/cases/modals/CreateCaseModal.test.js +14 -14
- package/components/routes/cases/modals/hooks.d.ts +3 -3
- package/components/routes/cases/modals/hooks.js +1 -1
- package/components/routes/cases/modals/types.d.ts +4 -1
- package/components/routes/events/EventViewer.d.ts +7 -0
- package/components/routes/events/EventViewer.js +27 -0
- package/components/routes/hits/search/SearchPane.js +3 -3
- package/components/routes/hits/search/grid/EnhancedCell.d.ts +2 -2
- package/components/routes/hits/search/grid/RecordRow.d.ts +3 -3
- package/components/routes/hits/search/grid/RecordRow.js +2 -2
- package/components/routes/hits/search/shared/IndexPicker.js +1 -1
- package/components/routes/home/ViewCard.js +4 -4
- package/components/routes/views/ViewComposer.js +2 -2
- package/locales/en/translation.json +4 -4
- package/locales/fr/translation.json +4 -4
- package/models/entities/generated/ECSEvent.d.ts +31 -0
- package/models/entities/generated/Event.d.ts +80 -26
- package/models/entities/generated/{ObservableCloud.d.ts → EventCloud.d.ts} +1 -1
- package/models/entities/generated/{ObservableDestination.d.ts → EventDestination.d.ts} +1 -1
- package/models/entities/generated/{ObservableEmail.d.ts → EventEmail.d.ts} +1 -1
- package/models/entities/generated/{ObservableFile.d.ts → EventFile.d.ts} +1 -1
- package/models/entities/generated/{ObservableHowler.d.ts → EventHowler.d.ts} +1 -1
- package/models/entities/generated/{ObservableHttp.d.ts → EventHttp.d.ts} +1 -1
- package/models/entities/generated/{ObservableObserver.d.ts → EventObserver.d.ts} +1 -1
- package/models/entities/generated/{ObservableOrganization.d.ts → EventOrganization.d.ts} +1 -1
- package/models/entities/generated/{ObservableProcess.d.ts → EventProcess.d.ts} +1 -1
- package/models/entities/generated/{ObservableSource.d.ts → EventSource.d.ts} +1 -1
- package/models/entities/generated/{ObservableThreat.d.ts → EventThreat.d.ts} +1 -1
- package/models/entities/generated/{ObservableTls.d.ts → EventTls.d.ts} +1 -1
- package/models/entities/generated/Hit.d.ts +2 -2
- package/models/entities/generated/Rule.d.ts +1 -1
- package/package.json +3 -3
- package/plugins/clue/utils.d.ts +2 -2
- package/tests/server-handlers.js +1 -1
- package/tests/utils.d.ts +2 -2
- package/tests/utils.js +3 -3
- package/utils/hitFunctions.d.ts +2 -2
- package/utils/typeUtils.d.ts +2 -2
- package/utils/typeUtils.js +2 -2
- package/components/elements/observable/ObservableCard.d.ts +0 -6
- package/components/elements/observable/ObservablePreview.js +0 -12
- package/components/routes/observables/ObservableViewer.d.ts +0 -7
- package/components/routes/observables/ObservableViewer.js +0 -27
- package/models/entities/generated/Observable.d.ts +0 -85
|
@@ -49,13 +49,13 @@ describe('Asset', () => {
|
|
|
49
49
|
const _case = createMockCase({
|
|
50
50
|
items: [
|
|
51
51
|
{ path: 'alerts/my-analytic (hit-001)', type: 'hit', value: 'hit-001' },
|
|
52
|
-
{ path: '
|
|
52
|
+
{ path: 'events/obs-002', type: 'event', value: 'obs-002' },
|
|
53
53
|
{ path: 'alerts/other-analytic (hit-003)', type: 'hit', value: 'hit-003' }
|
|
54
54
|
]
|
|
55
55
|
});
|
|
56
56
|
render(_jsx(MemoryRouter, { children: _jsx(Asset, { asset: makeAsset({ seenIn: ['hit-001', 'obs-002', 'hit-003'] }), case: _case }) }));
|
|
57
57
|
expect(screen.getByText('alerts/my-analytic (hit-001)')).toBeTruthy();
|
|
58
|
-
expect(screen.getByText('
|
|
58
|
+
expect(screen.getByText('events/obs-002')).toBeTruthy();
|
|
59
59
|
expect(screen.getByText('alerts/other-analytic (hit-003)')).toBeTruthy();
|
|
60
60
|
});
|
|
61
61
|
it('links each chip to /cases/:case_id/:path', () => {
|
|
@@ -10,7 +10,7 @@ export declare const collectAllLeaves: (tree: Tree) => Item[];
|
|
|
10
10
|
* Returns the URL to open for a given leaf item, or null if no URL applies.
|
|
11
11
|
* - reference: the item's value (an external URL)
|
|
12
12
|
* - hit: /hits/<id>
|
|
13
|
-
* -
|
|
13
|
+
* - event: /events/<id>
|
|
14
14
|
* - case: /cases/<id>
|
|
15
15
|
* - table / lead: null (no dedicated detail page)
|
|
16
16
|
*/
|
|
@@ -21,7 +21,7 @@ export const collectAllLeaves = (tree) => {
|
|
|
21
21
|
* Returns the URL to open for a given leaf item, or null if no URL applies.
|
|
22
22
|
* - reference: the item's value (an external URL)
|
|
23
23
|
* - hit: /hits/<id>
|
|
24
|
-
* -
|
|
24
|
+
* - event: /events/<id>
|
|
25
25
|
* - case: /cases/<id>
|
|
26
26
|
* - table / lead: null (no dedicated detail page)
|
|
27
27
|
*/
|
|
@@ -33,8 +33,8 @@ export const getOpenUrl = (leaf) => {
|
|
|
33
33
|
if (type === 'hit') {
|
|
34
34
|
return leaf.value ? `/hits/${leaf.value}` : null;
|
|
35
35
|
}
|
|
36
|
-
if (type === '
|
|
37
|
-
return leaf.value ? `/
|
|
36
|
+
if (type === 'event') {
|
|
37
|
+
return leaf.value ? `/events/${leaf.value}` : null;
|
|
38
38
|
}
|
|
39
39
|
if (type === 'case') {
|
|
40
40
|
return leaf.value ? `/cases/${leaf.value}` : null;
|
|
@@ -54,7 +54,7 @@ import CaseFolderContextMenu, { collectAllLeaves, getOpenUrl } from './CaseFolde
|
|
|
54
54
|
const mockCase = { case_id: 'case-1', title: 'Test Case', items: [] };
|
|
55
55
|
const hitLeaf = { type: 'hit', value: 'hit-123', path: 'folder/hit-item' };
|
|
56
56
|
const referenceLeaf = { type: 'reference', value: 'https://example.com', path: 'folder/ref-item' };
|
|
57
|
-
const
|
|
57
|
+
const eventLeaf = { type: 'event', value: 'obs-456', path: 'folder/event-item' };
|
|
58
58
|
const caseLeaf = { type: 'case', value: 'nested-case-id', path: 'folder/case-item' };
|
|
59
59
|
const tableLeaf = { type: 'table', value: 'table-789', path: 'folder/table-item' };
|
|
60
60
|
const leadLeaf = { type: 'lead', value: 'lead-999', path: 'folder/lead-item' };
|
|
@@ -119,8 +119,8 @@ describe('getOpenUrl', () => {
|
|
|
119
119
|
it('returns /hits/<id> for a hit item', () => {
|
|
120
120
|
expect(getOpenUrl(hitLeaf)).toBe('/hits/hit-123');
|
|
121
121
|
});
|
|
122
|
-
it('returns /
|
|
123
|
-
expect(getOpenUrl(
|
|
122
|
+
it('returns /events/<id> for an event item', () => {
|
|
123
|
+
expect(getOpenUrl(eventLeaf)).toBe('/events/obs-456');
|
|
124
124
|
});
|
|
125
125
|
it('returns /cases/<id> for a case item', () => {
|
|
126
126
|
expect(getOpenUrl(caseLeaf)).toBe('/cases/nested-case-id');
|
|
@@ -159,8 +159,8 @@ describe('CaseFolderContextMenu', () => {
|
|
|
159
159
|
expect(screen.getByTestId('open-item')).toBeInTheDocument();
|
|
160
160
|
expect(screen.getByTestId('remove-item')).toBeInTheDocument();
|
|
161
161
|
});
|
|
162
|
-
it('shows "Open item" and "Remove item" for an
|
|
163
|
-
renderMenu({ leaf:
|
|
162
|
+
it('shows "Open item" and "Remove item" for an event leaf', () => {
|
|
163
|
+
renderMenu({ leaf: eventLeaf });
|
|
164
164
|
expect(screen.getByTestId('open-item')).toBeInTheDocument();
|
|
165
165
|
expect(screen.getByTestId('remove-item')).toBeInTheDocument();
|
|
166
166
|
});
|
|
@@ -219,12 +219,12 @@ describe('CaseFolderContextMenu', () => {
|
|
|
219
219
|
});
|
|
220
220
|
expect(window.open).toHaveBeenCalledWith('https://example.com', '_blank', 'noopener noreferrer');
|
|
221
221
|
});
|
|
222
|
-
it('calls window.open with the
|
|
223
|
-
renderMenu({ leaf:
|
|
222
|
+
it('calls window.open with the event URL', () => {
|
|
223
|
+
renderMenu({ leaf: eventLeaf });
|
|
224
224
|
act(() => {
|
|
225
225
|
fireEvent.click(screen.getByTestId('open-item'));
|
|
226
226
|
});
|
|
227
|
-
expect(window.open).toHaveBeenCalledWith('/
|
|
227
|
+
expect(window.open).toHaveBeenCalledWith('/events/obs-456', '_blank', 'noopener noreferrer');
|
|
228
228
|
});
|
|
229
229
|
it('calls window.open with the case URL', () => {
|
|
230
230
|
renderMenu({ leaf: caseLeaf });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
|
|
1
2
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
3
3
|
import { type FC } from 'react';
|
|
4
4
|
declare const AddToCaseModal: FC<{
|
|
5
|
-
records: (Hit |
|
|
5
|
+
records: (Hit | Event)[];
|
|
6
6
|
}>;
|
|
7
7
|
export default AddToCaseModal;
|
|
@@ -5,7 +5,7 @@ import userEvent, {} from '@testing-library/user-event';
|
|
|
5
5
|
import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
|
|
6
6
|
import i18n from '@cccsaurora/howler-ui/i18n';
|
|
7
7
|
import { I18nextProvider } from 'react-i18next';
|
|
8
|
-
import { createMockCase,
|
|
8
|
+
import { createMockCase, createMockEvent, createMockHit } from '@cccsaurora/howler-ui/tests/utils';
|
|
9
9
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
10
10
|
import AddToCaseModal from './AddToCaseModal';
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
@@ -49,7 +49,7 @@ const MOCK_HIT_1 = createMockHit({
|
|
|
49
49
|
const MOCK_HIT_2 = createMockHit({
|
|
50
50
|
howler: { id: 'hit-002', analytic: 'AnalyticTwo', status: 'open' }
|
|
51
51
|
});
|
|
52
|
-
const
|
|
52
|
+
const MOCK_EVENT = createMockEvent({
|
|
53
53
|
howler: { id: 'obs-001' }
|
|
54
54
|
});
|
|
55
55
|
// ---------------------------------------------------------------------------
|
|
@@ -114,12 +114,12 @@ describe('AddToCaseModal', () => {
|
|
|
114
114
|
const titleInput = screen.getByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
115
115
|
expect(titleInput).toHaveValue(`${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`);
|
|
116
116
|
});
|
|
117
|
-
it('pre-populates title for an
|
|
118
|
-
renderModal([
|
|
117
|
+
it('pre-populates title for an event with Event and id', async () => {
|
|
118
|
+
renderModal([MOCK_EVENT]);
|
|
119
119
|
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalled());
|
|
120
120
|
await selectCase(user, 'Case Alpha');
|
|
121
121
|
const titleInput = screen.getByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
122
|
-
expect(titleInput).toHaveValue(`
|
|
122
|
+
expect(titleInput).toHaveValue(`Event (${MOCK_EVENT.howler.id})`);
|
|
123
123
|
});
|
|
124
124
|
it('title input is editable', async () => {
|
|
125
125
|
renderModal([MOCK_HIT_1]);
|
|
@@ -161,13 +161,13 @@ describe('AddToCaseModal', () => {
|
|
|
161
161
|
expect(titleInputs[0]).toHaveValue('Edited');
|
|
162
162
|
expect(titleInputs[1]).toHaveValue(`${MOCK_HIT_2.howler.analytic} (${MOCK_HIT_2.howler.id})`);
|
|
163
163
|
});
|
|
164
|
-
it('mixed hit and
|
|
165
|
-
renderModal([MOCK_HIT_1,
|
|
164
|
+
it('mixed hit and event records each get correct default titles', async () => {
|
|
165
|
+
renderModal([MOCK_HIT_1, MOCK_EVENT]);
|
|
166
166
|
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalled());
|
|
167
167
|
await selectCase(user, 'Case Alpha');
|
|
168
168
|
const titleInputs = screen.getAllByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
169
169
|
expect(titleInputs[0]).toHaveValue(`${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`);
|
|
170
|
-
expect(titleInputs[1]).toHaveValue(`
|
|
170
|
+
expect(titleInputs[1]).toHaveValue(`Event (${MOCK_EVENT.howler.id})`);
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
173
|
// -------------------------------------------------------------------------
|
|
@@ -260,14 +260,14 @@ describe('AddToCaseModal', () => {
|
|
|
260
260
|
path: `investigations/${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`
|
|
261
261
|
}));
|
|
262
262
|
});
|
|
263
|
-
it('uses
|
|
263
|
+
it('uses event __index for event records', async () => {
|
|
264
264
|
const api = (await import('api')).default;
|
|
265
|
-
renderModal([
|
|
265
|
+
renderModal([MOCK_EVENT]);
|
|
266
266
|
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalled());
|
|
267
267
|
await selectCase(user, 'Case Alpha');
|
|
268
268
|
await user.click(screen.getByRole('button', { name: i18n.t('confirm') }));
|
|
269
269
|
await waitFor(() => expect(mockClose).toHaveBeenCalled());
|
|
270
|
-
expect(api.v2.case.items.post).toHaveBeenCalledWith('case-a', expect.objectContaining({ type: '
|
|
270
|
+
expect(api.v2.case.items.post).toHaveBeenCalledWith('case-a', expect.objectContaining({ type: 'event' }));
|
|
271
271
|
});
|
|
272
272
|
});
|
|
273
273
|
// -------------------------------------------------------------------------
|
|
@@ -10,6 +10,6 @@ const CaseRecordRow = ({ entry, folderOptions = [], onTitleChange, onPathChange
|
|
|
10
10
|
const { record, path, title } = entry;
|
|
11
11
|
const fullPath = path ? `${path}/${title}` : title;
|
|
12
12
|
const pathError = path.startsWith('/') || path.endsWith('/');
|
|
13
|
-
return (_jsxs(Accordion, { variant: "outlined", defaultExpanded: true, sx: { flexShrink: 0 }, children: [_jsx(AccordionSummary, { expandIcon: _jsx(KeyboardArrowDown, {}), sx: { px: 1, minHeight: '48px !important', '& > *': { margin: '0 !important' } }, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, width: "100%", children: [_jsx(Typography, { variant: "body2", fontWeight: 500, sx: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: isHit(record) ? record.howler.analytic : '
|
|
13
|
+
return (_jsxs(Accordion, { variant: "outlined", defaultExpanded: true, sx: { flexShrink: 0 }, children: [_jsx(AccordionSummary, { expandIcon: _jsx(KeyboardArrowDown, {}), sx: { px: 1, minHeight: '48px !important', '& > *': { margin: '0 !important' } }, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, width: "100%", children: [_jsx(Typography, { variant: "body2", fontWeight: 500, sx: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: isHit(record) ? record.howler.analytic : 'Event' }), isHit(record) && _jsx(EscalationChip, { hit: record, layout: HitLayout.DENSE }), isHit(record) && _jsx(Chip, { label: record.howler.status, size: "small", color: "primary", sx: { flexShrink: 0 } }), _jsx(Typography, { variant: "caption", color: "textSecondary", sx: { flexShrink: 0 }, children: record.howler.id })] }) }), _jsx(AccordionDetails, { children: _jsxs(Stack, { spacing: 1, children: [_jsx(Autocomplete, { freeSolo: true, disablePortal: true, options: folderOptions, value: path, onInputChange: (_ev, newVal) => onPathChange(newVal), renderInput: params => (_jsx(TextField, { ...params, size: "small", placeholder: t('modal.cases.add_to_case.select_path'), fullWidth: true, error: pathError, helperText: pathError ? t('modal.cases.add_to_case.path_invalid') : undefined })) }), _jsx(TextField, { size: "small", fullWidth: true, placeholder: t('modal.cases.add_to_case.title'), value: title, onChange: ev => onTitleChange(ev.target.value) }), title && (_jsx(Typography, { variant: "caption", color: "textSecondary", children: t('modal.cases.add_to_case.full_path', { path: fullPath }) }))] }) })] }));
|
|
14
14
|
};
|
|
15
15
|
export default CaseRecordRow;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
|
|
1
2
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
3
3
|
import { type FC } from 'react';
|
|
4
4
|
declare const CreateCaseModal: FC<{
|
|
5
|
-
records: (Hit |
|
|
5
|
+
records: (Hit | Event)[];
|
|
6
6
|
}>;
|
|
7
7
|
export default CreateCaseModal;
|
|
@@ -6,7 +6,7 @@ import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers
|
|
|
6
6
|
import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
|
|
7
7
|
import i18n from '@cccsaurora/howler-ui/i18n';
|
|
8
8
|
import { I18nextProvider } from 'react-i18next';
|
|
9
|
-
import {
|
|
9
|
+
import { createMockEvent, createMockHit } from '@cccsaurora/howler-ui/tests/utils';
|
|
10
10
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
11
|
import CreateCaseModal from './CreateCaseModal';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -48,7 +48,7 @@ const MOCK_HIT_1 = createMockHit({
|
|
|
48
48
|
const MOCK_HIT_2 = createMockHit({
|
|
49
49
|
howler: { id: 'hit-002', analytic: 'AnalyticTwo', status: 'open' }
|
|
50
50
|
});
|
|
51
|
-
const
|
|
51
|
+
const MOCK_EVENT = createMockEvent({
|
|
52
52
|
howler: { id: 'obs-001' }
|
|
53
53
|
});
|
|
54
54
|
const MOCK_CONFIG = {
|
|
@@ -115,19 +115,19 @@ describe('CreateCaseModal', () => {
|
|
|
115
115
|
expect(screen.getByText(MOCK_HIT_1.howler.analytic)).toBeInTheDocument();
|
|
116
116
|
expect(screen.getByText(MOCK_HIT_2.howler.analytic)).toBeInTheDocument();
|
|
117
117
|
});
|
|
118
|
-
it('renders a row for an
|
|
119
|
-
renderModal([
|
|
120
|
-
expect(screen.getByText('
|
|
118
|
+
it('renders a row for an event record', () => {
|
|
119
|
+
renderModal([MOCK_EVENT]);
|
|
120
|
+
expect(screen.getByText('Event')).toBeInTheDocument();
|
|
121
121
|
});
|
|
122
122
|
it('pre-populates the title for a hit with analytic and id', () => {
|
|
123
123
|
renderModal([MOCK_HIT_1]);
|
|
124
124
|
const titleInputs = screen.getAllByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
125
125
|
expect(titleInputs[0]).toHaveValue(`${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`);
|
|
126
126
|
});
|
|
127
|
-
it('pre-populates the title for an
|
|
128
|
-
renderModal([
|
|
127
|
+
it('pre-populates the title for an event with Event and id', () => {
|
|
128
|
+
renderModal([MOCK_EVENT]);
|
|
129
129
|
const titleInput = screen.getByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
130
|
-
expect(titleInput).toHaveValue(`
|
|
130
|
+
expect(titleInput).toHaveValue(`Event (${MOCK_EVENT.howler.id})`);
|
|
131
131
|
});
|
|
132
132
|
it('shows the alert placement section label when there are records', () => {
|
|
133
133
|
renderModal([MOCK_HIT_1]);
|
|
@@ -302,14 +302,14 @@ describe('CreateCaseModal', () => {
|
|
|
302
302
|
path: `investigations/${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`
|
|
303
303
|
}));
|
|
304
304
|
});
|
|
305
|
-
it('uses
|
|
305
|
+
it('uses event __index for event records', async () => {
|
|
306
306
|
const api = (await import('api')).default;
|
|
307
307
|
mockDispatchApi.mockResolvedValue({ case_id: 'new-case-id' });
|
|
308
|
-
renderModal([
|
|
308
|
+
renderModal([MOCK_EVENT]);
|
|
309
309
|
await fillCaseMetadata(user);
|
|
310
310
|
await user.click(screen.getByRole('button', { name: i18n.t('confirm') }));
|
|
311
311
|
await waitFor(() => expect(mockClose).toHaveBeenCalled());
|
|
312
|
-
expect(api.v2.case.items.post).toHaveBeenCalledWith('new-case-id', expect.objectContaining({ type: '
|
|
312
|
+
expect(api.v2.case.items.post).toHaveBeenCalledWith('new-case-id', expect.objectContaining({ type: 'event' }));
|
|
313
313
|
});
|
|
314
314
|
it('does not call items.post when there are no records', async () => {
|
|
315
315
|
const api = (await import('api')).default;
|
|
@@ -348,11 +348,11 @@ describe('CreateCaseModal', () => {
|
|
|
348
348
|
expect(titleInputs[0]).toHaveValue('Edited Title');
|
|
349
349
|
expect(titleInputs[1]).toHaveValue(`${MOCK_HIT_2.howler.analytic} (${MOCK_HIT_2.howler.id})`);
|
|
350
350
|
});
|
|
351
|
-
it('mixed hit and
|
|
352
|
-
renderModal([MOCK_HIT_1,
|
|
351
|
+
it('mixed hit and event records each get correct default titles', () => {
|
|
352
|
+
renderModal([MOCK_HIT_1, MOCK_EVENT]);
|
|
353
353
|
const titleInputs = screen.getAllByPlaceholderText(i18n.t('modal.cases.add_to_case.title'));
|
|
354
354
|
expect(titleInputs[0]).toHaveValue(`${MOCK_HIT_1.howler.analytic} (${MOCK_HIT_1.howler.id})`);
|
|
355
|
-
expect(titleInputs[1]).toHaveValue(`
|
|
355
|
+
expect(titleInputs[1]).toHaveValue(`Event (${MOCK_EVENT.howler.id})`);
|
|
356
356
|
});
|
|
357
357
|
});
|
|
358
358
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
|
|
2
|
+
import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
|
|
2
3
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
3
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
4
4
|
import type { RecordEntry } from './types';
|
|
5
|
-
export declare const defaultTitle: (record: Hit |
|
|
5
|
+
export declare const defaultTitle: (record: Hit | Event) => string;
|
|
6
6
|
export declare const useFolderOptions: (selectedCase: Case | null) => string[];
|
|
7
|
-
export declare const useRecordEntries: (records: (Hit |
|
|
7
|
+
export declare const useRecordEntries: (records: (Hit | Event)[]) => readonly [RecordEntry[], (index: number, field: "title" | "path", value: string) => void];
|
|
@@ -3,7 +3,7 @@ export const defaultTitle = (record) => {
|
|
|
3
3
|
if (record.__index === 'hit') {
|
|
4
4
|
return `${record.howler.analytic} (${record.howler.id})`;
|
|
5
5
|
}
|
|
6
|
-
return `
|
|
6
|
+
return `Event (${record.howler.id})`;
|
|
7
7
|
};
|
|
8
8
|
export const useFolderOptions = (selectedCase) => {
|
|
9
9
|
return useMemo(() => {
|
|
@@ -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 EventViewer = ({ event: provided, eventId }) => {
|
|
8
|
+
const { dispatchApi } = useMyApi();
|
|
9
|
+
const [event, setEvent] = useState(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (provided) {
|
|
12
|
+
setEvent(provided);
|
|
13
|
+
}
|
|
14
|
+
}, [provided]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (eventId) {
|
|
17
|
+
dispatchApi(api.v2.search.post('event', { query: `howler.id:${eventId}`, rows: 1 }), {
|
|
18
|
+
throwError: false
|
|
19
|
+
}).then(res => setEvent(res.items[0]));
|
|
20
|
+
}
|
|
21
|
+
}, [dispatchApi, eventId]);
|
|
22
|
+
if (!event) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
return _jsx(Box, { p: 1, children: event ? _jsx(ObjectDetails, { obj: event }) : _jsx(Skeleton, { height: 120 }) });
|
|
26
|
+
};
|
|
27
|
+
export default EventViewer;
|
|
@@ -14,9 +14,9 @@ import VSBoxContent from '@cccsaurora/howler-ui/components/elements/addons/layou
|
|
|
14
14
|
import VSBoxHeader from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxHeader';
|
|
15
15
|
import SearchPagination from '@cccsaurora/howler-ui/components/elements/addons/search/SearchPagination';
|
|
16
16
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
17
|
+
import EventCard from '@cccsaurora/howler-ui/components/elements/event/EventCard';
|
|
17
18
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
18
19
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
19
|
-
import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
|
|
20
20
|
import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
|
|
21
21
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
22
22
|
import useRecordSelection from '@cccsaurora/howler-ui/components/hooks/useRecordSelection';
|
|
@@ -26,7 +26,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
26
26
|
import { Link } from 'react-router-dom';
|
|
27
27
|
import { useContextSelector } from 'use-context-selector';
|
|
28
28
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
29
|
-
import {
|
|
29
|
+
import { isEvent, isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
|
|
30
30
|
import LayoutSettings from './LayoutSettings';
|
|
31
31
|
import QuerySettings from './QuerySettings';
|
|
32
32
|
import RecordQuery from './RecordQuery';
|
|
@@ -73,7 +73,7 @@ const Item = memo(({ record, onClick }) => {
|
|
|
73
73
|
boxShadow: `0px 0px 5px 2px ${theme.palette.primary.main}`
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
], children: [isHit(record) && _jsx(HitCard, { id: record.howler.id, layout: layout }),
|
|
76
|
+
], children: [isHit(record) && _jsx(HitCard, { id: record.howler.id, layout: layout }), isEvent(record) && _jsx(EventCard, { id: record.howler.id, event: record })] }));
|
|
77
77
|
});
|
|
78
78
|
const SearchPane = () => {
|
|
79
79
|
const { t } = useTranslation();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type SxProps } from '@mui/material';
|
|
2
|
+
import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
|
|
2
3
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
3
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
4
4
|
declare const _default: import("react").NamedExoticComponent<{
|
|
5
|
-
record: Hit |
|
|
5
|
+
record: Hit | Event;
|
|
6
6
|
value: string;
|
|
7
7
|
sx?: SxProps;
|
|
8
8
|
className: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import type { Event } from '@cccsaurora/howler-ui/models/entities/generated/Event';
|
|
1
2
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
3
3
|
declare const _default: import("react").NamedExoticComponent<{
|
|
4
|
-
record: Hit |
|
|
4
|
+
record: Hit | Event;
|
|
5
5
|
analyticIds: Record<string, string>;
|
|
6
6
|
columns: string[];
|
|
7
7
|
columnWidths: Record<string, string>;
|
|
8
8
|
collapseMainColumn: boolean;
|
|
9
|
-
onClick: (e: React.MouseEvent<HTMLDivElement>, record: Hit |
|
|
9
|
+
onClick: (e: React.MouseEvent<HTMLDivElement>, record: Hit | Event) => void;
|
|
10
10
|
}>;
|
|
11
11
|
export default _default;
|
|
@@ -3,11 +3,11 @@ import { KeyboardArrowUp } from '@mui/icons-material';
|
|
|
3
3
|
import { Box, Collapse, IconButton, lighten, Stack, TableCell, TableRow, Typography, useTheme } from '@mui/material';
|
|
4
4
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
5
5
|
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
6
|
+
import EventCard from '@cccsaurora/howler-ui/components/elements/event/EventCard';
|
|
6
7
|
import Assigned from '@cccsaurora/howler-ui/components/elements/hit/elements/Assigned';
|
|
7
8
|
import EscalationChip from '@cccsaurora/howler-ui/components/elements/hit/elements/EscalationChip';
|
|
8
9
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
9
10
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
10
|
-
import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
|
|
11
11
|
import { get } from 'lodash-es';
|
|
12
12
|
import { memo, useState } from 'react';
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
@@ -47,6 +47,6 @@ const RecordRow = ({ record, analyticIds, columns, columnWidths, collapseMainCol
|
|
|
47
47
|
e.preventDefault();
|
|
48
48
|
e.stopPropagation();
|
|
49
49
|
setExpandRow(_expanded => !_expanded);
|
|
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(
|
|
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(EventCard, { id: record.howler.id, event: record })) }) }) }) })] }));
|
|
51
51
|
};
|
|
52
52
|
export default memo(RecordRow);
|
|
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
8
8
|
import { useContextSelector } from 'use-context-selector';
|
|
9
9
|
const FILTER_OPTIONS = [
|
|
10
10
|
{ label: 'hit.search.index.hit', value: 'hit' },
|
|
11
|
-
{ label: 'hit.search.index.
|
|
11
|
+
{ label: 'hit.search.index.event', value: 'event' }
|
|
12
12
|
];
|
|
13
13
|
const IndexPicker = () => {
|
|
14
14
|
const { t } = useTranslation();
|
|
@@ -5,16 +5,16 @@ import api from '@cccsaurora/howler-ui/api';
|
|
|
5
5
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
6
6
|
import { useRecordContextSelector } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
7
7
|
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
8
|
+
import EventCard from '@cccsaurora/howler-ui/components/elements/event/EventCard';
|
|
8
9
|
import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
|
|
9
10
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
10
|
-
import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
|
|
11
11
|
import RecordContextMenu from '@cccsaurora/howler-ui/components/elements/record/RecordContextMenu';
|
|
12
12
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
13
13
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
14
14
|
import { useTranslation } from 'react-i18next';
|
|
15
15
|
import { Link, useNavigate } from 'react-router-dom';
|
|
16
16
|
import { useContextSelector } from 'use-context-selector';
|
|
17
|
-
import {
|
|
17
|
+
import { isEvent } from '@cccsaurora/howler-ui/utils/typeUtils';
|
|
18
18
|
import { buildViewUrl } from '@cccsaurora/howler-ui/utils/viewUtils';
|
|
19
19
|
// Custom hook to select records by IDs with proper memoization
|
|
20
20
|
const useSelectRecordsByIds = (recordIds) => {
|
|
@@ -49,7 +49,7 @@ const createRecordSignature = (record) => {
|
|
|
49
49
|
if (!record) {
|
|
50
50
|
return '';
|
|
51
51
|
}
|
|
52
|
-
if (
|
|
52
|
+
if (isEvent(record)) {
|
|
53
53
|
return record.howler?.id;
|
|
54
54
|
}
|
|
55
55
|
return `${record.howler?.id}:${normalize(record.howler?.status)}:${normalize(record.howler?.assignment)}:${normalize(record.howler?.assessment)}`;
|
|
@@ -157,6 +157,6 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
|
|
|
157
157
|
}
|
|
158
158
|
return selectedElement.id;
|
|
159
159
|
}, []);
|
|
160
|
-
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" })] })) : records.length > 0 ? (_jsx(RecordContextMenu, { getSelectedId: getSelectedId, children: records.map((r) => (_jsx(Card, { id: r.howler.id, variant: "outlined", sx: { cursor: 'pointer' }, onClick: () => navigate(`/hits/${r.howler.id}`), children: _jsx(CardContent, { children: r.__index == 'hit' ? (_jsx(HitBanner, { layout: HitLayout.DENSE, hit: r })) : (_jsx(
|
|
160
|
+
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" })] })) : records.length > 0 ? (_jsx(RecordContextMenu, { getSelectedId: getSelectedId, children: records.map((r) => (_jsx(Card, { id: r.howler.id, variant: "outlined", sx: { cursor: 'pointer' }, onClick: () => navigate(`/hits/${r.howler.id}`), children: _jsx(CardContent, { children: r.__index == 'hit' ? (_jsx(HitBanner, { layout: HitLayout.DENSE, hit: r })) : (_jsx(EventCard, { event: r })) }) }, r.howler.id))) })) : (_jsx(AppListEmpty, {}))] }) }));
|
|
161
161
|
};
|
|
162
162
|
export default ViewCard;
|
|
@@ -16,9 +16,9 @@ import VSBox from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox
|
|
|
16
16
|
import VSBoxContent from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxContent';
|
|
17
17
|
import VSBoxHeader from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxHeader';
|
|
18
18
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
19
|
+
import EventCard from '@cccsaurora/howler-ui/components/elements/event/EventCard';
|
|
19
20
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
20
21
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
21
|
-
import ObservableCard from '@cccsaurora/howler-ui/components/elements/observable/ObservableCard';
|
|
22
22
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
23
23
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
24
24
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
@@ -199,6 +199,6 @@ const ViewComposer = () => {
|
|
|
199
199
|
fontSize: '0.9em',
|
|
200
200
|
fontStyle: 'italic',
|
|
201
201
|
mb: 0.5
|
|
202
|
-
}), 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(IndexPicker, {}), _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(record => record.__index === 'hit' ? (_jsx(HitCard, { id: record.howler.id, layout: HitLayout.DENSE }, record.howler.id)) : (_jsx(
|
|
202
|
+
}), 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(IndexPicker, {}), _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(record => record.__index === 'hit' ? (_jsx(HitCard, { id: record.howler.id, layout: HitLayout.DENSE }, record.howler.id)) : (_jsx(EventCard, { event: record }, record.howler.id)))] }) })] }) }) }) }));
|
|
203
203
|
};
|
|
204
204
|
export default ViewComposer;
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
"hit.header.threat": "Threat",
|
|
189
189
|
"hit.header.view.case": "View case {{id}}",
|
|
190
190
|
"hit.header.view.hit": "View hit {{id}}",
|
|
191
|
-
"hit.header.view.
|
|
191
|
+
"hit.header.view.event": "View event {{id}}",
|
|
192
192
|
"hit.header.votes": "Votes: ",
|
|
193
193
|
"hit.howler.related": "{{count}} related records",
|
|
194
194
|
"hit.label": "Labels",
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
"hit.related.tab.case": "Cases",
|
|
234
234
|
"hit.related.tab.hit": "Hits",
|
|
235
235
|
"hit.related.tab.links": "Links",
|
|
236
|
-
"hit.related.tab.
|
|
236
|
+
"hit.related.tab.event": "Events",
|
|
237
237
|
"hit.search.aggregate.button": "Create Summary",
|
|
238
238
|
"hit.search.button": "Perform search",
|
|
239
239
|
"hit.search.custom": "Custom Sort",
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
"hit.search.filter.values": "Values",
|
|
245
245
|
"hit.search.index": "Indexes",
|
|
246
246
|
"hit.search.index.hit": "Hits",
|
|
247
|
-
"hit.search.index.
|
|
247
|
+
"hit.search.index.event": "Events",
|
|
248
248
|
"hit.search.invalid": "Invalid Query",
|
|
249
249
|
"hit.search.keyboard": "Keyboard shortcuts",
|
|
250
250
|
"hit.search.keyboard.no_shortcuts": "No shortcuts",
|
|
@@ -368,7 +368,7 @@
|
|
|
368
368
|
"no.data": "No Data",
|
|
369
369
|
"none": "None",
|
|
370
370
|
"normal": "Normal Priority",
|
|
371
|
-
"
|
|
371
|
+
"event.open": "Open Event",
|
|
372
372
|
"on": "on",
|
|
373
373
|
"open": "Open",
|
|
374
374
|
"operations.add_label": "Add Label",
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
"hit.header.threat": "Menace",
|
|
189
189
|
"hit.header.view.case": "Voir le cas {{id}}",
|
|
190
190
|
"hit.header.view.hit": "Voir l'alerte {{id}}",
|
|
191
|
-
"hit.header.view.
|
|
191
|
+
"hit.header.view.event": "Voir l'événement {{id}}",
|
|
192
192
|
"hit.header.votes": "Votes: ",
|
|
193
193
|
"hit.howler.related": "{{count}} enregistrements associés",
|
|
194
194
|
"hit.label": "Étiquettes",
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
"hit.related.tab.case": "Cas",
|
|
234
234
|
"hit.related.tab.hit": "Alertes",
|
|
235
235
|
"hit.related.tab.links": "Liens",
|
|
236
|
-
"hit.related.tab.
|
|
236
|
+
"hit.related.tab.event": "Événements",
|
|
237
237
|
"hit.search.aggregate.button": "Créer un sommaire",
|
|
238
238
|
"hit.search.button": "Effectuer une recherche",
|
|
239
239
|
"hit.search.custom": "Triage personnalisé",
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
"hit.search.filter.values": "Valeurs",
|
|
245
245
|
"hit.search.index": "Indexes",
|
|
246
246
|
"hit.search.index.hit": "Hits",
|
|
247
|
-
"hit.search.index.
|
|
247
|
+
"hit.search.index.event": "Événements",
|
|
248
248
|
"hit.search.invalid": "Requête invalide",
|
|
249
249
|
"hit.search.keyboard": "Raccourcis clavier",
|
|
250
250
|
"hit.search.keyboard.no_shortcuts": "Pas de raccourcis",
|
|
@@ -368,7 +368,7 @@
|
|
|
368
368
|
"no.data": "Aucune donnée",
|
|
369
369
|
"none": "Rien",
|
|
370
370
|
"normal": "Priorité normale",
|
|
371
|
-
"
|
|
371
|
+
"event.open": "Ouvrir événement",
|
|
372
372
|
"on": "sur",
|
|
373
373
|
"open": "Ouvert",
|
|
374
374
|
"operations.add_label": "Ajouter un label",
|