mirror of
https://github.com/datahub-project/datahub.git
synced 2025-09-05 07:12:27 +00:00
refactor(ui): UI Navigation Refactoring (#5076)
This commit is contained in:
parent
ccf82229d5
commit
4da3d132f8
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route, Redirect } from 'react-router-dom';
|
||||||
import { Layout } from 'antd';
|
import { Layout } from 'antd';
|
||||||
import { BrowseResultsPage } from './browse/BrowseResultsPage';
|
import { BrowseResultsPage } from './browse/BrowseResultsPage';
|
||||||
import { EntityPage } from './entity/EntityPage';
|
import { EntityPage } from './entity/EntityPage';
|
||||||
@ -8,13 +8,11 @@ import { useEntityRegistry } from './useEntityRegistry';
|
|||||||
import { HomePage } from './home/HomePage';
|
import { HomePage } from './home/HomePage';
|
||||||
import { SearchPage } from './search/SearchPage';
|
import { SearchPage } from './search/SearchPage';
|
||||||
import { AnalyticsPage } from './analyticsDashboard/components/AnalyticsPage';
|
import { AnalyticsPage } from './analyticsDashboard/components/AnalyticsPage';
|
||||||
import { PoliciesPage } from './policy/PoliciesPage';
|
|
||||||
import AppConfigProvider from '../AppConfigProvider';
|
import AppConfigProvider from '../AppConfigProvider';
|
||||||
import { ManageIdentitiesPage } from './identity/ManageIdentitiesPage';
|
|
||||||
import { SettingsPage } from './settings/SettingsPage';
|
|
||||||
import { ManageIngestionPage } from './ingest/ManageIngestionPage';
|
import { ManageIngestionPage } from './ingest/ManageIngestionPage';
|
||||||
import { ManageDomainsPage } from './domain/ManageDomainsPage';
|
import { ManageDomainsPage } from './domain/ManageDomainsPage';
|
||||||
import BusinessGlossaryPage from './glossary/BusinessGlossaryPage';
|
import BusinessGlossaryPage from './glossary/BusinessGlossaryPage';
|
||||||
|
import { SettingsPage } from './settings/SettingsPage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for all views behind an authentication wall.
|
* Container for all views behind an authentication wall.
|
||||||
@ -37,8 +35,8 @@ export const ProtectedRoutes = (): JSX.Element => {
|
|||||||
<Route path={PageRoutes.SEARCH_RESULTS} render={() => <SearchPage />} />
|
<Route path={PageRoutes.SEARCH_RESULTS} render={() => <SearchPage />} />
|
||||||
<Route path={PageRoutes.BROWSE_RESULTS} render={() => <BrowseResultsPage />} />
|
<Route path={PageRoutes.BROWSE_RESULTS} render={() => <BrowseResultsPage />} />
|
||||||
<Route path={PageRoutes.ANALYTICS} render={() => <AnalyticsPage />} />
|
<Route path={PageRoutes.ANALYTICS} render={() => <AnalyticsPage />} />
|
||||||
<Route path={PageRoutes.POLICIES} render={() => <PoliciesPage />} />
|
<Route path={PageRoutes.POLICIES} render={() => <Redirect to="/settings/policies" />} />
|
||||||
<Route path={PageRoutes.IDENTITIES} render={() => <ManageIdentitiesPage />} />
|
<Route path={PageRoutes.IDENTITIES} render={() => <Redirect to="/settings/identities" />} />
|
||||||
<Route path={PageRoutes.DOMAINS} render={() => <ManageDomainsPage />} />
|
<Route path={PageRoutes.DOMAINS} render={() => <ManageDomainsPage />} />
|
||||||
<Route path={PageRoutes.INGESTION} render={() => <ManageIngestionPage />} />
|
<Route path={PageRoutes.INGESTION} render={() => <ManageIngestionPage />} />
|
||||||
<Route path={PageRoutes.SETTINGS} render={() => <SettingsPage />} />
|
<Route path={PageRoutes.SETTINGS} render={() => <SettingsPage />} />
|
||||||
|
@ -14,6 +14,7 @@ import { SidebarRecommendationsSection } from '../shared/containers/profile/side
|
|||||||
import { SidebarTagsSection } from '../shared/containers/profile/sidebar/SidebarTagsSection';
|
import { SidebarTagsSection } from '../shared/containers/profile/sidebar/SidebarTagsSection';
|
||||||
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
|
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
|
||||||
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
|
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
|
||||||
|
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the DataHub Container entity.
|
* Definition of the DataHub Container entity.
|
||||||
@ -67,6 +68,7 @@ export class ContainerEntity implements Entity<Container> {
|
|||||||
useEntityQuery={useGetContainerQuery}
|
useEntityQuery={useGetContainerQuery}
|
||||||
useUpdateQuery={undefined}
|
useUpdateQuery={undefined}
|
||||||
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
||||||
|
headerDropdownItems={new Set([EntityMenuItems.COPY_URL])}
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
name: 'Entities',
|
name: 'Entities',
|
||||||
|
@ -10,6 +10,7 @@ import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Owners
|
|||||||
import { getDataForEntityType } from '../shared/containers/profile/utils';
|
import { getDataForEntityType } from '../shared/containers/profile/utils';
|
||||||
import { useGetDomainQuery } from '../../../graphql/domain.generated';
|
import { useGetDomainQuery } from '../../../graphql/domain.generated';
|
||||||
import { DomainEntitiesTab } from './DomainEntitiesTab';
|
import { DomainEntitiesTab } from './DomainEntitiesTab';
|
||||||
|
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the DataHub Domain entity.
|
* Definition of the DataHub Domain entity.
|
||||||
@ -63,6 +64,7 @@ export class DomainEntity implements Entity<Domain> {
|
|||||||
useEntityQuery={useGetDomainQuery}
|
useEntityQuery={useGetDomainQuery}
|
||||||
useUpdateQuery={undefined}
|
useUpdateQuery={undefined}
|
||||||
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
||||||
|
headerDropdownItems={new Set([EntityMenuItems.COPY_URL])}
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
name: 'Entities',
|
name: 'Entities',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { CheckOutlined, CopyOutlined, InfoCircleOutlined, RightOutlined } from '@ant-design/icons';
|
import { InfoCircleOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
import { Typography, Button, Tooltip, Popover } from 'antd';
|
import { Typography, Button, Tooltip, Popover } from 'antd';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
@ -16,6 +16,7 @@ import { useGetAuthenticatedUser } from '../../../../../useGetAuthenticatedUser'
|
|||||||
import { EntityType, PlatformPrivileges } from '../../../../../../types.generated';
|
import { EntityType, PlatformPrivileges } from '../../../../../../types.generated';
|
||||||
import EntityCount from './EntityCount';
|
import EntityCount from './EntityCount';
|
||||||
import EntityName from './EntityName';
|
import EntityName from './EntityName';
|
||||||
|
import CopyUrn from '../../../../../shared/CopyUrn';
|
||||||
|
|
||||||
const TitleWrapper = styled.div`
|
const TitleWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -189,15 +190,7 @@ export const EntityHeader = ({ refreshBrowser, headerDropdownItems, isNameEditab
|
|||||||
</MainHeaderContent>
|
</MainHeaderContent>
|
||||||
<SideHeaderContent>
|
<SideHeaderContent>
|
||||||
<TopButtonsWrapper>
|
<TopButtonsWrapper>
|
||||||
<Tooltip title="Copy URN. An URN uniquely identifies an entity on DataHub.">
|
<CopyUrn urn={urn} isActive={copiedUrn} onClick={() => setCopiedUrn(true)} />
|
||||||
<Button
|
|
||||||
icon={copiedUrn ? <CheckOutlined /> : <CopyOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard.writeText(urn);
|
|
||||||
setCopiedUrn(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
{headerDropdownItems && (
|
{headerDropdownItems && (
|
||||||
<EntityDropdown
|
<EntityDropdown
|
||||||
menuItems={headerDropdownItems}
|
menuItems={headerDropdownItems}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { SearchablePage } from '../search/SearchablePage';
|
|
||||||
import { RoutedTabs } from '../shared/RoutedTabs';
|
import { RoutedTabs } from '../shared/RoutedTabs';
|
||||||
import { GroupList } from './group/GroupList';
|
import { GroupList } from './group/GroupList';
|
||||||
import { UserList } from './user/UserList';
|
import { UserList } from './user/UserList';
|
||||||
|
|
||||||
const PageContainer = styled.div`
|
const PageContainer = styled.div`
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const PageHeaderContainer = styled.div`
|
const PageHeaderContainer = styled.div`
|
||||||
@ -30,7 +30,7 @@ const Content = styled.div`
|
|||||||
height: calc(100vh - 60px);
|
height: calc(100vh - 60px);
|
||||||
|
|
||||||
&&& .ant-tabs > .ant-tabs-nav .ant-tabs-nav-wrap {
|
&&& .ant-tabs > .ant-tabs-nav .ant-tabs-nav-wrap {
|
||||||
padding-left: 15px;
|
padding-left: 28px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ enum TabType {
|
|||||||
}
|
}
|
||||||
const ENABLED_TAB_TYPES = [TabType.Users, TabType.Groups];
|
const ENABLED_TAB_TYPES = [TabType.Users, TabType.Groups];
|
||||||
|
|
||||||
export const ManageIdentitiesPage = () => {
|
export const ManageIdentities = () => {
|
||||||
/**
|
/**
|
||||||
* Determines which view should be visible: users or groups list.
|
* Determines which view should be visible: users or groups list.
|
||||||
*/
|
*/
|
||||||
@ -70,18 +70,16 @@ export const ManageIdentitiesPage = () => {
|
|||||||
const onTabChange = () => null;
|
const onTabChange = () => null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchablePage>
|
<PageContainer>
|
||||||
<PageContainer>
|
<PageHeaderContainer>
|
||||||
<PageHeaderContainer>
|
<PageTitle level={3}>Manage Users & Groups</PageTitle>
|
||||||
<PageTitle level={3}>Manage Users & Groups</PageTitle>
|
<Typography.Paragraph type="secondary">
|
||||||
<Typography.Paragraph type="secondary">
|
View your DataHub users & groups. Take administrative actions.
|
||||||
View your DataHub users & groups. Take administrative actions.
|
</Typography.Paragraph>
|
||||||
</Typography.Paragraph>
|
</PageHeaderContainer>
|
||||||
</PageHeaderContainer>
|
<Content>
|
||||||
<Content>
|
<RoutedTabs defaultPath={defaultTabPath} tabs={getTabs()} onTabChange={onTabChange} />
|
||||||
<RoutedTabs defaultPath={defaultTabPath} tabs={getTabs()} onTabChange={onTabChange} />
|
</Content>
|
||||||
</Content>
|
</PageContainer>
|
||||||
</PageContainer>
|
|
||||||
</SearchablePage>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -4,8 +4,6 @@ import styled from 'styled-components';
|
|||||||
import * as QueryString from 'query-string';
|
import * as QueryString from 'query-string';
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router';
|
||||||
|
|
||||||
import { SearchablePage } from '../search/SearchablePage';
|
|
||||||
import PolicyBuilderModal from './PolicyBuilderModal';
|
import PolicyBuilderModal from './PolicyBuilderModal';
|
||||||
import {
|
import {
|
||||||
Policy,
|
Policy,
|
||||||
@ -86,6 +84,10 @@ const EditPolicyButton = styled(Button)`
|
|||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const PageContainer = styled.span`
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 10;
|
const DEFAULT_PAGE_SIZE = 10;
|
||||||
|
|
||||||
type PrivilegeOptionType = {
|
type PrivilegeOptionType = {
|
||||||
@ -139,7 +141,7 @@ const toPolicyInput = (policy: Omit<Policy, 'urn'>): PolicyUpdateInput => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Cleanup the styling.
|
// TODO: Cleanup the styling.
|
||||||
export const PoliciesPage = () => {
|
export const ManagePolicies = () => {
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const params = QueryString.parse(location.search, { arrayFormat: 'comma' });
|
const params = QueryString.parse(location.search, { arrayFormat: 'comma' });
|
||||||
@ -416,7 +418,7 @@ export const PoliciesPage = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchablePage>
|
<PageContainer>
|
||||||
{policiesLoading && !policiesData && (
|
{policiesLoading && !policiesData && (
|
||||||
<Message type="loading" content="Loading policies..." style={{ marginTop: '10%' }} />
|
<Message type="loading" content="Loading policies..." style={{ marginTop: '10%' }} />
|
||||||
)}
|
)}
|
||||||
@ -424,9 +426,9 @@ export const PoliciesPage = () => {
|
|||||||
{updateError && message.error('Failed to update the Policy :(')}
|
{updateError && message.error('Failed to update the Policy :(')}
|
||||||
<PoliciesContainer>
|
<PoliciesContainer>
|
||||||
<PoliciesHeaderContainer>
|
<PoliciesHeaderContainer>
|
||||||
<PoliciesTitle level={2}>Manage Policies</PoliciesTitle>
|
<PoliciesTitle level={2}>Manage Access Policies</PoliciesTitle>
|
||||||
<Typography.Paragraph type="secondary">
|
<Typography.Paragraph type="secondary">
|
||||||
Manage access for DataHub Users & Groups using Policies.
|
Manage access for DataHub Users & Groups using Access Policies.
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
</PoliciesHeaderContainer>
|
</PoliciesHeaderContainer>
|
||||||
</PoliciesContainer>
|
</PoliciesContainer>
|
||||||
@ -492,6 +494,6 @@ export const PoliciesPage = () => {
|
|||||||
privileges={getPrivilegeNames(focusPolicy)}
|
privileges={getPrivilegeNames(focusPolicy)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</SearchablePage>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -1,8 +1,14 @@
|
|||||||
import { Menu, Typography } from 'antd';
|
import React from 'react';
|
||||||
import React, { useState } from 'react';
|
import { Menu, Typography, Divider } from 'antd';
|
||||||
|
import { BankOutlined, SafetyCertificateOutlined, UsergroupAddOutlined } from '@ant-design/icons';
|
||||||
|
import { Redirect, Route, useHistory, useLocation, useRouteMatch, Switch } from 'react-router';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { ANTD_GRAY } from '../entity/shared/constants';
|
import { ANTD_GRAY } from '../entity/shared/constants';
|
||||||
|
import { ManageIdentities } from '../identity/ManageIdentities';
|
||||||
|
import { ManagePolicies } from '../policy/ManagePolicies';
|
||||||
import { SearchablePage } from '../search/SearchablePage';
|
import { SearchablePage } from '../search/SearchablePage';
|
||||||
|
import { useAppConfig } from '../useAppConfig';
|
||||||
|
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
|
||||||
import { AccessTokens } from './AccessTokens';
|
import { AccessTokens } from './AccessTokens';
|
||||||
|
|
||||||
const PageContainer = styled.div`
|
const PageContainer = styled.div`
|
||||||
@ -28,12 +34,48 @@ const PageTitle = styled(Typography.Title)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsPage = () => {
|
const ThinDivider = styled(Divider)`
|
||||||
const [selectedKey, setSelectedKey] = useState('access-tokens');
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
const onMenuClick = ({ key }) => {
|
const ItemTitle = styled.span`
|
||||||
setSelectedKey(key);
|
margin-left: 8px;
|
||||||
};
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL Paths for each settings page.
|
||||||
|
*/
|
||||||
|
const PATHS = [
|
||||||
|
{ path: 'tokens', content: <AccessTokens /> },
|
||||||
|
{ path: 'identities', content: <ManageIdentities /> },
|
||||||
|
{ path: 'policies', content: <ManagePolicies /> },
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default selected path
|
||||||
|
*/
|
||||||
|
const DEFAULT_PATH = PATHS[0];
|
||||||
|
|
||||||
|
export const SettingsPage = () => {
|
||||||
|
const { path, url } = useRouteMatch();
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const history = useHistory();
|
||||||
|
const subRoutes = PATHS.map((p) => p.path.replace('/', ''));
|
||||||
|
const currPathName = pathname.replace(path, '');
|
||||||
|
const trimmedPathName = currPathName.endsWith('/') ? pathname.slice(0, pathname.length - 1) : currPathName;
|
||||||
|
const splitPathName = trimmedPathName.split('/');
|
||||||
|
const providedPath = splitPathName[1];
|
||||||
|
const activePath = subRoutes.includes(providedPath) ? providedPath : DEFAULT_PATH.path.replace('/', '');
|
||||||
|
|
||||||
|
const me = useGetAuthenticatedUser();
|
||||||
|
const { config } = useAppConfig();
|
||||||
|
|
||||||
|
const isPoliciesEnabled = config?.policiesConfig.enabled;
|
||||||
|
const isIdentityManagementEnabled = config?.identityManagementConfig.enabled;
|
||||||
|
|
||||||
|
const showPolicies = (isPoliciesEnabled && me && me.platformPrivileges.managePolicies) || false;
|
||||||
|
const showUsersGroups = (isIdentityManagementEnabled && me && me.platformPrivileges.manageIdentities) || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchablePage>
|
<SearchablePage>
|
||||||
@ -43,19 +85,48 @@ export const SettingsPage = () => {
|
|||||||
<PageTitle level={3}>Settings</PageTitle>
|
<PageTitle level={3}>Settings</PageTitle>
|
||||||
<Typography.Paragraph type="secondary">Manage your DataHub settings.</Typography.Paragraph>
|
<Typography.Paragraph type="secondary">Manage your DataHub settings.</Typography.Paragraph>
|
||||||
</SettingsBarHeader>
|
</SettingsBarHeader>
|
||||||
|
<ThinDivider />
|
||||||
<Menu
|
<Menu
|
||||||
selectable={false}
|
selectable={false}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
style={{ width: 256 }}
|
style={{ width: 256, marginTop: 8 }}
|
||||||
selectedKeys={[selectedKey]}
|
selectedKeys={[activePath]}
|
||||||
onClick={(key) => {
|
onClick={(newPath) => {
|
||||||
onMenuClick(key);
|
history.push(`${url}/${newPath.key}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu.Item key="access-tokens">Access Tokens</Menu.Item>
|
<Menu.ItemGroup title="Developer">
|
||||||
|
<Menu.Item key="tokens">
|
||||||
|
<SafetyCertificateOutlined />
|
||||||
|
<ItemTitle>Access Tokens</ItemTitle>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
{(showPolicies || showUsersGroups) && (
|
||||||
|
<Menu.ItemGroup title="Access">
|
||||||
|
{showPolicies && (
|
||||||
|
<Menu.Item key="identities">
|
||||||
|
<UsergroupAddOutlined />
|
||||||
|
<ItemTitle>Users & Groups</ItemTitle>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
{showUsersGroups && (
|
||||||
|
<Menu.Item key="policies">
|
||||||
|
<BankOutlined />
|
||||||
|
<ItemTitle>Privileges</ItemTitle>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
</SettingsBarContainer>
|
</SettingsBarContainer>
|
||||||
{selectedKey === 'access-tokens' && <AccessTokens />}
|
<Switch>
|
||||||
|
<Route exact path={path}>
|
||||||
|
<Redirect to={`${pathname}${pathname.endsWith('/') ? '' : '/'}${DEFAULT_PATH.path}`} />
|
||||||
|
</Route>
|
||||||
|
{PATHS.map((p) => (
|
||||||
|
<Route path={`${path}/${p.path.replace('/', '')}`} render={() => p.content} key={p.path} />
|
||||||
|
))}
|
||||||
|
</Switch>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</SearchablePage>
|
</SearchablePage>
|
||||||
);
|
);
|
||||||
|
23
datahub-web-react/src/app/shared/CopyUrn.tsx
Normal file
23
datahub-web-react/src/app/shared/CopyUrn.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface CopyUrnProps {
|
||||||
|
urn: string;
|
||||||
|
isActive: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CopyUrn({ urn, isActive, onClick }: CopyUrnProps) {
|
||||||
|
return (
|
||||||
|
<Tooltip title="Copy URN. An URN uniquely identifies an entity on DataHub.">
|
||||||
|
<Button
|
||||||
|
icon={isActive ? <CheckOutlined /> : <CopyOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(urn);
|
||||||
|
onClick();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
@ -27,8 +27,8 @@ const MenuItem = styled(Menu.Item)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const DownArrow = styled(CaretDownOutlined)`
|
const DownArrow = styled(CaretDownOutlined)`
|
||||||
vertical-align: -3px;
|
vertical-align: -1px;
|
||||||
font-size: 12px;
|
font-size: 10px;
|
||||||
color: ${ANTD_GRAY[7]};
|
color: ${ANTD_GRAY[7]};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ export const ManageAccount = ({ urn: _urn, pictureLink: _pictureLink, name }: Pr
|
|||||||
return (
|
return (
|
||||||
<Dropdown overlay={menu}>
|
<Dropdown overlay={menu}>
|
||||||
<StyledLink to={`/${entityRegistry.getPathName(EntityType.CorpUser)}/${_urn}`}>
|
<StyledLink to={`/${entityRegistry.getPathName(EntityType.CorpUser)}/${_urn}`}>
|
||||||
<CustomAvatar photoUrl={_pictureLink} style={{ marginRight: 5 }} name={name} />
|
<CustomAvatar photoUrl={_pictureLink} style={{ marginRight: 4 }} name={name} />
|
||||||
<DownArrow />
|
<DownArrow />
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
@ -18,6 +18,9 @@ import { useGetSearchResultsForMultipleQuery } from '../../graphql/search.genera
|
|||||||
import analytics, { EventType, EntityActionType } from '../analytics';
|
import analytics, { EventType, EntityActionType } from '../analytics';
|
||||||
import { GetSearchResultsParams, SearchResultInterface } from '../entity/shared/components/styled/search/types';
|
import { GetSearchResultsParams, SearchResultInterface } from '../entity/shared/components/styled/search/types';
|
||||||
import { AddOwnersModal } from '../entity/shared/containers/profile/sidebar/Ownership/AddOwnersModal';
|
import { AddOwnersModal } from '../entity/shared/containers/profile/sidebar/Ownership/AddOwnersModal';
|
||||||
|
import CopyUrn from './CopyUrn';
|
||||||
|
import EntityDropdown from '../entity/shared/EntityDropdown';
|
||||||
|
import { EntityMenuItems } from '../entity/shared/EntityDropdown/EntityDropdown';
|
||||||
|
|
||||||
function useWrappedSearchResults(params: GetSearchResultsParams) {
|
function useWrappedSearchResults(params: GetSearchResultsParams) {
|
||||||
const { data, loading, error } = useGetSearchResultsForMultipleQuery(params);
|
const { data, loading, error } = useGetSearchResultsForMultipleQuery(params);
|
||||||
@ -146,6 +149,16 @@ const TagName = styled.div`
|
|||||||
justify-content: left;
|
justify-content: left;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ActionButtons = styled.div`
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TagHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: top;
|
||||||
|
`;
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -180,6 +193,7 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
|
|||||||
const [colorValue, setColorValue] = useState('');
|
const [colorValue, setColorValue] = useState('');
|
||||||
const ownersEmpty = !data?.tag?.ownership?.owners?.length;
|
const ownersEmpty = !data?.tag?.ownership?.owners?.length;
|
||||||
const [showAddModal, setShowAddModal] = useState(false);
|
const [showAddModal, setShowAddModal] = useState(false);
|
||||||
|
const [copiedUrn, setCopiedUrn] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUpdatedDescription(description);
|
setUpdatedDescription(description);
|
||||||
@ -302,22 +316,28 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Tag Title */}
|
{/* Tag Title */}
|
||||||
<div>
|
<TagHeader>
|
||||||
<TitleLabel>Tag</TitleLabel>
|
<div>
|
||||||
<TagName>
|
<TitleLabel>Tag</TitleLabel>
|
||||||
<ColorPicker>
|
<TagName>
|
||||||
<ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} />
|
<ColorPicker>
|
||||||
</ColorPicker>
|
<ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} />
|
||||||
<TitleText>
|
</ColorPicker>
|
||||||
{(data?.tag && entityRegistry.getDisplayName(EntityType.Tag, data?.tag)) || ''}
|
<TitleText>
|
||||||
</TitleText>
|
{(data?.tag && entityRegistry.getDisplayName(EntityType.Tag, data?.tag)) || ''}
|
||||||
</TagName>
|
</TitleText>
|
||||||
|
</TagName>
|
||||||
|
</div>
|
||||||
|
<ActionButtons>
|
||||||
|
<CopyUrn urn={urn} isActive={copiedUrn} onClick={() => setCopiedUrn(true)} />
|
||||||
|
<EntityDropdown menuItems={new Set([EntityMenuItems.COPY_URL])} />
|
||||||
|
</ActionButtons>
|
||||||
{displayColorPicker && (
|
{displayColorPicker && (
|
||||||
<ColorPickerPopOver ref={colorPickerRef}>
|
<ColorPickerPopOver ref={colorPickerRef}>
|
||||||
<ChromePicker color={colorValue} onChange={handleColorChange} />
|
<ChromePicker color={colorValue} onChange={handleColorChange} />
|
||||||
</ColorPickerPopOver>
|
</ColorPickerPopOver>
|
||||||
)}
|
)}
|
||||||
</div>
|
</TagHeader>
|
||||||
<Divider />
|
<Divider />
|
||||||
{/* Tag Description */}
|
{/* Tag Description */}
|
||||||
<DescriptionLabel>About</DescriptionLabel>
|
<DescriptionLabel>About</DescriptionLabel>
|
||||||
|
@ -2,13 +2,11 @@ import styled from 'styled-components';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
ApiOutlined,
|
ApiOutlined,
|
||||||
BankOutlined,
|
|
||||||
BarChartOutlined,
|
BarChartOutlined,
|
||||||
BookOutlined,
|
BookOutlined,
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
UsergroupAddOutlined,
|
|
||||||
FolderOutlined,
|
FolderOutlined,
|
||||||
ContainerOutlined,
|
SolutionOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -17,7 +15,7 @@ import { useAppConfig } from '../../useAppConfig';
|
|||||||
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
|
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
|
||||||
|
|
||||||
const AdminLink = styled.span`
|
const AdminLink = styled.span`
|
||||||
margin-right: 4px;
|
margin-right: 0px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LinksWrapper = styled.div<{ areLinksHidden?: boolean }>`
|
const LinksWrapper = styled.div<{ areLinksHidden?: boolean }>`
|
||||||
@ -48,14 +46,9 @@ export function AdminHeaderLinks(props: Props) {
|
|||||||
const { config } = useAppConfig();
|
const { config } = useAppConfig();
|
||||||
|
|
||||||
const isAnalyticsEnabled = config?.analyticsConfig.enabled;
|
const isAnalyticsEnabled = config?.analyticsConfig.enabled;
|
||||||
const isPoliciesEnabled = config?.policiesConfig.enabled;
|
|
||||||
const isIdentityManagementEnabled = config?.identityManagementConfig.enabled;
|
|
||||||
const isIngestionEnabled = config?.managedIngestionConfig.enabled;
|
const isIngestionEnabled = config?.managedIngestionConfig.enabled;
|
||||||
|
|
||||||
const showAnalytics = (isAnalyticsEnabled && me && me.platformPrivileges.viewAnalytics) || false;
|
const showAnalytics = (isAnalyticsEnabled && me && me.platformPrivileges.viewAnalytics) || false;
|
||||||
const showPolicyBuilder = (isPoliciesEnabled && me && me.platformPrivileges.managePolicies) || false;
|
|
||||||
const showIdentityManagement =
|
|
||||||
(isIdentityManagementEnabled && me && me.platformPrivileges.manageIdentities) || false;
|
|
||||||
const showSettings = true;
|
const showSettings = true;
|
||||||
const showIngestion =
|
const showIngestion =
|
||||||
isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets;
|
isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets;
|
||||||
@ -73,15 +66,6 @@ export function AdminHeaderLinks(props: Props) {
|
|||||||
</Link>
|
</Link>
|
||||||
</AdminLink>
|
</AdminLink>
|
||||||
)}
|
)}
|
||||||
{showIdentityManagement && (
|
|
||||||
<AdminLink>
|
|
||||||
<Link to="/identities">
|
|
||||||
<Button type="text">
|
|
||||||
<UsergroupAddOutlined /> Users & Groups
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</AdminLink>
|
|
||||||
)}
|
|
||||||
{showIngestion && (
|
{showIngestion && (
|
||||||
<AdminLink>
|
<AdminLink>
|
||||||
<Link to="/ingestion">
|
<Link to="/ingestion">
|
||||||
@ -91,15 +75,6 @@ export function AdminHeaderLinks(props: Props) {
|
|||||||
</Link>
|
</Link>
|
||||||
</AdminLink>
|
</AdminLink>
|
||||||
)}
|
)}
|
||||||
{showPolicyBuilder && (
|
|
||||||
<AdminLink>
|
|
||||||
<Link to="/policies">
|
|
||||||
<Button type="text">
|
|
||||||
<BankOutlined /> Policies
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</AdminLink>
|
|
||||||
)}
|
|
||||||
{(showGlossary || showDomains) && (
|
{(showGlossary || showDomains) && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
@ -124,13 +99,13 @@ export function AdminHeaderLinks(props: Props) {
|
|||||||
>
|
>
|
||||||
<AdminLink>
|
<AdminLink>
|
||||||
<Button type="text">
|
<Button type="text">
|
||||||
<ContainerOutlined /> Manage <DownOutlined style={{ fontSize: '12px' }} />
|
<SolutionOutlined /> Govern <DownOutlined style={{ fontSize: '6px' }} />
|
||||||
</Button>
|
</Button>
|
||||||
</AdminLink>
|
</AdminLink>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)}
|
)}
|
||||||
{showSettings && (
|
{showSettings && (
|
||||||
<AdminLink style={{ marginRight: 16 }}>
|
<AdminLink style={{ marginRight: 12 }}>
|
||||||
<Link to="/settings">
|
<Link to="/settings">
|
||||||
<Button type="text">
|
<Button type="text">
|
||||||
<SettingOutlined />
|
<SettingOutlined />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user