chore(ui): cleanup activity feed from settings page (#9362)

This commit is contained in:
Chirag Madlani 2022-12-17 07:30:16 +05:30 committed by GitHub
parent 1430e7e9c0
commit 17344f2c10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 0 additions and 423 deletions

View File

@ -1,19 +0,0 @@
.activity-feed-settings-tree {
.ant-tree-list-holder-inner {
margin-left: -16px;
}
.ant-tree-switcher.ant-tree-switcher_open {
visibility: hidden;
}
.ant-tree-node-content-wrapper {
padding: 0;
}
.ant-divider.ant-divider-horizontal {
margin: 16px 0;
}
}
.settings-page-card-container {
box-shadow: 1px 1px 3px 0px rgb(0 0 0 / 12%);
border: 1px #dde3ea solid;
}

View File

@ -1,26 +0,0 @@
import { render } from '@testing-library/react';
import React from 'react';
import ActivityFeedSettingsPage from './ActivityFeedSettingsPage';
jest.mock('../../axiosAPIs/eventFiltersAPI', () => ({
getActivityFeedEventFilters: jest
.fn()
.mockImplementation(() => Promise.resolve()),
resetAllFilters: jest.fn().mockImplementation(() => Promise.resolve()),
updateFilters: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
showSuccessToast: jest.fn(),
}));
describe('Test ActivityFeedSettingsPage', () => {
it('should render properly', async () => {
const { findByText } = render(<ActivityFeedSettingsPage />);
expect(
await findByText(/No activity feed settings available/)
).toBeInTheDocument();
});
});

View File

@ -1,294 +0,0 @@
/* eslint-disable @typescript-eslint/camelcase */
import {
Button,
Card,
Col,
Divider,
Row,
Space,
Tooltip,
Tree,
Typography,
} from 'antd';
import { AxiosError } from 'axios';
import { cloneDeep, isUndefined, map, startCase } from 'lodash';
import React, { Key, useEffect, useState } from 'react';
import {
ActivityFeedSettings,
getActivityFeedEventFilters,
resetAllFilters,
updateFilters,
} from '../../axiosAPIs/eventFiltersAPI';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import Loader from '../../components/Loader/Loader';
import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider';
import { ResourceEntity } from '../../components/PermissionProvider/PermissionProvider.interface';
import { TERM_ALL } from '../../constants/constants';
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
import { Operation } from '../../generated/entity/policies/policy';
import {
EventFilter,
Filters,
SettingType,
} from '../../generated/settings/settings';
import jsonData from '../../jsons/en';
import { checkPermission } from '../../utils/PermissionsUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import './ActivityFeedSettingsPage.style.less';
import { getEventFilterFromTree } from './ActivityFeedSettingsPage.utils';
const ActivityFeedSettingsPage: React.FC = () => {
const [eventFilters, setEventFilters] = useState<EventFilter[]>();
const [loading, setLoading] = useState(true);
const [selectedKey, setSelectedKey] = useState<string>();
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
const [updatedTree, setUpdatedTree] = useState<Record<string, string[]>>();
const fetchEventFilters = async () => {
try {
const data = await getActivityFeedEventFilters();
setEventFilters(data);
} catch (error) {
const err = error as AxiosError;
showErrorToast(err, jsonData['api-error-messages']['fetch-settings']);
} finally {
setLoading(false);
}
};
const createActivityFeed = async (req: ActivityFeedSettings) => {
try {
setLoading(true);
const data = await updateFilters(req);
const filteredData = data.config_value?.filter(
({ entityType }) => entityType !== TERM_ALL
);
setEventFilters(filteredData);
showSuccessToast(
jsonData['api-success-messages']['add-settings-success']
);
} catch {
showErrorToast(jsonData['api-error-messages']['add-settings-error']);
} finally {
setLoading(false);
}
};
const generateTreeData = (entityType: string, data?: Filters[]) => {
return [
{
key: entityType,
title: (
<Typography.Text strong>{startCase(entityType)}</Typography.Text>
),
data: true,
children:
data?.map(({ eventType, include, exclude }) => {
const key = `${entityType}-${eventType}` as string;
return {
key: key,
title: startCase(eventType),
data: include,
children:
(include?.length === 1 && include[0] === TERM_ALL) ||
(exclude?.length === 1 && exclude[0] === TERM_ALL)
? undefined
: [
...(include?.map((inc) => ({
key: `${key}-${inc}`,
title: startCase(inc),
data: true,
})) || []),
...(exclude?.map((ex) => ({
key: `${key}-${ex}`,
title: startCase(ex),
data: false,
})) || []),
],
};
}) || [],
},
];
};
const getCheckedKeys = (eventFilters: EventFilter[]) => {
const checkedArray = [] as string[];
const clonedFilters = cloneDeep(eventFilters);
clonedFilters?.map(({ entityType, filters }) => {
filters &&
filters.map((obj) => {
if (
obj.include &&
obj.include.length === 1 &&
obj.include[0] === 'all'
) {
checkedArray.push(`${entityType}-${obj.eventType}`);
} else {
obj?.include?.forEach((entityUpdated) => {
const name = `${entityType}-${obj.eventType}-${entityUpdated}`;
checkedArray.push(name);
});
}
});
});
return checkedArray;
};
const handleTreeCheckChange = (keys: Key[], entityType: string) => {
const key = String(keys[0]).split('-')[0];
setCheckedKeys(keys as string[]);
const updateData = cloneDeep(updatedTree || {});
updateData[entityType] = keys as string[];
setUpdatedTree(updateData);
setSelectedKey(key);
};
const onSave = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
event.stopPropagation();
if (!isUndefined(updatedTree) && selectedKey && eventFilters) {
const deepClonedTree = cloneDeep(updatedTree);
const data = {
config_type: SettingType.ActivityFeedFilterSetting,
config_value: getEventFilterFromTree(deepClonedTree, eventFilters),
} as ActivityFeedSettings;
createActivityFeed(data);
setUpdatedTree(undefined);
setSelectedKey(undefined);
}
};
useEffect(() => {
fetchEventFilters();
}, []);
useEffect(() => {
const checkKeys = getCheckedKeys(eventFilters as EventFilter[]);
setCheckedKeys(checkKeys);
}, [eventFilters, updatedTree, selectedKey]);
const { permissions } = usePermissionProvider();
const editPermission = checkPermission(
Operation.EditAll,
ResourceEntity.FEED,
permissions
);
const createPermission = checkPermission(
Operation.Create,
ResourceEntity.FEED,
permissions
);
const handleResetClick = async () => {
try {
setLoading(true);
const data = await resetAllFilters();
const filteredData = data.config_value?.filter(
({ entityType }) => entityType !== TERM_ALL
);
setEventFilters(filteredData);
showSuccessToast(
jsonData['api-success-messages']['add-settings-success']
);
} catch {
showErrorToast(jsonData['api-error-messages']['add-settings-error']);
} finally {
setLoading(false);
}
};
return loading ? (
<Col span={24}>
<Loader />
</Col>
) : (
<>
{eventFilters ? (
<>
<Row gutter={[16, 16]}>
<Col span={24}>
<Typography.Title
level={5}
style={{ margin: 0 }}
type="secondary">
Activity Feed
</Typography.Title>
</Col>
<Col span={24}>
<Card className="settings-page-card-container" size="small">
{eventFilters &&
map(eventFilters, ({ entityType, filters }, index) => (
<>
{entityType !== TERM_ALL ? (
<div
className="activity-feed-settings-tree"
key={entityType}>
<Tree
checkable
defaultExpandAll
defaultCheckedKeys={checkedKeys}
icon={null}
key={entityType}
treeData={generateTreeData(entityType, filters)}
onCheck={(keys) =>
handleTreeCheckChange(keys as Key[], entityType)
}
/>
{index !== eventFilters?.length - 1 && <Divider />}
</div>
) : null}
</>
))}
</Card>
</Col>
<Col>
<Space direction="horizontal" size={16}>
<Tooltip
title={
editPermission || createPermission
? ''
: NO_PERMISSION_FOR_ACTION
}>
<Button
disabled={!(editPermission || createPermission)}
type="primary"
onClick={onSave}>
Save
</Button>
</Tooltip>
<Tooltip
title={createPermission ? '' : NO_PERMISSION_FOR_ACTION}>
<Button
disabled={!createPermission}
type="text"
onClick={handleResetClick}>
Reset to default
</Button>
</Tooltip>
</Space>
</Col>
<Col span={24} />
<Col span={24} />
</Row>
</>
) : (
<ErrorPlaceHolder>
<Typography.Text>No activity feed settings available</Typography.Text>
</ErrorPlaceHolder>
)}
</>
);
};
export default ActivityFeedSettingsPage;

