Skip to content

Commit

Permalink
[SIEM] [Cases] Case closed and add user email (#60463)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored Mar 19, 2020
1 parent fe4c164 commit d5ed93e
Show file tree
Hide file tree
Showing 31 changed files with 692 additions and 267 deletions.
7 changes: 5 additions & 2 deletions x-pack/legacy/plugins/siem/public/containers/case/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface Comment {

export interface Case {
id: string;
closedAt: string | null;
closedBy: ElasticUser | null;
comments: Comment[];
commentIds: string[];
createdAt: string;
Expand Down Expand Up @@ -59,12 +61,13 @@ export interface AllCases extends CasesStatus {

export enum SortFieldCase {
createdAt = 'createdAt',
updatedAt = 'updatedAt',
closedAt = 'closedAt',
}

export interface ElasticUser {
readonly username: string;
readonly email?: string | null;
readonly fullName?: string | null;
readonly username: string;
}

export interface FetchCasesProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const dataFetchReducer = (state: CaseState, action: Action): CaseState => {
};
const initialData: Case = {
id: '',
closedAt: null,
closedBy: null,
createdAt: '',
comments: [],
commentIds: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { useReducer, useCallback } from 'react';

import { cloneDeep } from 'lodash/fp';
import { CaseRequest } from '../../../../../../plugins/case/common/api';
import { errorToToaster, useStateToaster } from '../../components/toasters';

Expand Down Expand Up @@ -47,7 +47,7 @@ const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState =>
...state,
isLoading: false,
isError: false,
caseData: action.payload,
caseData: cloneDeep(action.payload),
updateKey: null,
};
case 'FETCH_FAILURE':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const useGetCasesMockState: UseGetCasesState = {
countOpenCases: 0,
cases: [
{
closedAt: null,
closedBy: null,
id: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:23.627Z',
createdBy: { username: 'elastic' },
Expand All @@ -27,6 +29,8 @@ export const useGetCasesMockState: UseGetCasesState = {
version: 'WzQ3LDFd',
},
{
closedAt: null,
closedBy: null,
id: '362a5c10-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:13.328Z',
createdBy: { username: 'elastic' },
Expand All @@ -41,6 +45,8 @@ export const useGetCasesMockState: UseGetCasesState = {
version: 'WzQ3LDFd',
},
{
closedAt: null,
closedBy: null,
id: '34f8b9e0-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:11.328Z',
createdBy: { username: 'elastic' },
Expand All @@ -55,6 +61,8 @@ export const useGetCasesMockState: UseGetCasesState = {
version: 'WzQ3LDFd',
},
{
closedAt: '2020-02-13T19:44:13.328Z',
closedBy: { username: 'elastic' },
id: '31890e90-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:05.563Z',
createdBy: { username: 'elastic' },
Expand All @@ -64,11 +72,13 @@ export const useGetCasesMockState: UseGetCasesState = {
status: 'closed',
tags: ['phishing'],
title: 'Uh oh',
updatedAt: null,
updatedBy: null,
updatedAt: '2020-02-13T19:44:13.328Z',
updatedBy: { username: 'elastic' },
version: 'WzQ3LDFd',
},
{
closedAt: null,
closedBy: null,
id: '2f5b3210-4e99-11ea-9290-35d05cb55c15',
createdAt: '2020-02-13T19:44:01.901Z',
createdBy: { username: 'elastic' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const Spacer = styled.span`
const renderStringField = (field: string, dataTestSubj: string) =>
field != null ? <span data-test-subj={dataTestSubj}>{field}</span> : getEmptyTagValue();
export const getCasesColumns = (
actions: Array<DefaultItemIconButtonAction<Case>>
actions: Array<DefaultItemIconButtonAction<Case>>,
filterStatus: string
): CasesColumns[] => [
{
name: i18n.NAME,
Expand Down Expand Up @@ -113,22 +114,39 @@ export const getCasesColumns = (
render: (comments: Case['commentIds']) =>
renderStringField(`${comments.length}`, `case-table-column-commentCount`),
},
{
field: 'createdAt',
name: i18n.OPENED_ON,
sortable: true,
render: (createdAt: Case['createdAt']) => {
if (createdAt != null) {
return (
<FormattedRelativePreferenceDate
value={createdAt}
data-test-subj={`case-table-column-createdAt`}
/>
);
filterStatus === 'open'
? {
field: 'createdAt',
name: i18n.OPENED_ON,
sortable: true,
render: (createdAt: Case['createdAt']) => {
if (createdAt != null) {
return (
<FormattedRelativePreferenceDate
value={createdAt}
data-test-subj={`case-table-column-createdAt`}
/>
);
}
return getEmptyTagValue();
},
}
return getEmptyTagValue();
},
},
: {
field: 'closedAt',
name: i18n.CLOSED_ON,
sortable: true,
render: (closedAt: Case['closedAt']) => {
if (closedAt != null) {
return (
<FormattedRelativePreferenceDate
value={closedAt}
data-test-subj={`case-table-column-closedAt`}
/>
);
}
return getEmptyTagValue();
},
},
{
name: 'Actions',
actions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const ProgressLoader = styled(EuiProgress)`
const getSortField = (field: string): SortFieldCase => {
if (field === SortFieldCase.createdAt) {
return SortFieldCase.createdAt;
} else if (field === SortFieldCase.updatedAt) {
return SortFieldCase.updatedAt;
} else if (field === SortFieldCase.closedAt) {
return SortFieldCase.closedAt;
}
return SortFieldCase.createdAt;
};
Expand Down Expand Up @@ -206,17 +206,25 @@ export const AllCases = React.memo(() => {
}
setQueryParams(newQueryParams);
},
[setQueryParams, queryParams]
[queryParams]
);

const onFilterChangedCallback = useCallback(
(newFilterOptions: Partial<FilterOptions>) => {
if (newFilterOptions.status && newFilterOptions.status === 'closed') {
setQueryParams({ ...queryParams, sortField: SortFieldCase.closedAt });
} else if (newFilterOptions.status && newFilterOptions.status === 'open') {
setQueryParams({ ...queryParams, sortField: SortFieldCase.createdAt });
}
setFilters({ ...filterOptions, ...newFilterOptions });
},
[filterOptions, setFilters]
[filterOptions, queryParams]
);

const memoizedGetCasesColumns = useMemo(() => getCasesColumns(actions), [actions]);
const memoizedGetCasesColumns = useMemo(() => getCasesColumns(actions, filterOptions.status), [
actions,
filterOptions.status,
]);
const memoizedPagination = useMemo(
() => ({
pageIndex: queryParams.page - 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,3 @@ export const CLOSED = i18n.translate('xpack.siem.case.caseTable.closed', {
export const DELETE = i18n.translate('xpack.siem.case.caseTable.delete', {
defaultMessage: 'Delete',
});
export const REOPEN_CASE = i18n.translate('xpack.siem.case.caseTable.reopenCase', {
defaultMessage: 'Reopen case',
});
export const CLOSE_CASE = i18n.translate('xpack.siem.case.caseTable.closeCase', {
defaultMessage: 'Close case',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useCallback } from 'react';
import styled, { css } from 'styled-components';
import {
EuiBadge,
EuiButtonToggle,
EuiDescriptionList,
EuiDescriptionListDescription,
EuiDescriptionListTitle,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import * as i18n from '../case_view/translations';
import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date';
import { CaseViewActions } from '../case_view/actions';

const MyDescriptionList = styled(EuiDescriptionList)`
${({ theme }) => css`
& {
padding-right: ${theme.eui.euiSizeL};
border-right: ${theme.eui.euiBorderThin};
}
`}
`;

interface CaseStatusProps {
'data-test-subj': string;
badgeColor: string;
buttonLabel: string;
caseId: string;
caseTitle: string;
icon: string;
isLoading: boolean;
isSelected: boolean;
status: string;
title: string;
toggleStatusCase: (status: string) => void;
value: string | null;
}
const CaseStatusComp: React.FC<CaseStatusProps> = ({
'data-test-subj': dataTestSubj,
badgeColor,
buttonLabel,
caseId,
caseTitle,
icon,
isLoading,
isSelected,
status,
title,
toggleStatusCase,
value,
}) => {
const onChange = useCallback(e => toggleStatusCase(e.target.checked ? 'closed' : 'open'), [
toggleStatusCase,
]);
return (
<EuiFlexGroup gutterSize="l" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<MyDescriptionList compressed>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiDescriptionListTitle>{i18n.STATUS}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<EuiBadge color={badgeColor} data-test-subj="case-view-status">
{status}
</EuiBadge>
</EuiDescriptionListDescription>
</EuiFlexItem>
<EuiFlexItem>
<EuiDescriptionListTitle>{title}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<FormattedRelativePreferenceDate data-test-subj={dataTestSubj} value={value} />
</EuiDescriptionListDescription>
</EuiFlexItem>
</EuiFlexGroup>
</MyDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="l" alignItems="center">
<EuiFlexItem>
<EuiButtonToggle
data-test-subj="toggle-case-status"
iconType={icon}
isLoading={isLoading}
isSelected={isSelected}
label={buttonLabel}
onChange={onChange}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<CaseViewActions caseId={caseId} caseTitle={caseTitle} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

export const CaseStatus = React.memo(CaseStatusComp);
Loading

0 comments on commit d5ed93e

Please sign in to comment.