UI: Added users and admin page to global settings menu (#6429)

* added users and admin page to global settings menu

* addressing comment
This commit is contained in:
Shailesh Parmar 2022-07-30 23:17:07 +05:30 committed by GitHub
parent 512b241069
commit 6fb888437a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 626 additions and 17 deletions

View File

@ -0,0 +1,10 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_12104_20402)">
<path d="M11.8121 2.38207C10.5267 1.09691 8.81773 0.388984 6.99992 0.388984C6.00789 0.388984 5.03719 0.603359 4.14824 1.01379L4.28934 0.653125C4.38559 0.406757 4.26391 0.129218 4.01781 0.0329683C3.77145 -0.0632817 3.49391 0.058398 3.39766 0.304492L2.8073 1.81469C2.74141 1.98312 2.74852 2.17316 2.82672 2.33641C2.90492 2.49937 3.04875 2.62406 3.22129 2.6782L4.7684 3.16328C4.81598 3.17832 4.86438 3.18543 4.91168 3.18543C5.11539 3.18543 5.30406 3.05445 5.36832 2.84992C5.44734 2.59754 5.30707 2.32902 5.05469 2.25L4.29098 2.01047C5.12305 1.57488 6.04973 1.34684 6.99965 1.34684C8.5618 1.34684 10.0302 1.95523 11.1348 3.05965C12.2393 4.16434 12.8477 5.63269 12.8477 7.19484C12.8477 8.75699 12.2393 10.2256 11.1348 11.33C10.0302 12.4345 8.5618 13.0429 6.99965 13.0429C5.4375 13.0429 3.96887 12.4345 2.86445 11.33C1.75977 10.2254 1.15164 8.75699 1.15164 7.19484C1.15164 6.2657 1.36301 5.37758 1.77973 4.55508C1.89922 4.31937 1.80488 4.03117 1.56918 3.91168C1.33348 3.79219 1.04527 3.88652 0.925781 4.12223C0.44043 5.0798 0.194336 6.1134 0.194336 7.1943C0.194336 9.01211 0.902266 10.7211 2.1877 12.0065C3.47312 13.292 5.18211 13.9999 6.99992 13.9999C8.81773 13.9999 10.5267 13.292 11.8121 12.0065C13.0976 10.7211 13.8055 9.01211 13.8055 7.1943C13.8055 5.37648 13.0976 3.6675 11.8121 2.38207Z" fill="#37352F"/>
</g>
<defs>
<clipPath id="clip0_12104_20402">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -121,7 +121,7 @@ export const createUser = (
return APIClient.post(`/users`, userDetails);
};
export const updateUser = (data: User): Promise<AxiosResponse> => {
export const updateUser = (data: User | CreateUser): Promise<AxiosResponse> => {
return APIClient.put('/users', data);
};

View File

@ -0,0 +1,265 @@
/*
* Copyright 2022 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button, Col, Modal, Row, Space, Switch, Table, Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { AxiosError } from 'axios';
import { isUndefined } from 'lodash';
import React, { FC, useMemo, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { updateUser } from '../../axiosAPIs/userAPI';
import { getUserPath, PAGE_SIZE, ROUTES } from '../../constants/constants';
import { CreateUser } from '../../generated/api/teams/createUser';
import { EntityReference, User } from '../../generated/entity/teams/user';
import { Paging } from '../../generated/type/paging';
import jsonData from '../../jsons/en';
import { getEntityName, getTeamsText } from '../../utils/CommonUtils';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal';
import NextPrevious from '../common/next-previous/NextPrevious';
import Searchbar from '../common/searchbar/Searchbar';
import Loader from '../Loader/Loader';
import './usersList.less';
interface UserListV1Props {
data: User[];
paging: Paging;
searchTerm: string;
currentPage: number;
isDataLoading: boolean;
showDeletedUser: boolean;
onPagingChange: (cursorValue: string | number, activePage?: number) => void;
onShowDeletedUserChange: (value: boolean) => void;
onSearch: (text: string) => void;
afterDeleteAction: () => void;
}
const UserListV1: FC<UserListV1Props> = ({
data,
paging,
searchTerm,
currentPage,
isDataLoading,
showDeletedUser,
onSearch,
onShowDeletedUserChange,
onPagingChange,
afterDeleteAction,
}) => {
const history = useHistory();
const [selectedUser, setSelectedUser] = useState<User>();
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showReactiveModal, setShowReactiveModal] = useState(false);
const showRestore = showDeletedUser && !isDataLoading;
const handleAddNewUser = () => {
history.push(ROUTES.CREATE_USER);
};
const handleReactiveUser = async () => {
if (isUndefined(selectedUser)) {
return;
}
const updatedUserData: CreateUser = {
description: selectedUser.description,
displayName: selectedUser.displayName,
email: selectedUser.email,
isAdmin: selectedUser.isAdmin,
name: selectedUser.name,
profile: selectedUser.profile,
roles: selectedUser.roles?.map((role) => role.id),
teams: selectedUser.teams?.map((team) => team.id),
};
try {
const { data } = await updateUser(updatedUserData);
if (data) {
afterDeleteAction();
showSuccessToast(
jsonData['api-success-messages']['user-restored-success']
);
} else {
throw jsonData['api-error-messages']['update-user-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['update-user-error']
);
}
setSelectedUser(undefined);
setShowReactiveModal(false);
};
const columns: ColumnsType<User> = useMemo(() => {
return [
{
title: 'Username',
dataIndex: 'username',
key: 'username',
render: (_, record) => (
<Link
className="hover:tw-underline tw-cursor-pointer"
to={getUserPath(record.fullyQualifiedName || record.name)}>
{getEntityName(record)}
</Link>
),
},
{
title: 'Email',
dataIndex: 'email',
key: 'email',
},
{
title: 'Teams',
dataIndex: 'teams',
key: 'teams',
render: (teams: EntityReference[]) => getTeamsText(teams),
},
{
title: 'Actions',
dataIndex: 'actions',
key: 'actions',
width: 90,
render: (_, record) => (
<Space
align="center"
className="tw-w-full tw-justify-center"
size={8}>
{showRestore && (
<Tooltip placement="bottom" title="Restore">
<Button
icon={
<SVGIcons
alt="Restore"
className="tw-w-4"
icon={Icons.RESTORE}
/>
}
type="text"
onClick={() => {
setSelectedUser(record);
setShowReactiveModal(true);
}}
/>
</Tooltip>
)}
<Tooltip placement="bottom" title="Delete">
<Button
icon={
<SVGIcons
alt="Delete"
className="tw-w-4"
icon={Icons.DELETE}
/>
}
type="text"
onClick={() => {
setSelectedUser(record);
setShowDeleteModal(true);
}}
/>
</Tooltip>
</Space>
),
},
];
}, [showRestore]);
return (
<Row className="user-listing" gutter={[16, 16]}>
<Col span={8}>
<Searchbar
removeMargin
placeholder="Search for user..."
searchValue={searchTerm}
typingInterval={500}
onSearch={onSearch}
/>
</Col>
<Col span={16}>
<Space align="center" className="tw-w-full tw-justify-end" size={16}>
<span>
<Switch
checked={showDeletedUser}
onClick={onShowDeletedUserChange}
/>
<span className="tw-ml-2">Deleted Users</span>
</span>
<Button type="primary" onClick={handleAddNewUser}>
Add User
</Button>
</Space>
</Col>
<Col span={24}>
<Table
className="user-list-table"
columns={columns}
dataSource={data}
loading={{
spinning: isDataLoading,
indicator: <Loader size="small" />,
}}
pagination={false}
size="middle"
/>
</Col>
<Col span={24}>
{paging.total > PAGE_SIZE && (
<NextPrevious
currentPage={currentPage}
isNumberBased={Boolean(searchTerm)}
pageSize={PAGE_SIZE}
paging={paging}
pagingHandler={onPagingChange}
totalCount={paging.total}
/>
)}
</Col>
<Modal
cancelButtonProps={{
type: 'link',
}}
className="reactive-modal"
okText="Restore"
title="restore User"
visible={showReactiveModal}
onCancel={() => {
setShowReactiveModal(false);
setSelectedUser(undefined);
}}
onOk={handleReactiveUser}>
<p>Are you sure you want to restore {getEntityName(selectedUser)}?</p>
</Modal>
<DeleteWidgetModal
afterDeleteAction={afterDeleteAction}
allowSoftDelete={!showDeletedUser}
entityId={selectedUser?.id || ''}
entityName={selectedUser?.name || ''}
entityType="user"
visible={showDeleteModal}
onCancel={() => {
setShowDeleteModal(false);
setSelectedUser(undefined);
}}
/>
</Row>
);
};
export default UserListV1;

View File

@ -0,0 +1,36 @@
/*
* Copyright 2022 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.user-listing,
.reactive-modal {
.ant-btn {
border-radius: 4px;
}
.user-list-table {
.ant-table-thead > tr > th {
font-weight: bold;
background: #fff;
}
.ant-table-row .ant-table-cell:first-child,
.ant-table-thead .ant-table-cell:first-child {
padding-left: 16px;
}
table {
border: 1px solid #dde3ea;
box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
}
}

View File

@ -14,7 +14,7 @@
import { Modal, Radio, RadioChangeEvent } from 'antd';
import { AxiosError, AxiosResponse } from 'axios';
import { startCase } from 'lodash';
import React, { ChangeEvent, useCallback, useState } from 'react';
import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { deleteEntity } from '../../../axiosAPIs/miscAPI';
import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
@ -28,6 +28,7 @@ import Loader from '../../Loader/Loader';
import { DeleteType, DeleteWidgetModalProps } from './DeleteWidget.interface';
const DeleteWidgetV1 = ({
allowSoftDelete = true,
visible,
entityName,
entityType,
@ -40,7 +41,9 @@ const DeleteWidgetV1 = ({
const [entityDeleteState, setEntityDeleteState] =
useState<typeof ENTITY_DELETE_STATE>(ENTITY_DELETE_STATE);
const [name, setName] = useState<string>('');
const [value, setValue] = useState<DeleteType>(DeleteType.SOFT_DELETE);
const [value, setValue] = useState<DeleteType>(
allowSoftDelete ? DeleteType.SOFT_DELETE : DeleteType.HARD_DELETE
);
const prepareDeleteMessage = (softDelete = false) => {
const softDeleteText = `Soft deleting will deactivate the ${entityName}. This will disable any discovery, read or write operations on ${entityName}`;
@ -54,11 +57,13 @@ const DeleteWidgetV1 = ({
title: `Delete ${entityType}${entityName}`,
description: prepareDeleteMessage(true),
type: DeleteType.SOFT_DELETE,
isAllowd: allowSoftDelete,
},
{
title: `Permanently Delete ${entityType}${entityName}`,
description: prepareDeleteMessage(),
type: DeleteType.HARD_DELETE,
isAllowd: true,
},
];
@ -154,6 +159,14 @@ const DeleteWidgetV1 = ({
handleOnEntityDelete(value === DeleteType.SOFT_DELETE);
};
useEffect(() => {
setValue(allowSoftDelete ? DeleteType.SOFT_DELETE : DeleteType.HARD_DELETE);
setEntityDeleteState({
...ENTITY_DELETE_STATE,
softDelete: allowSoftDelete,
});
}, [allowSoftDelete]);
const Footer = () => {
return (
<div className="tw-justify-end" data-testid="footer">
@ -202,17 +215,22 @@ const DeleteWidgetV1 = ({
visible={visible}
onCancel={handleOnEntityDeleteCancel}>
<Radio.Group value={value} onChange={onChange}>
{DELETE_OPTION.map((option) => (
<Radio
data-testid={option.type}
key={option.type}
value={option.type}>
<p className="tw-text-sm tw-mb-1 tw-font-medium">{option.title}</p>
<p className="tw-text-grey-muted tw-text-xs tw-mb-2">
{option.description}
</p>
</Radio>
))}
{DELETE_OPTION.map(
(option) =>
option.isAllowd && (
<Radio
data-testid={option.type}
key={option.type}
value={option.type}>
<p className="tw-text-sm tw-mb-1 tw-font-medium">
{option.title}
</p>
<p className="tw-text-grey-muted tw-text-xs tw-mb-2">
{option.description}
</p>
</Radio>
)
)}
</Radio.Group>
<div>
<p className="tw-mb-2">

View File

@ -14,7 +14,7 @@
export const GLOBAL_SETTINGS_MENU = [
{
category: 'Access',
items: ['Teams', 'Users', 'Roles'],
items: ['Teams', 'Users', 'Admins', 'Roles'],
},
{
category: 'Services',
@ -26,7 +26,7 @@ export const GLOBAL_SETTINGS_MENU = [
},
{
category: 'Integrations',
items: ['Webhook', 'Slack', 'Bots'],
items: ['Webhook', 'Bots'],
},
];
@ -47,6 +47,7 @@ export enum GlobalSettingsMenuCategory {
export enum GlobalSettingOptions {
USERS = 'users',
ADMINS = 'admins',
TEAMS = 'teams',
ROLES = 'roles',
POLICIES = 'policies',

View File

@ -153,6 +153,8 @@ const jsonData = {
'delete-glossary-success': 'Glossary deleted successfully!',
'delete-glossary-term-success': 'Glossary term deleted successfully!',
'test-connection-success': 'Connection tested successfully!',
'user-restored-success': 'User restored successfully!',
},
'form-error-messages': {
'empty-email': 'Email is required.',

View File

@ -0,0 +1,212 @@
/*
* Copyright 2022 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AxiosError } from 'axios';
import { FormattedUsersData, SearchResponse } from 'Models';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { searchData } from '../../axiosAPIs/miscAPI';
import { getUsers } from '../../axiosAPIs/userAPI';
import Loader from '../../components/Loader/Loader';
import UserListV1 from '../../components/UserList/UserListV1';
import { WILD_CARD_CHAR } from '../../constants/char.constants';
import {
INITIAL_PAGIN_VALUE,
PAGE_SIZE,
pagingObject,
} from '../../constants/constants';
import { GlobalSettingOptions } from '../../constants/globalSettings.constants';
import { SearchIndex } from '../../enums/search.enum';
import { User } from '../../generated/entity/teams/user';
import { Include } from '../../generated/type/include';
import { Paging } from '../../generated/type/paging';
import jsonData from '../../jsons/en';
import { formatUsersResponse } from '../../utils/APIUtils';
import { showErrorToast } from '../../utils/ToastUtils';
const teamsAndUsers = [GlobalSettingOptions.USERS, GlobalSettingOptions.ADMINS];
const UserListPageV1 = () => {
const { tab } = useParams<{ [key: string]: GlobalSettingOptions }>();
const [isAdminPage, setIsAdminPage] = useState<boolean | undefined>(
tab === GlobalSettingOptions.ADMINS || undefined
);
const [isPageLoading, setIsPageLoading] = useState<boolean>(true);
const [isDataLoading, setIsDataLoading] = useState<boolean>(true);
const [showDeletedUser, setShowDeletedUser] = useState<boolean>(false);
const [userList, setUserList] = useState<User[]>([]);
const [paging, setPaging] = useState<Paging>(pagingObject);
const [searchValue, setSearchValue] = useState<string>('');
const [currentPage, setCurrentPage] = useState<number>(INITIAL_PAGIN_VALUE);
const initialSetup = () => {
setIsAdminPage(tab === GlobalSettingOptions.ADMINS || undefined);
setIsPageLoading(true);
setIsDataLoading(true);
setShowDeletedUser(false);
setSearchValue('');
setCurrentPage(INITIAL_PAGIN_VALUE);
};
const fetchUsersList = async (
isAdmin: boolean | undefined = undefined,
param = {} as Record<string, string>,
limit = PAGE_SIZE
) => {
setIsDataLoading(true);
try {
const { data } = await getUsers(
'profile,teams,roles',
limit,
param,
isAdmin,
false
);
if (data) {
setUserList(data.data);
setPaging(data.paging);
} else {
throw jsonData['api-error-messages']['fetch-users-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['fetch-users-error']
);
}
setIsDataLoading(false);
setIsPageLoading(false);
};
const handleFetch = () => {
fetchUsersList(isAdminPage, {
include: showDeletedUser ? Include.Deleted : Include.NonDeleted,
});
};
const userQuerySearch = (
text = WILD_CARD_CHAR,
currentPage = 1,
isAdmin = false,
isDeleted = false
) => {
let filters = '';
if (isAdmin) {
filters = '(isAdmin:true)';
}
return new Promise<Array<FormattedUsersData>>((resolve) => {
searchData(
text,
currentPage,
PAGE_SIZE,
filters,
'',
'',
SearchIndex.USER,
isDeleted
)
.then((res: SearchResponse) => {
const data = formatUsersResponse(res.data.hits.hits);
setPaging({
total: res.data.hits.total.value,
});
resolve(data);
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['fetch-users-error']
);
resolve([]);
});
});
};
const getSearchedUsers = (value: string, pageNumber: number) => {
setIsDataLoading(true);
userQuerySearch(value, pageNumber, isAdminPage, showDeletedUser).then(
(resUsers) => {
setUserList(resUsers as unknown as User[]);
setIsDataLoading(false);
}
);
};
const handlePagingChange = (
cursorValue: string | number,
activePage?: number
) => {
if (searchValue) {
setCurrentPage(cursorValue as number);
getSearchedUsers(searchValue, cursorValue as number);
} else {
setCurrentPage(activePage as number);
fetchUsersList(isAdminPage, {
[cursorValue]: paging[cursorValue as keyof Paging] as string,
include: showDeletedUser ? Include.Deleted : Include.NonDeleted,
});
}
};
const handleShowDeletedUserChange = (value: boolean) => {
setCurrentPage(INITIAL_PAGIN_VALUE);
setSearchValue('');
setShowDeletedUser(value);
fetchUsersList(isAdminPage, {
include: value ? Include.Deleted : Include.NonDeleted,
});
};
const handleSearch = (value: string) => {
setSearchValue(value);
setCurrentPage(INITIAL_PAGIN_VALUE);
if (value) {
getSearchedUsers(value, INITIAL_PAGIN_VALUE);
} else {
handleFetch();
}
};
useEffect(() => {
initialSetup();
if (teamsAndUsers.includes(tab)) {
fetchUsersList(tab === GlobalSettingOptions.ADMINS || undefined);
} else {
setIsPageLoading(false);
setIsDataLoading(false);
}
}, [tab]);
if (isPageLoading) {
return <Loader />;
}
return (
<UserListV1
afterDeleteAction={handleFetch}
currentPage={currentPage}
data={userList}
isDataLoading={isDataLoading}
paging={paging}
searchTerm={searchValue}
showDeletedUser={showDeletedUser}
onPagingChange={handlePagingChange}
onSearch={handleSearch}
onShowDeletedUserChange={handleShowDeletedUserChange}
/>
);
};
export default UserListPageV1;

View File

@ -34,6 +34,12 @@ const CustomPropertiesPageV1 = withSuspenseFallback(
() => import('../pages/CustomPropertiesPage/CustomPropertiesPageV1')
)
);
const RolesPageComponent = withSuspenseFallback(
React.lazy(() => import('../pages/RolesPage/RolesPage.component'))
);
const UserListPageV1 = withSuspenseFallback(
React.lazy(() => import('../pages/UserListPage/UserListPageV1'))
);
const GlobalSettingRouter = () => {
return (
@ -46,6 +52,27 @@ const GlobalSettingRouter = () => {
)}
/>
</Route>
<Route
exact
path={getSettingPath(
GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.TEAMS
)}
/>
<Route
exact
component={RolesPageComponent}
path={getSettingPath(
GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.ROLES
)}
/>
<Route
exact
component={UserListPageV1}
path={getSettingCategoryPath(GlobalSettingsMenuCategory.ACCESS)}
/>
<Route
exact
component={WebhooksPageV1}

View File

@ -11,6 +11,7 @@
* limitations under the License.
*/
import { Popover } from 'antd';
import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames';
import { capitalize, isEmpty, isNil, isNull, isUndefined } from 'lodash';
@ -612,7 +613,9 @@ export const getEntityPlaceHolder = (value: string, isDeleted?: boolean) => {
* @param entity - entity reference
* @returns - entity name
*/
export const getEntityName = (entity?: EntityReference | DataService) => {
export const getEntityName = (
entity?: EntityReference | DataService | User
) => {
return entity?.displayName || entity?.name || '';
};
@ -729,3 +732,32 @@ export const isAllowedHost = () => {
export const showPagination = (paging: Paging) => {
return !isNil(paging.after) || !isNil(paging.before);
};
export const getTeamsText = (teams: EntityReference[]) => {
return teams.length === 0 ? (
'No teams'
) : teams.length > 1 ? (
<span>
{getEntityName(teams[0])}, &{' '}
<Popover
content={
<span>
{teams.map((t, i) => {
return i >= 1 ? (
<span className="tw-block tw-text-left" key={i}>
{getEntityName(t)}
</span>
) : null;
})}
</span>
}
trigger="hover">
<span className="tw-underline tw-cursor-pointer">
{teams.length - 1} more
</span>
</Popover>
</span>
) : (
`${getEntityName(teams[0])}`
);
};

View File

@ -89,6 +89,7 @@ import IconMyData from '../assets/svg/ic-mydata.svg';
import IconQuality from '../assets/svg/ic-quality.svg';
import IconReply from '../assets/svg/ic-reply.svg';
import IconReports from '../assets/svg/ic-reports.svg';
import IconRestore from '../assets/svg/ic-restore.svg';
import IconSchema from '../assets/svg/ic-schema.svg';
import IconSearch from '../assets/svg/ic-search.svg';
import IconSettings from '../assets/svg/ic-settings.svg';
@ -206,6 +207,7 @@ export const Icons = {
QUALITY: 'icon-quality',
ISSUES: 'icon-issues',
TRENDS: 'icon-trends',
RESTORE: 'icon-restore',
LINEAGE: 'icon-lineage',
MANAGE: 'icon-manage',
HOME: 'icon-home',
@ -362,6 +364,10 @@ const SVGIcons: FunctionComponent<Props> = ({
case Icons.TEAMS:
IconComponent = IconTeams;
break;
case Icons.RESTORE:
IconComponent = IconRestore;
break;
case Icons.TEAMS_GREY:
IconComponent = IconTeamsGrey;