View File

@ -1,54 +0,0 @@
import { xor } from 'lodash';
import { EventFilter, EventType } from '../../generated/settings/settings';
export const getEventFilterFromTree = (
updatedTree: Record<string, string[]>,
eventFilters: EventFilter[]
): EventFilter[] => {
return eventFilters.map((eventFilter) => ({
...eventFilter,
filters: eventFilter.filters?.map((filter) => {
let includeList = filter.include;
let excludeList = filter.exclude;
// derive the merge list
const mergedList = [
...(includeList as string[]),
...(excludeList as string[]),
];
// manipulate tree if event type is present
if (updatedTree[eventFilter.entityType]) {
// Split the value to get list of [eventType, filter, event]
const temp = updatedTree[eventFilter.entityType].map((key) =>
key.split('-')
);
// grab the list of current eventType
const eventList = temp.filter((f) => f[1] === filter.eventType);
if (eventList.length > 0) {
if (filter.eventType === EventType.EntityUpdated) {
// derive include list based on selected events
includeList = eventList.map((f) => f[2]).filter(Boolean);
// derive the exclude list by symmetric difference
excludeList = xor(mergedList, includeList);
} else {
includeList = ['all'];
excludeList = [];
}
} else {
excludeList = [...(includeList ?? []), ...(excludeList ?? [])];
includeList = [];
}
}
return {
...filter,
include: includeList,
exclude: excludeList,
};
}),
}));
};

