refactor(ui): UI Navigation Refactoring (#5076)

This commit is contained in:
John Joyce 2022-06-03 07:47:39 -07:00 committed by GitHub
parent ccf82229d5
commit 4da3d132f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 179 additions and 95 deletions

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { Switch, Route, Redirect } from 'react-router-dom';
import { Layout } from 'antd';
import { BrowseResultsPage } from './browse/BrowseResultsPage';
import { EntityPage } from './entity/EntityPage';
@ -8,13 +8,11 @@ import { useEntityRegistry } from './useEntityRegistry';
import { HomePage } from './home/HomePage';
import { SearchPage } from './search/SearchPage';
import { AnalyticsPage } from './analyticsDashboard/components/AnalyticsPage';
import { PoliciesPage } from './policy/PoliciesPage';
import AppConfigProvider from '../AppConfigProvider';
import { ManageIdentitiesPage } from './identity/ManageIdentitiesPage';
import { SettingsPage } from './settings/SettingsPage';
import { ManageIngestionPage } from './ingest/ManageIngestionPage';
import { ManageDomainsPage } from './domain/ManageDomainsPage';
import BusinessGlossaryPage from './glossary/BusinessGlossaryPage';
import { SettingsPage } from './settings/SettingsPage';
/**
* 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.BROWSE_RESULTS} render={() => <BrowseResultsPage />} />
<Route path={PageRoutes.ANALYTICS} render={() => <AnalyticsPage />} />
<Route path={PageRoutes.POLICIES} render={() => <PoliciesPage />} />
<Route path={PageRoutes.IDENTITIES} render={() => <ManageIdentitiesPage />} />
<Route path={PageRoutes.POLICIES} render={() => <Redirect to="/settings/policies" />} />
<Route path={PageRoutes.IDENTITIES} render={() => <Redirect to="/settings/identities" />} />
<Route path={PageRoutes.DOMAINS} render={() => <ManageDomainsPage />} />
<Route path={PageRoutes.INGESTION} render={() => <ManageIngestionPage />} />
<Route path={PageRoutes.SETTINGS} render={() => <SettingsPage />} />

View File

@ -14,6 +14,7 @@ import { SidebarRecommendationsSection } from '../shared/containers/profile/side
import { SidebarTagsSection } from '../shared/containers/profile/sidebar/SidebarTagsSection';
import { PropertiesTab } from '../shared/tabs/Properties/PropertiesTab';
import { SidebarDomainSection } from '../shared/containers/profile/sidebar/Domain/SidebarDomainSection';
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
/**
* Definition of the DataHub Container entity.
@ -67,6 +68,7 @@ export class ContainerEntity implements Entity<Container> {
useEntityQuery={useGetContainerQuery}
useUpdateQuery={undefined}
getOverrideProperties={this.getOverridePropertiesFromEntity}
headerDropdownItems={new Set([EntityMenuItems.COPY_URL])}
tabs={[
{
name: 'Entities',

View File

@ -10,6 +10,7 @@ import { SidebarOwnerSection } from '../shared/containers/profile/sidebar/Owners
import { getDataForEntityType } from '../shared/containers/profile/utils';
import { useGetDomainQuery } from '../../../graphql/domain.generated';
import { DomainEntitiesTab } from './DomainEntitiesTab';
import { EntityMenuItems } from '../shared/EntityDropdown/EntityDropdown';
/**
* Definition of the DataHub Domain entity.
@ -63,6 +64,7 @@ export class DomainEntity implements Entity<Domain> {
useEntityQuery={useGetDomainQuery}
useUpdateQuery={undefined}
getOverrideProperties={this.getOverridePropertiesFromEntity}
headerDropdownItems={new Set([EntityMenuItems.COPY_URL])}
tabs={[
{
name: 'Entities',

View File

@ -1,5 +1,5 @@
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 styled from 'styled-components/macro';
import moment from 'moment';
@ -16,6 +16,7 @@ import { useGetAuthenticatedUser } from '../../../../../useGetAuthenticatedUser'
import { EntityType, PlatformPrivileges } from '../../../../../../types.generated';
import EntityCount from './EntityCount';
import EntityName from './EntityName';
import CopyUrn from '../../../../../shared/CopyUrn';
const TitleWrapper = styled.div`
display: flex;
@ -189,15 +190,7 @@ export const EntityHeader = ({ refreshBrowser, headerDropdownItems, isNameEditab
</MainHeaderContent>
<SideHeaderContent>
<TopButtonsWrapper>
<Tooltip title="Copy URN. An URN uniquely identifies an entity on DataHub.">
<Button
icon={copiedUrn ? <CheckOutlined /> : <CopyOutlined />}
onClick={() => {
navigator.clipboard.writeText(urn);
setCopiedUrn(true);
}}
/>
</Tooltip>
<CopyUrn urn={urn} isActive={copiedUrn} onClick={() => setCopiedUrn(true)} />
{headerDropdownItems && (
<EntityDropdown
menuItems={headerDropdownItems}

View File

@ -1,13 +1,13 @@
import React from 'react';
import { Typography } from 'antd';
import styled from 'styled-components';
import { SearchablePage } from '../search/SearchablePage';
import { RoutedTabs } from '../shared/RoutedTabs';
import { GroupList } from './group/GroupList';
import { UserList } from './user/UserList';
const PageContainer = styled.div`
padding-top: 20px;
width: 100%;
`;
const PageHeaderContainer = styled.div`
@ -30,7 +30,7 @@ const Content = styled.div`
height: calc(100vh - 60px);
&&& .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];
export const ManageIdentitiesPage = () => {
export const ManageIdentities = () => {
/**
* Determines which view should be visible: users or groups list.
*/
@ -70,18 +70,16 @@ export const ManageIdentitiesPage = () => {
const onTabChange = () => null;
return (
<SearchablePage>
<PageContainer>
<PageHeaderContainer>
<PageTitle level={3}>Manage Users & Groups</PageTitle>
<Typography.Paragraph type="secondary">
View your DataHub users & groups. Take administrative actions.
</Typography.Paragraph>
</PageHeaderContainer>
<Content>
<RoutedTabs defaultPath={defaultTabPath} tabs={getTabs()} onTabChange={onTabChange} />
</Content>
</PageContainer>
</SearchablePage>
<PageContainer>
<PageHeaderContainer>
<PageTitle level={3}>Manage Users & Groups</PageTitle>
<Typography.Paragraph type="secondary">
View your DataHub users & groups. Take administrative actions.
</Typography.Paragraph>
</PageHeaderContainer>
<Content>
<RoutedTabs defaultPath={defaultTabPath} tabs={getTabs()} onTabChange={onTabChange} />
</Content>
</PageContainer>
);
};

View File

@ -4,8 +4,6 @@ import styled from 'styled-components';
import * as QueryString from 'query-string';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { useLocation } from 'react-router';
import { SearchablePage } from '../search/SearchablePage';
import PolicyBuilderModal from './PolicyBuilderModal';
import {
Policy,
@ -86,6 +84,10 @@ const EditPolicyButton = styled(Button)`
margin-right: 16px;
`;
const PageContainer = styled.span`
width: 100%;
`;
const DEFAULT_PAGE_SIZE = 10;
type PrivilegeOptionType = {
@ -139,7 +141,7 @@ const toPolicyInput = (policy: Omit<Policy, 'urn'>): PolicyUpdateInput => {
};
// TODO: Cleanup the styling.
export const PoliciesPage = () => {
export const ManagePolicies = () => {
const entityRegistry = useEntityRegistry();
const location = useLocation();
const params = QueryString.parse(location.search, { arrayFormat: 'comma' });
@ -416,7 +418,7 @@ export const PoliciesPage = () => {
}));
return (
<SearchablePage>
<PageContainer>
{policiesLoading && !policiesData && (
<Message type="loading" content="Loading policies..." style={{ marginTop: '10%' }} />
)}
@ -424,9 +426,9 @@ export const PoliciesPage = () => {
{updateError && message.error('Failed to update the Policy :(')}
<PoliciesContainer>
<PoliciesHeaderContainer>
<PoliciesTitle level={2}>Manage Policies</PoliciesTitle>
<PoliciesTitle level={2}>Manage Access Policies</PoliciesTitle>
<Typography.Paragraph type="secondary">
Manage access for DataHub Users & Groups using Policies.
Manage access for DataHub Users & Groups using Access Policies.
</Typography.Paragraph>
</PoliciesHeaderContainer>
</PoliciesContainer>
@ -492,6 +494,6 @@ export const PoliciesPage = () => {
privileges={getPrivilegeNames(focusPolicy)}
/>
)}
</SearchablePage>
</PageContainer>
);
};

View File

@ -1,8 +1,14 @@
import { Menu, Typography } from 'antd';
import React, { useState } from 'react';
import React 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 { ANTD_GRAY } from '../entity/shared/constants';
import { ManageIdentities } from '../identity/ManageIdentities';
import { ManagePolicies } from '../policy/ManagePolicies';
import { SearchablePage } from '../search/SearchablePage';
import { useAppConfig } from '../useAppConfig';
import { useGetAuthenticatedUser } from '../useGetAuthenticatedUser';
import { AccessTokens } from './AccessTokens';
const PageContainer = styled.div`
@ -28,12 +34,48 @@ const PageTitle = styled(Typography.Title)`
}
`;
export const SettingsPage = () => {
const [selectedKey, setSelectedKey] = useState('access-tokens');
const ThinDivider = styled(Divider)`
padding: 0px;
margin: 0px;
`;
const onMenuClick = ({ key }) => {
setSelectedKey(key);
};
const ItemTitle = styled.span`
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 (
<SearchablePage>
@ -43,19 +85,48 @@ export const SettingsPage = () => {
<PageTitle level={3}>Settings</PageTitle>
<Typography.Paragraph type="secondary">Manage your DataHub settings.</Typography.Paragraph>
</SettingsBarHeader>
<ThinDivider />
<Menu
selectable={false}
mode="inline"
style={{ width: 256 }}
selectedKeys={[selectedKey]}
onClick={(key) => {
onMenuClick(key);
style={{ width: 256, marginTop: 8 }}
selectedKeys={[activePath]}
onClick={(newPath) => {
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>
</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>
</SearchablePage>
);

View 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>
);
}

View File

@ -27,8 +27,8 @@ const MenuItem = styled(Menu.Item)`
`;
const DownArrow = styled(CaretDownOutlined)`
vertical-align: -3px;
font-size: 12px;
vertical-align: -1px;
font-size: 10px;
color: ${ANTD_GRAY[7]};
`;
@ -90,7 +90,7 @@ export const ManageAccount = ({ urn: _urn, pictureLink: _pictureLink, name }: Pr
return (
<Dropdown overlay={menu}>
<StyledLink to={`/${entityRegistry.getPathName(EntityType.CorpUser)}/${_urn}`}>
<CustomAvatar photoUrl={_pictureLink} style={{ marginRight: 5 }} name={name} />
<CustomAvatar photoUrl={_pictureLink} style={{ marginRight: 4 }} name={name} />
<DownArrow />
</StyledLink>
</Dropdown>

View File

@ -18,6 +18,9 @@ import { useGetSearchResultsForMultipleQuery } from '../../graphql/search.genera
import analytics, { EventType, EntityActionType } from '../analytics';
import { GetSearchResultsParams, SearchResultInterface } from '../entity/shared/components/styled/search/types';
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) {
const { data, loading, error } = useGetSearchResultsForMultipleQuery(params);
@ -146,6 +149,16 @@ const TagName = styled.div`
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;
type Props = {
@ -180,6 +193,7 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
const [colorValue, setColorValue] = useState('');
const ownersEmpty = !data?.tag?.ownership?.owners?.length;
const [showAddModal, setShowAddModal] = useState(false);
const [copiedUrn, setCopiedUrn] = useState(false);
useEffect(() => {
setUpdatedDescription(description);
@ -302,22 +316,28 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
return (
<>
{/* Tag Title */}
<div>
<TitleLabel>Tag</TitleLabel>
<TagName>
<ColorPicker>
<ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} />
</ColorPicker>
<TitleText>
{(data?.tag && entityRegistry.getDisplayName(EntityType.Tag, data?.tag)) || ''}
</TitleText>
</TagName>
<TagHeader>
<div>
<TitleLabel>Tag</TitleLabel>
<TagName>
<ColorPicker>
<ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} />
</ColorPicker>
<TitleText>
{(data?.tag && entityRegistry.getDisplayName(EntityType.Tag, data?.tag)) || ''}
</TitleText>
</TagName>
</div>
<ActionButtons>
<CopyUrn urn={urn} isActive={copiedUrn} onClick={() => setCopiedUrn(true)} />
<EntityDropdown menuItems={new Set([EntityMenuItems.COPY_URL])} />
</ActionButtons>
{displayColorPicker && (
<ColorPickerPopOver ref={colorPickerRef}>
<ChromePicker color={colorValue} onChange={handleColorChange} />
</ColorPickerPopOver>
)}
</div>
</TagHeader>
<Divider />
{/* Tag Description */}
<DescriptionLabel>About</DescriptionLabel>

View File

@ -2,13 +2,11 @@ import styled from 'styled-components';
import * as React from 'react';
import {
ApiOutlined,
BankOutlined,
BarChartOutlined,
BookOutlined,
SettingOutlined,
UsergroupAddOutlined,
FolderOutlined,
ContainerOutlined,
SolutionOutlined,
DownOutlined,
} from '@ant-design/icons';
import { Link } from 'react-router-dom';
@ -17,7 +15,7 @@ import { useAppConfig } from '../../useAppConfig';
import { useGetAuthenticatedUser } from '../../useGetAuthenticatedUser';
const AdminLink = styled.span`
margin-right: 4px;
margin-right: 0px;
`;
const LinksWrapper = styled.div<{ areLinksHidden?: boolean }>`
@ -48,14 +46,9 @@ export function AdminHeaderLinks(props: Props) {
const { config } = useAppConfig();
const isAnalyticsEnabled = config?.analyticsConfig.enabled;
const isPoliciesEnabled = config?.policiesConfig.enabled;
const isIdentityManagementEnabled = config?.identityManagementConfig.enabled;
const isIngestionEnabled = config?.managedIngestionConfig.enabled;
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 showIngestion =
isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets;
@ -73,15 +66,6 @@ export function AdminHeaderLinks(props: Props) {
</Link>
</AdminLink>
)}
{showIdentityManagement && (
<AdminLink>
<Link to="/identities">
<Button type="text">
<UsergroupAddOutlined /> Users & Groups
</Button>
</Link>
</AdminLink>
)}
{showIngestion && (
<AdminLink>
<Link to="/ingestion">
@ -91,15 +75,6 @@ export function AdminHeaderLinks(props: Props) {
</Link>
</AdminLink>
)}
{showPolicyBuilder && (
<AdminLink>
<Link to="/policies">
<Button type="text">
<BankOutlined /> Policies
</Button>
</Link>
</AdminLink>
)}
{(showGlossary || showDomains) && (
<Dropdown
trigger={['click']}
@ -124,13 +99,13 @@ export function AdminHeaderLinks(props: Props) {
>
<AdminLink>
<Button type="text">
<ContainerOutlined /> Manage <DownOutlined style={{ fontSize: '12px' }} />
<SolutionOutlined /> Govern <DownOutlined style={{ fontSize: '6px' }} />
</Button>
</AdminLink>
</Dropdown>
)}
{showSettings && (
<AdminLink style={{ marginRight: 16 }}>
<AdminLink style={{ marginRight: 12 }}>
<Link to="/settings">
<Button type="text">
<SettingOutlined />