Skip to content

Commit f86f191

Browse files
committed
selectAll & refacto ok
1 parent 600a325 commit f86f191

File tree

7 files changed

+146
-509
lines changed

7 files changed

+146
-509
lines changed

opencti-platform/opencti-front/src/components/dataGrid/DataTable.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ type DataTableInternalToolbarProps = Pick<DataTableProps,
108108
| 'handleCopy'
109109
| 'removeAuthMembersEnabled'
110110
| 'removeFromDraftEnabled'
111+
| 'markAsReadEnabled'
111112
> & {
112113
taskScope?: string
113114
globalSearch?: string;
@@ -120,6 +121,7 @@ const DataTableInternalToolbar = ({
120121
globalSearch,
121122
removeAuthMembersEnabled,
122123
removeFromDraftEnabled,
124+
markAsReadEnabled,
123125
}: DataTableInternalToolbarProps) => {
124126
const theme = useTheme<Theme>();
125127

@@ -156,6 +158,7 @@ const DataTableInternalToolbar = ({
156158
handleCopy={handleCopy}
157159
removeAuthMembersEnabled={removeAuthMembersEnabled}
158160
removeFromDraftEnabled={removeFromDraftEnabled}
161+
markAsReadEnabled={markAsReadEnabled}
159162
/>
160163
</div>
161164
);
@@ -215,6 +218,7 @@ const DataTable = (props: OCTIDataTableProps) => {
215218
taskScope,
216219
removeAuthMembersEnabled,
217220
removeFromDraftEnabled,
221+
markAsReadEnabled,
218222
} = props;
219223

220224
const settingsMessagesBannerHeight = useSettingsMessagesBannerHeight();
@@ -269,6 +273,7 @@ const DataTable = (props: OCTIDataTableProps) => {
269273
globalSearch={globalSearch}
270274
removeAuthMembersEnabled={removeAuthMembersEnabled}
271275
removeFromDraftEnabled={removeFromDraftEnabled}
276+
markAsReadEnabled={markAsReadEnabled}
272277
/>
273278
)}
274279
/>

opencti-platform/opencti-front/src/components/dataGrid/dataTableTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export interface DataTableProps {
139139
disableSelectAll?: boolean
140140
removeAuthMembersEnabled?: boolean
141141
removeFromDraftEnabled?: boolean
142+
markAsReadEnabled?: boolean
142143
selectOnLineClick?: boolean
143144
onLineClick?: (line: any) => void
144145
hideHeaders?: boolean

opencti-platform/opencti-front/src/private/components/data/DataTableToolBar.jsx

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
BrushOutlined,
3333
CancelOutlined,
3434
CenterFocusStrong,
35+
CheckCircleOutlined,
3536
ClearOutlined,
3637
CloseOutlined,
3738
ContentCopyOutlined,
@@ -44,6 +45,7 @@ import {
4445
MoveToInboxOutlined,
4546
RestoreOutlined,
4647
TransformOutlined,
48+
UnpublishedOutlined,
4749
} from '@mui/icons-material';
4850
import { BankMinus, BankPlus, CloudRefreshOutline, LabelOutline } from 'mdi-material-ui';
4951
import Autocomplete from '@mui/material/Autocomplete';
@@ -196,11 +198,11 @@ const styles = (theme) => ({
196198
},
197199
});
198200

199-
const notMergableTypes = ['Playbook', 'Indicator', 'Note', 'Opinion', 'Label', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
200-
const notAddableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
201-
const notUpdatableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
202-
const notScannableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
203-
const notEnrichableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
201+
const notMergableTypes = ['Playbook', 'Indicator', 'Note', 'Opinion', 'Label', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
202+
const notAddableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
203+
const notUpdatableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
204+
const notScannableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
205+
const notEnrichableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'Task', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
204206
const typesWithScore = [
205207
'Stix-Cyber-Observable',
206208
'Indicator',
@@ -249,7 +251,7 @@ const typesWithIndicatorTypes = ['Indicator'];
249251
const typesWithPlatforms = ['Indicator'];
250252

251253
const typesWithoutStatus = ['Stix-Core-Object', 'Stix-Domain-Object', 'Stix-Cyber-Observable', 'Artifact', 'ExternalReference'];
252-
const notShareableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace'];
254+
const notShareableTypes = ['Playbook', 'Label', 'Vocabulary', 'Case-Template', 'DeleteOperation', 'InternalFile', 'PublicDashboard', 'Workspace', 'DraftWorkspace', 'Notification'];
253255

254256
const Transition = React.forwardRef((props, ref) => (
255257
<Slide direction="up" ref={ref} {...props} />
@@ -607,7 +609,19 @@ class DataTableToolBar extends Component {
607609
}
608610
this.setState({ actionsInputs });
609611
}
610-
612+
handleLaunchRead(read) {
613+
const actions = [{
614+
type: 'REPLACE',
615+
context: {
616+
field: 'is_read',
617+
type: 'ATTRIBUTE',
618+
values: [read ? 'true' : 'false'],
619+
},
620+
}];
621+
this.setState({ actions }, () => {
622+
this.handleOpenTask();
623+
});
624+
}
611625
handleLaunchDelete() {
612626
const actions = [{ type: 'DELETE', context: null }];
613627
this.setState({ actions }, () => {
@@ -1779,6 +1793,7 @@ class DataTableToolBar extends Component {
17791793
deleteOperationEnabled,
17801794
removeAuthMembersEnabled,
17811795
removeFromDraftEnabled,
1796+
markAsReadEnabled,
17821797
warning,
17831798
warningMessage,
17841799
taskScope,
@@ -1910,6 +1925,36 @@ class DataTableToolBar extends Component {
19101925
</IconButton>
19111926
</div>
19121927
<div>
1928+
{markAsReadEnabled && (
1929+
<>
1930+
<Tooltip title={t('Mark as read')}>
1931+
<span>
1932+
<IconButton
1933+
aria-label="ack"
1934+
disabled={numberOfSelectedElements === 0 || this.state.processing}
1935+
onClick={this.handleLaunchRead.bind(this, true)}
1936+
color="success"
1937+
size="small"
1938+
>
1939+
<CheckCircleOutlined fontSize="small" />
1940+
</IconButton>
1941+
</span>
1942+
</Tooltip>
1943+
<Tooltip title={t('Mark as unread')}>
1944+
<span>
1945+
<IconButton
1946+
aria-label="ack"
1947+
disabled={numberOfSelectedElements === 0 || this.state.processing}
1948+
onClick={this.handleLaunchRead.bind(this, false)}
1949+
color="warning"
1950+
size="small"
1951+
>
1952+
<UnpublishedOutlined fontSize="small" />
1953+
</IconButton>
1954+
</span>
1955+
</Tooltip>
1956+
</>
1957+
)}
19131958
{removeAuthMembersEnabled && (
19141959
<Security needs={[BYPASS]}>
19151960
<Tooltip title={t('Remove access restriction')}>
@@ -3058,6 +3103,7 @@ DataTableToolBar.propTypes = {
30583103
deleteOperationEnabled: PropTypes.bool,
30593104
removeAuthMembersEnabled: PropTypes.bool,
30603105
removeFromDraft: PropTypes.bool,
3106+
markAsReadEnabled: PropTypes.bool,
30613107
taskScope: PropTypes.string,
30623108
};
30633109

opencti-platform/opencti-front/src/private/components/profile/Notifications.tsx

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import React, { FunctionComponent, useState } from 'react';
2-
import { NotificationsLines_data$data } from '@components/profile/notifications/__generated__/NotificationsLines_data.graphql';
3-
import { notificationLineFragment } from '@components/profile/notifications/NotificationLine';
42
import { Badge, Tooltip } from '@mui/material';
53
import Chip from '@mui/material/Chip';
64
import { deepPurple, green, indigo, red } from '@mui/material/colors';
75
import { BellCogOutline, BellOutline, BellPlusOutline, BellRemoveOutline, FileTableBoxMultipleOutline } from 'mdi-material-ui';
8-
import { NotificationLine_node$data } from '@components/profile/notifications/__generated__/NotificationLine_node.graphql';
96
import IconButton from '@mui/material/IconButton';
107
import { CheckCircleOutlined, DeleteOutlined, UnpublishedOutlined } from '@mui/icons-material';
118
import { graphql } from 'react-relay';
@@ -14,10 +11,11 @@ import DialogContent from '@mui/material/DialogContent';
1411
import DialogContentText from '@mui/material/DialogContentText';
1512
import Button from '@mui/material/Button';
1613
import DialogActions from '@mui/material/DialogActions';
14+
import { NotificationsLine_node$data } from '@components/profile/__generated__/NotificationsLine_node.graphql';
15+
import { NotificationsLinesPaginationQuery, NotificationsLinesPaginationQuery$variables } from '@components/profile/__generated__/NotificationsLinesPaginationQuery.graphql';
16+
import { NotificationsLines_data$data } from '@components/profile/__generated__/NotificationsLines_data.graphql';
1717
import { usePaginationLocalStorage } from '../../../utils/hooks/useLocalStorage';
1818
import useQueryLoading from '../../../utils/hooks/useQueryLoading';
19-
import { notificationsLinesFragment, notificationsLinesQuery } from './notifications/NotificationsLines';
20-
import { NotificationsLinesPaginationQuery, NotificationsLinesPaginationQuery$variables } from './notifications/__generated__/NotificationsLinesPaginationQuery.graphql';
2119
import useAuth from '../../../utils/hooks/useAuth';
2220
import { emptyFilterGroup, isFilterGroupNotEmpty, useGetDefaultFilterObject, useRemoveIdAndIncorrectKeysFromFilterGroupObject } from '../../../utils/filters/filtersUtils';
2321
import Breadcrumbs from '../../../components/Breadcrumbs';
@@ -34,13 +32,87 @@ import { deleteNode } from '../../../utils/store';
3432

3533
export const LOCAL_STORAGE_KEY = 'notifiers';
3634

35+
const notificationsLineFragment = graphql`
36+
fragment NotificationsLine_node on Notification {
37+
id
38+
entity_type
39+
name
40+
created
41+
notification_type
42+
is_read
43+
notification_content {
44+
title
45+
events {
46+
message
47+
operation
48+
instance_id
49+
}
50+
}
51+
}
52+
`;
53+
54+
const notificationsLinesQuery = graphql`
55+
query NotificationsLinesPaginationQuery(
56+
$search: String
57+
$count: Int!
58+
$cursor: ID
59+
$orderBy: NotificationsOrdering
60+
$orderMode: OrderingMode
61+
$filters: FilterGroup
62+
) {
63+
...NotificationsLines_data
64+
@arguments(
65+
search: $search
66+
count: $count
67+
cursor: $cursor
68+
orderBy: $orderBy
69+
orderMode: $orderMode
70+
filters: $filters
71+
)
72+
}
73+
`;
74+
75+
const notificationsLinesFragment = graphql`
76+
fragment NotificationsLines_data on Query
77+
@argumentDefinitions(
78+
search: { type: "String" }
79+
count: { type: "Int", defaultValue: 25 }
80+
cursor: { type: "ID" }
81+
orderBy: { type: "NotificationsOrdering", defaultValue: created }
82+
orderMode: { type: "OrderingMode", defaultValue: asc }
83+
filters: { type: "FilterGroup" }
84+
)
85+
@refetchable(queryName: "NotificationsLinesRefetchQuery") {
86+
myNotifications(
87+
search: $search
88+
first: $count
89+
after: $cursor
90+
orderBy: $orderBy
91+
orderMode: $orderMode
92+
filters: $filters
93+
) @connection(key: "Pagination_myNotifications") {
94+
edges {
95+
node {
96+
id
97+
...NotificationsLine_node
98+
}
99+
}
100+
pageInfo {
101+
endCursor
102+
hasNextPage
103+
globalCount
104+
}
105+
}
106+
}
107+
`;
108+
37109
export const notificationLineNotificationMarkReadMutation = graphql`
38110
mutation NotificationsNotificationMarkReadMutation(
39111
$id: ID!
40112
$read: Boolean!
41113
) {
42114
notificationMarkRead(id: $id, read: $read) {
43-
...NotificationLine_node
115+
...NotificationsLine_node
44116
}
45117
}
46118
`;
@@ -61,7 +133,7 @@ const Notifications: FunctionComponent = () => {
61133
const [commitDelete] = useApiMutation(
62134
notificationLineNotificationDeleteMutation,
63135
);
64-
const [notificationToDelete, setNotificationToDelete] = useState<NotificationLine_node$data>();
136+
const [notificationToDelete, setNotificationToDelete] = useState<NotificationsLine_node$data>();
65137

66138
setTitle(t_i18n('Notifications'));
67139

@@ -137,13 +209,13 @@ const Notifications: FunctionComponent = () => {
137209
delete: red[500],
138210
multiple: indigo[500],
139211
};
140-
const getFirstOperation = ({ notification_content, notification_type }: Pick<NotificationLine_node$data, 'notification_content' | 'notification_type'>) => {
212+
const getFirstOperation = ({ notification_content, notification_type }: Pick<NotificationsLine_node$data, 'notification_content' | 'notification_type'>) => {
141213
const events = notification_content.map((n) => n.events).flat();
142214
const firstEvent = events.at(0);
143215
const isDigest = notification_type === 'digest';
144216
return isDigest ? 'multiple' : (firstEvent?.operation ?? 'none');
145217
};
146-
const iconSelector = (notification: NotificationLine_node$data) => {
218+
const iconSelector = (notification: NotificationsLine_node$data) => {
147219
const operation = getFirstOperation(notification);
148220
switch (operation) {
149221
case 'create':
@@ -166,7 +238,7 @@ const Notifications: FunctionComponent = () => {
166238
label: 'Operation',
167239
percentWidth: 10,
168240
isSortable: isRuntimeSort,
169-
render: ({ notification_content, notification_type }: NotificationLine_node$data) => {
241+
render: ({ notification_content, notification_type }: NotificationsLine_node$data) => {
170242
const firstOperation = getFirstOperation({ notification_content, notification_type });
171243
const events = notification_content.map((n) => n.events).flat();
172244
const eventTypes: Record<string, string> = {
@@ -200,7 +272,7 @@ const Notifications: FunctionComponent = () => {
200272
label: 'Message',
201273
percentWidth: 48,
202274
isSortable: isRuntimeSort,
203-
render: ({ notification_content }: NotificationLine_node$data) => {
275+
render: ({ notification_content }: NotificationsLine_node$data) => {
204276
const events = notification_content.map((n) => n.events).flat();
205277
const firstEvent = events.at(0);
206278

@@ -276,7 +348,7 @@ const Notifications: FunctionComponent = () => {
276348
setNumberOfElements: helpers.handleSetNumberOfElements,
277349
} as UsePreloadedPaginationFragment<NotificationsLinesPaginationQuery>;
278350

279-
const renderActions = (data: NotificationLine_node$data) => {
351+
const renderActions = (data: NotificationsLine_node$data) => {
280352
const [updating, setUpdating] = useState<boolean>(false);
281353

282354
const handleRead = (id: string, read: boolean) => {
@@ -345,11 +417,13 @@ const Notifications: FunctionComponent = () => {
345417
{iconSelector(data)}
346418
</Badge>
347419
)}
348-
lineFragment={notificationLineFragment}
420+
taskScope={'USER'}
421+
lineFragment={notificationsLineFragment}
349422
toolbarFilters={contextFilters}
350423
exportContext={{ entity_type: 'Notification' }}
351424
availableEntityTypes={['Notification']}
352425
actions={renderActions}
426+
markAsReadEnabled={true}
353427
/>
354428
)}
355429
{notificationToDelete && (

opencti-platform/opencti-front/src/private/components/profile/notifications/NotificationLine.tsx

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)