-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Security Solution] [Cases] All Cases Refactor & getAllCasesSelectorModal introduced #97149
Changes from 26 commits
e0cfb51
e91a2e7
97244cf
e8675f3
6b92abc
e6c6956
d693aa1
895a76e
a4d4abc
20971db
7f812c6
59a8cfd
c4c2042
ba8b6f9
9e0b6a6
af12c84
cc64b38
3ebfc8e
2db2cc5
aeea4ac
a7df6c8
3f00989
cefcc6d
f3a7c56
6989af2
5192797
9e98bd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
/* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was Look at the diffchecker here, you'll thank me later (and ill thank you back for the thorough review) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for that! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I knew you'd thank me later 🥳 Thank YOU for the thorough review! |
||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React, { useCallback, useMemo, useRef, useState } from 'react'; | ||
import { EuiProgress } from '@elastic/eui'; | ||
import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; | ||
import { isEmpty, memoize } from 'lodash/fp'; | ||
import styled, { css } from 'styled-components'; | ||
import classnames from 'classnames'; | ||
|
||
import { | ||
Case, | ||
CaseStatuses, | ||
CaseType, | ||
CommentRequestAlertType, | ||
CommentType, | ||
FilterOptions, | ||
SortFieldCase, | ||
SubCase, | ||
} from '../../../common'; | ||
import { SELECTABLE_MESSAGE_COLLECTIONS } from '../../common/translations'; | ||
import { useGetActionLicense } from '../../containers/use_get_action_license'; | ||
import { useGetCases } from '../../containers/use_get_cases'; | ||
import { usePostComment } from '../../containers/use_post_comment'; | ||
import { CaseCallOut } from '../callout'; | ||
import { CaseDetailsHrefSchema, CasesNavigation } from '../links'; | ||
import { Panel } from '../panel'; | ||
import { getActionLicenseError } from '../use_push_to_service/helpers'; | ||
import { ERROR_PUSH_SERVICE_CALLOUT_TITLE } from '../use_push_to_service/translations'; | ||
import { useCasesColumns } from './columns'; | ||
import { getExpandedRowMap } from './expanded_row'; | ||
import { CasesTableHeader } from './header'; | ||
import { CasesTableFilters } from './table_filters'; | ||
import { EuiBasicTableOnChange } from './types'; | ||
|
||
import { CasesTable } from './table'; | ||
const ProgressLoader = styled(EuiProgress)` | ||
${({ $isShow }: { $isShow: boolean }) => | ||
$isShow | ||
? css` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏾 Nice, didn't know about this |
||
top: 2px; | ||
border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; | ||
z-index: ${({ theme }) => theme.eui.euiZHeader}; | ||
` | ||
: ` | ||
display: none; | ||
`} | ||
`; | ||
|
||
const getSortField = (field: string): SortFieldCase => | ||
field === SortFieldCase.closedAt ? SortFieldCase.closedAt : SortFieldCase.createdAt; | ||
|
||
interface AllCasesGenericProps { | ||
alertData?: Omit<CommentRequestAlertType, 'type'>; | ||
caseDetailsNavigation?: CasesNavigation<CaseDetailsHrefSchema, 'configurable'>; // if not passed, case name is not displayed as a link (Formerly dependant on isSelectorView) | ||
configureCasesNavigation?: CasesNavigation; // if not passed, header with nav is not displayed (Formerly dependant on isSelectorView) | ||
createCaseNavigation: CasesNavigation; | ||
disabledStatuses?: CaseStatuses[]; | ||
isSelectorView?: boolean; | ||
onRowClick?: (theCase?: Case | SubCase) => void; | ||
updateCase?: (newCase: Case) => void; | ||
userCanCrud: boolean; | ||
} | ||
|
||
export const AllCasesGeneric = React.memo<AllCasesGenericProps>( | ||
({ | ||
alertData, | ||
caseDetailsNavigation, | ||
configureCasesNavigation, | ||
createCaseNavigation, | ||
disabledStatuses, | ||
isSelectorView, | ||
onRowClick, | ||
updateCase, | ||
userCanCrud, | ||
}) => { | ||
const { actionLicense } = useGetActionLicense(); | ||
const { | ||
data, | ||
filterOptions, | ||
loading, | ||
queryParams, | ||
selectedCases, | ||
refetchCases, | ||
setFilters, | ||
setQueryParams, | ||
setSelectedCases, | ||
} = useGetCases(); | ||
// Post Comment to Case | ||
const { postComment, isLoading: isCommentUpdating } = usePostComment(); | ||
|
||
const sorting = useMemo( | ||
() => ({ | ||
sort: { field: queryParams.sortField, direction: queryParams.sortOrder }, | ||
}), | ||
[queryParams.sortField, queryParams.sortOrder] | ||
); | ||
|
||
const filterRefetch = useRef<() => void>(); | ||
const setFilterRefetch = useCallback( | ||
(refetchFilter: () => void) => { | ||
filterRefetch.current = refetchFilter; | ||
}, | ||
[filterRefetch] | ||
); | ||
const [refresh, doRefresh] = useState<number>(0); | ||
const [isLoading, handleIsLoading] = useState<boolean>(false); | ||
const refreshCases = useCallback( | ||
(dataRefresh = true) => { | ||
if (dataRefresh) refetchCases(); | ||
doRefresh((prev) => prev + 1); | ||
setSelectedCases([]); | ||
if (filterRefetch.current != null) { | ||
filterRefetch.current(); | ||
} | ||
}, | ||
[filterRefetch, refetchCases, setSelectedCases] | ||
); | ||
|
||
const { onClick: onCreateCaseNavClick } = createCaseNavigation; | ||
const goToCreateCase = useCallback( | ||
(ev) => { | ||
ev.preventDefault(); | ||
if (isSelectorView && onRowClick != null) { | ||
onRowClick(); | ||
} else if (onCreateCaseNavClick) { | ||
onCreateCaseNavClick(ev); | ||
} | ||
}, | ||
[isSelectorView, onCreateCaseNavClick, onRowClick] | ||
); | ||
const actionsErrors = useMemo(() => getActionLicenseError(actionLicense), [actionLicense]); | ||
|
||
const tableOnChangeCallback = useCallback( | ||
({ page, sort }: EuiBasicTableOnChange) => { | ||
let newQueryParams = queryParams; | ||
if (sort) { | ||
newQueryParams = { | ||
...newQueryParams, | ||
sortField: getSortField(sort.field), | ||
sortOrder: sort.direction, | ||
}; | ||
} | ||
if (page) { | ||
newQueryParams = { | ||
...newQueryParams, | ||
page: page.index + 1, | ||
perPage: page.size, | ||
}; | ||
} | ||
setQueryParams(newQueryParams); | ||
refreshCases(false); | ||
}, | ||
[queryParams, refreshCases, setQueryParams] | ||
); | ||
|
||
const onFilterChangedCallback = useCallback( | ||
(newFilterOptions: Partial<FilterOptions>) => { | ||
if (newFilterOptions.status && newFilterOptions.status === CaseStatuses.closed) { | ||
setQueryParams({ sortField: SortFieldCase.closedAt }); | ||
} else if (newFilterOptions.status && newFilterOptions.status === CaseStatuses.open) { | ||
setQueryParams({ sortField: SortFieldCase.createdAt }); | ||
} else if ( | ||
newFilterOptions.status && | ||
newFilterOptions.status === CaseStatuses['in-progress'] | ||
) { | ||
setQueryParams({ sortField: SortFieldCase.createdAt }); | ||
} | ||
setFilters(newFilterOptions); | ||
refreshCases(false); | ||
}, | ||
[refreshCases, setQueryParams, setFilters] | ||
); | ||
|
||
const showActions = userCanCrud && !isSelectorView; | ||
|
||
const columns = useCasesColumns({ | ||
caseDetailsNavigation, | ||
filterStatus: filterOptions.status, | ||
refreshCases, | ||
handleIsLoading, | ||
showActions, | ||
}); | ||
|
||
const itemIdToExpandedRowMap = useMemo( | ||
() => | ||
getExpandedRowMap({ | ||
columns, | ||
data: data.cases, | ||
onSubCaseClick: onRowClick, | ||
}), | ||
[data.cases, columns, onRowClick] | ||
); | ||
|
||
const pagination = useMemo( | ||
() => ({ | ||
pageIndex: queryParams.page - 1, | ||
pageSize: queryParams.perPage, | ||
totalItemCount: data.total, | ||
pageSizeOptions: [5, 10, 15, 20, 25], | ||
}), | ||
[data, queryParams] | ||
); | ||
|
||
const euiBasicTableSelectionProps = useMemo<EuiTableSelectionType<Case>>( | ||
() => ({ | ||
onSelectionChange: setSelectedCases, | ||
selectableMessage: (selectable) => (!selectable ? SELECTABLE_MESSAGE_COLLECTIONS : ''), | ||
initialSelected: selectedCases, | ||
}), | ||
[selectedCases, setSelectedCases] | ||
); | ||
const isCasesLoading = useMemo(() => loading.indexOf('cases') > -1, [loading]); | ||
const isDataEmpty = useMemo(() => data.total === 0, [data]); | ||
|
||
const TableWrap = useMemo(() => (isSelectorView ? 'span' : Panel), [isSelectorView]); | ||
|
||
const tableRowProps = useCallback( | ||
(theCase: Case) => { | ||
const onTableRowClick = memoize(async () => { | ||
if (alertData != null) { | ||
await postComment({ | ||
caseId: theCase.id, | ||
data: { | ||
type: CommentType.alert, | ||
...alertData, | ||
}, | ||
updateCase, | ||
}); | ||
} | ||
if (onRowClick) { | ||
onRowClick(theCase); | ||
} | ||
}); | ||
|
||
return { | ||
'data-test-subj': `cases-table-row-${theCase.id}`, | ||
className: classnames({ isDisabled: theCase.type === CaseType.collection }), | ||
...(isSelectorView && theCase.type !== CaseType.collection | ||
? { onClick: onTableRowClick } | ||
: {}), | ||
}; | ||
}, | ||
[isSelectorView, alertData, onRowClick, postComment, updateCase] | ||
); | ||
|
||
return ( | ||
<> | ||
{!isEmpty(actionsErrors) && ( | ||
<CaseCallOut title={ERROR_PUSH_SERVICE_CALLOUT_TITLE} messages={actionsErrors} /> | ||
)} | ||
{configureCasesNavigation != null && ( | ||
<CasesTableHeader | ||
actionsErrors={actionsErrors} | ||
createCaseNavigation={createCaseNavigation} | ||
configureCasesNavigation={configureCasesNavigation} | ||
refresh={refresh} | ||
userCanCrud={userCanCrud} | ||
/> | ||
)} | ||
<ProgressLoader | ||
size="xs" | ||
color="accent" | ||
className="essentialAnimation" | ||
$isShow={(isCasesLoading || isLoading || isCommentUpdating) && !isDataEmpty} | ||
/> | ||
<TableWrap | ||
data-test-subj="table-wrap" | ||
loading={!isSelectorView ? isCasesLoading : undefined} | ||
> | ||
<CasesTableFilters | ||
countClosedCases={data.countClosedCases} | ||
countOpenCases={data.countOpenCases} | ||
countInProgressCases={data.countInProgressCases} | ||
onFilterChanged={onFilterChangedCallback} | ||
initial={{ | ||
search: filterOptions.search, | ||
reporters: filterOptions.reporters, | ||
tags: filterOptions.tags, | ||
status: filterOptions.status, | ||
}} | ||
setFilterRefetch={setFilterRefetch} | ||
disabledStatuses={disabledStatuses} | ||
/> | ||
<CasesTable | ||
columns={columns} | ||
createCaseNavigation={createCaseNavigation} | ||
data={data} | ||
filterOptions={filterOptions} | ||
goToCreateCase={goToCreateCase} | ||
handleIsLoading={handleIsLoading} | ||
isCasesLoading={isCasesLoading} | ||
isCommentUpdating={isCommentUpdating} | ||
isDataEmpty={isDataEmpty} | ||
isSelectorView={isSelectorView} | ||
itemIdToExpandedRowMap={itemIdToExpandedRowMap} | ||
onChange={tableOnChangeCallback} | ||
pagination={pagination} | ||
refreshCases={refreshCases} | ||
selectedCases={selectedCases} | ||
selection={euiBasicTableSelectionProps} | ||
showActions={showActions} | ||
sorting={sorting} | ||
tableRowProps={tableRowProps} | ||
userCanCrud={userCanCrud} | ||
/> | ||
</TableWrap> | ||
</> | ||
); | ||
} | ||
); | ||
|
||
AllCasesGeneric.displayName = 'AllCasesGeneric'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused