Skip to content
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

Add bulk action to events page. #21027

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const EventsEntityTable = () => {

const columnRenderers = useColumnRenderers();
const _fetchEvents = useCallback((searchParams: SearchParams) => fetchEvents(searchParams, streamId as string), [streamId]);
const { entityActions, expandedSections } = useTableElements({ defaultLayout: eventsTableElements.defaultLayout });
const { entityActions, expandedSections, bulkSelection } = useTableElements({ defaultLayout: eventsTableElements.defaultLayout });

return (
<PaginatedEntityTable<Event, EventsAdditionalData> humanName="events"
Expand All @@ -47,7 +47,8 @@ const EventsEntityTable = () => {
expandedSectionsRenderer={expandedSections}
entityAttributesAreCamelCase={false}
filterValueRenderers={FilterValueRenderers}
columnRenderers={columnRenderers} />
columnRenderers={columnRenderers}
bulkSelection={bulkSelection} />
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as React from 'react';

import BulkActionsDropdown from 'components/common/EntityDataTable/BulkActionsDropdown';
import type { Event } from 'components/events/events/types';
import useEventBulkActions from 'components/events/events/hooks/useEventBulkActions';

type Props = {
selectedEntitiesData: {[eventId: string]: Event}
}

const BulkActions = ({ selectedEntitiesData }: Props) => {
const { actions, pluggableActionModals } = useEventBulkActions(Object.values(selectedEntitiesData));

return (
<>
<BulkActionsDropdown>
{actions}
</BulkActionsDropdown>
{pluggableActionModals}
</>
);
};

export default BulkActions;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { MenuItem } from 'components/bootstrap';
import LinkToReplaySearch from 'components/event-definitions/replay-search/LinkToReplaySearch';

const useEventAction = (event) => {
const { actions: pluggableActions, actionModals: pluggableActionModals } = usePluggableEventActions(event.id);
const { actions: pluggableActions, actionModals: pluggableActionModals } = usePluggableEventActions([event.id]);
const hasReplayInfo = !!event.replay_info;
const isNotSystemEvent = !event.event_definition_type.startsWith('system-notifications');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import { useMemo } from 'react';

import usePluggableEventActions from 'components/events/events/hooks/usePluggableEventActions';
import type { Event } from 'components/events/events/types';

const useEventBulkAction = (events: Array<Event>) => {
const { actions: pluggableActions, actionModals: pluggableActionModals } = usePluggableEventActions(events.map(({ id }: Event) => id));
// const hasReplayInfo = !!event.replay_info;
// const isNotSystemEvent = !event.event_definition_type.startsWith('system-notifications');

const actions = useMemo(() => [
pluggableActions,
].filter(Boolean), [pluggableActions]);

return { actions, pluggableActionModals };
};

export default useEventBulkAction;
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,26 @@ import React, { useRef } from 'react';
import usePluginEntities from 'hooks/usePluginEntities';
import type { EventActionComponentProps } from 'views/types';

const usePluggableEventActions = (eventId: string) => {
const usePluggableEventActions = (eventIds: Array<string>, onlyBulk: boolean = false) => {
const modalRefs = useRef({});
const pluggableActions = usePluginEntities('views.components.eventActions');
const availableActions = pluggableActions.filter(
(perspective) => (perspective.useCondition ? !!perspective.useCondition() : true),
(perspective) => (onlyBulk ? perspective.isBulk : true) && (perspective.useCondition ? !!perspective.useCondition() : true),
);

const actions = availableActions.map(({ component: PluggableEventAction, key }: { component: React.ComponentType<EventActionComponentProps>, key: string }) => (
<PluggableEventAction key={`event-action-${key}`}
eventId={eventId}
// eventId={eventId}
eventIds={eventIds}
modalRef={() => modalRefs.current[key]} />
));

const actionModals = availableActions
.filter(({ modal }) => !!modal)
.map(({ modal: ActionModal, key }) => (
<ActionModal key={`event-action-modal-${key}`}
eventId={eventId}
// eventId={eventId}
eventIds={eventIds}
ref={(r) => { modalRefs.current[key] = r; }} />
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import pickBy from 'lodash/pickBy';
import keyBy from 'lodash/keyBy';

import EventActions from 'components/events/events/EventActions';
import type { Event } from 'components/events/events/types';
import ExpandedSection from 'components/events/ExpandedSection';
import type useTableLayout from 'components/common/EntityDataTable/hooks/useTableLayout';
import BulkActions from 'components/events/events/BulkActions';

const useTableElements = ({ defaultLayout }: {
defaultLayout: Parameters<typeof useTableLayout>[0],
Expand All @@ -39,9 +42,27 @@ const useTableElements = ({ defaultLayout }: {
},
}), [renderExpandedRules]);

const [selectedEntitiesData, setSelectedEntitiesData] = useState<{[eventId: string]: Event}>({});
const bulkSelection = useMemo(() => ({
onChangeSelection: (selectedItemsIds: Array<string>, list: Array<Event>) => {
setSelectedEntitiesData((cur) => {
const selectedItemsIdsSet = new Set(selectedItemsIds);
const filtratedCurrentItems: {[eventId: string]: Event} = pickBy(cur, (_, eventId) => selectedItemsIdsSet.has(eventId));
const filtratedCurrentEntries = list.filter(({ id }) => selectedItemsIdsSet.has(id));
const listOfCurrentEntries: {[eventId: string]: Event} = keyBy(filtratedCurrentEntries, 'id');

return ({ ...filtratedCurrentItems, ...listOfCurrentEntries });
});
},
actions: <BulkActions selectedEntitiesData={selectedEntitiesData} />,

}), [selectedEntitiesData]);

return {
entityActions,
expandedSections,
bulkActions: <BulkActions selectedEntitiesData={selectedEntitiesData} />,
bulkSelection,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ const usePluggableDashboardActions = (eventId: string) => {
);
const actions = availableActions.map(({ component: PluggableDashboardAction, key }) => (
<PluggableDashboardAction key={`event-action-${key}`}
eventId={eventId}
eventIds={[eventId]}
modalRef={() => modalRefs.current[key]} />
));

const actionModals = availableActions
.filter(({ modal }) => !!modal)
.map(({ modal: ActionModal, key }) => (
<ActionModal key={`event-action-modal-${key}`}
eventId={eventId}
eventIds={[eventId]}
ref={(r) => { modalRefs.current[key] = r; }} />
));

Expand Down
7 changes: 4 additions & 3 deletions graylog2-web-interface/src/views/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ type DashboardActionComponentProps<T> = {
}

type EventWidgetActionComponentProps<T> = {
eventId: string,
eventIds: Array<string>,
modalRef: () => T,
}

Expand All @@ -281,7 +281,7 @@ type DashboardActionModalProps<T> = React.PropsWithRef<{
};

type EventActionModalProps<T> = React.PropsWithRef<{
eventId: string,
eventIds: Array<string>,
}> & {
ref: React.LegacyRef<T>,
}
Expand Down Expand Up @@ -316,6 +316,7 @@ type EventAction = {
modal?: React.ComponentType<EventActionModalProps<unknown>>,
component: React.ComponentType<EventActionComponentProps>,
key: string,
isBulk?: boolean
}

type EventWidgetAction<T> = {
Expand All @@ -331,7 +332,7 @@ type AssetInformation = {
}

export type EventActionComponentProps = {
eventId: string,
eventIds: Array<string>,
modalRef: () => unknown,
}

Expand Down
Loading