View File

@ -20,7 +20,6 @@ import {
GlobalSettingsMenuCategory,
} from '../constants/GlobalSettings.constants';
import { TeamType } from '../generated/entity/teams/team';
import ActivityFeedSettingsPage from '../pages/ActivityFeedSettingsPage/ActivityFeedSettingsPage';
import TeamsPage from '../pages/teams/TeamsPage';
import { userPermissions } from '../utils/PermissionsUtils';
import {
@ -170,21 +169,6 @@ const GlobalSettingRouter = () => {
)}
/>
<AdminProtectedRoute
exact
// Currently we don't have any permission related to ActivityFeed settings page
// update below once we have it
component={ActivityFeedSettingsPage}
hasPermission={userPermissions.hasViewPermissions(
ResourceEntity.FEED,
permissions
)}
path={getSettingPath(
GlobalSettingsMenuCategory.COLLABORATION,
GlobalSettingOptions.ACTIVITY_FEED
)}
/>
<AdminProtectedRoute
exact
component={ElasticSearchIndexPage}

View File

@ -15,7 +15,6 @@ import { ItemType } from 'antd/lib/menu/hooks/useItems';
import { camelCase } from 'lodash';
import React, { ReactNode } from 'react';
import { ReactComponent as AdminIcon } from '../../src/assets/svg/admin.svg';
import { ReactComponent as AllActivityIcon } from '../../src/assets/svg/all-activity.svg';
import { ReactComponent as BotIcon } from '../../src/assets/svg/bot-profile.svg';
import { ReactComponent as DashboardIcon } from '../../src/assets/svg/dashboard-grey.svg';
import { ReactComponent as ElasticSearchIcon } from '../../src/assets/svg/elasticsearch.svg';
@ -153,19 +152,6 @@ export const getGlobalSettingsMenuWithPermission = (
},
],
},
{
category: 'Collaboration',
items: [
{
label: 'Activity Feed',
isProtected: userPermissions.hasViewPermissions(
ResourceEntity.FEED,
permissions
),
icon: <AllActivityIcon className="side-panel-icons" />,
},
],
},
{
category: 'Custom Attributes',
items: [