Skip to content

Commit

Permalink
feat(ui): Add support for Deprecating an entity via the UI (#4633)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ankit-Keshari-Vituity authored Apr 15, 2022
1 parent d9aedca commit e572af6
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { Button, DatePicker, Form, Input, message, Modal } from 'antd';
import { useUpdateDeprecationMutation } from '../../../../../../graphql/mutations.generated';

type Props = {
urn: string;
visible: boolean;
onClose: () => void;
refetch?: () => Promise<any>;
};

export const AddDeprecationDetailsModal = ({ urn, visible, onClose, refetch }: Props) => {
const [updateDeprecation] = useUpdateDeprecationMutation();
const [form] = Form.useForm();

const handleClose = () => {
form.resetFields();
onClose();
};

const handleOk = async (formData: any) => {
message.loading({ content: 'Updating...' });
try {
await updateDeprecation({
variables: {
input: {
urn,
deprecated: true,
note: formData.note,
decommissionTime: formData.decommissionTime && formData.decommissionTime.unix(),
},
},
});
message.destroy();
message.success({ content: 'Deprecation Updated', duration: 2 });
} catch (e: unknown) {
message.destroy();
if (e instanceof Error) {
message.error({ content: `Failed to update Deprecation: \n ${e.message || ''}`, duration: 2 });
}
}
refetch?.();
handleClose();
};

return (
<Modal
title="Add Deprecation Details"
visible={visible}
onCancel={handleClose}
keyboard
footer={
<>
<Button onClick={handleClose} type="text">
Cancel
</Button>
<Button form="addDeprecationForm" key="submit" htmlType="submit">
Ok
</Button>
</>
}
>
<Form form={form} name="addDeprecationForm" onFinish={handleOk} layout="vertical">
<Form.Item name="note" label="Note" rules={[{ whitespace: true }, { min: 0, max: 100 }]}>
<Input placeholder="Add Note" autoFocus />
</Form.Item>
<Form.Item name="decommissionTime" label="Decommission Date">
<DatePicker style={{ width: '100%' }} />
</Form.Item>
</Form>
</Modal>
);
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { CheckOutlined, CopyOutlined, FolderOpenOutlined } from '@ant-design/icons';
import { Typography, Image, Button, Tooltip } from 'antd';
import React, { useState } from 'react';
import { CheckOutlined, CopyOutlined, FolderOpenOutlined, InfoCircleOutlined, MoreOutlined } from '@ant-design/icons';
import { Typography, Image, Button, Tooltip, Menu, Dropdown, message, Popover } from 'antd';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import moment from 'moment';

import { EntityType } from '../../../../../../types.generated';
import { capitalizeFirstLetterOnly } from '../../../../../shared/textUtil';
import { useEntityRegistry } from '../../../../../useEntityRegistry';
import { IconStyleType } from '../../../../Entity';
import { ANTD_GRAY } from '../../../constants';
import { useEntityData } from '../../../EntityContext';
import { useEntityData, useRefetch } from '../../../EntityContext';
import { useEntityPath } from '../utils';
import analytics, { EventType, EntityActionType } from '../../../../../analytics';
import { EntityHealthStatus } from './EntityHealthStatus';
import { useUpdateDeprecationMutation } from '../../../../../../graphql/mutations.generated';
import { getLocaleTimezone } from '../../../../../shared/time/timeUtils';
import { AddDeprecationDetailsModal } from './AddDeprecationDetailsModal';

const LogoContainer = styled.span`
margin-right: 10px;
Expand Down Expand Up @@ -93,8 +98,59 @@ const ContainerIcon = styled(FolderOpenOutlined)`
}
`;

const DeprecatedContainer = styled.div`
width: 110px;
height: 18px;
border: 1px solid #ef5b5b;
border-radius: 15px;
display: flex;
justify-content: center;
align-items: center;
color: #ef5b5b;
margin-left: 15px;
padding-top: 12px;
padding-bottom: 12px;
`;

const DeprecatedText = styled.div`
color: #ef5b5b;
margin-left: 5px;
`;

const MenuIcon = styled(MoreOutlined)`
display: flex;
justify-content: center;
align-items: center;
font-size: 25px;
height: 32px;
margin-left: 5px;
`;

const MenuItem = styled.div`
font-size: 12px;
padding-left: 12px;
padding-right: 12px;
color: rgba(0, 0, 0, 0.85);
`;

const LastEvaluatedAtLabel = styled.div`
padding: 0;
margin: 0;
display: flex;
align-items: center;
color: ${ANTD_GRAY[7]};
`;

const Divider = styled.div`
border-top: 1px solid #f0f0f0;
padding-top: 5px;
`;

export const EntityHeader = () => {
const { urn, entityType, entityData } = useEntityData();
const [updateDeprecation] = useUpdateDeprecationMutation();
const [showAddDeprecationDetailsModal, setShowAddDeprecationDetailsModal] = useState(false);
const refetch = useRefetch();
const entityRegistry = useEntityRegistry();
const [copiedUrn, setCopiedUrn] = useState(false);
const basePlatformName = entityData?.platform?.properties?.displayName || entityData?.platform?.name;
Expand All @@ -120,68 +176,170 @@ export const EntityHeader = () => {
const entityCount = entityData?.entityCount;
const typeIcon = entityRegistry.getIcon(entityType, 12, IconStyleType.ACCENT);
const container = entityData?.container;

// Update the Deprecation
const handleUpdateDeprecation = async (deprecatedStatus: boolean) => {
message.loading({ content: 'Updating...' });
try {
await updateDeprecation({
variables: {
input: {
urn,
deprecated: deprecatedStatus,
note: '',
decommissionTime: null,
},
},
});
message.destroy();
message.success({ content: 'Deprecation Updated', duration: 2 });
} catch (e: unknown) {
message.destroy();
if (e instanceof Error) {
message.error({ content: `Failed to update Deprecation: \n ${e.message || ''}`, duration: 2 });
}
}
refetch?.();
};

const menu = (
<Menu>
<Menu.Item key="0">
{!entityData?.deprecation?.deprecated ? (
<MenuItem onClick={() => setShowAddDeprecationDetailsModal(true)}>Mark as deprecated</MenuItem>
) : (
<MenuItem onClick={() => handleUpdateDeprecation(false)}>Mark as un-deprecated</MenuItem>
)}
</Menu.Item>
</Menu>
);

/**
* Deprecation Decommission Timestamp
*/
const localeTimezone = getLocaleTimezone();
const decommissionTimeLocal =
(entityData?.deprecation?.decommissionTime &&
`Scheduled to be decommissioned on ${moment
.unix(entityData?.deprecation?.decommissionTime)
.format('DD/MMM/YYYY')} at ${moment
.unix(entityData?.deprecation?.decommissionTime)
.format('HH:mm:ss')} (${localeTimezone})`) ||
undefined;
const decommissionTimeGMT =
entityData?.deprecation?.decommissionTime &&
moment.unix(entityData?.deprecation?.decommissionTime).utc().format('dddd, DD/MMM/YYYY HH:mm:ss z');

const hasDetails = entityData?.deprecation?.note !== '' || entityData?.deprecation?.decommissionTime !== null;
const isDividerNeeded = entityData?.deprecation?.note !== '' && entityData?.deprecation?.decommissionTime !== null;

return (
<HeaderContainer>
<MainHeaderContent>
<PlatformContent>
{platformName && (
<LogoContainer>
{(!!platformLogoUrl && (
<PreviewImage preview={false} src={platformLogoUrl} alt={platformName} />
)) ||
entityLogoComponent}
</LogoContainer>
)}
<PlatformText>{platformName}</PlatformText>
{(platformLogoUrl || platformName) && <PlatformDivider />}
{typeIcon && <TypeIcon>{typeIcon}</TypeIcon>}
<PlatformText>{entityData?.entityTypeOverride || entityTypeCased}</PlatformText>
{container && (
<Link to={entityRegistry.getEntityUrl(EntityType.Container, container?.urn)}>
<PlatformDivider />
<ContainerIcon
style={{
color: ANTD_GRAY[9],
}}
/>
<ContainerText>
{entityRegistry.getDisplayName(EntityType.Container, container)}
</ContainerText>
<>
<HeaderContainer>
<MainHeaderContent>
<PlatformContent>
{platformName && (
<LogoContainer>
{(!!platformLogoUrl && (
<PreviewImage preview={false} src={platformLogoUrl} alt={platformName} />
)) ||
entityLogoComponent}
</LogoContainer>
)}
<PlatformText>{platformName}</PlatformText>
{(platformLogoUrl || platformName) && <PlatformDivider />}
{typeIcon && <TypeIcon>{typeIcon}</TypeIcon>}
<PlatformText>{entityData?.entityTypeOverride || entityTypeCased}</PlatformText>
{container && (
<Link to={entityRegistry.getEntityUrl(EntityType.Container, container?.urn)}>
<PlatformDivider />
<ContainerIcon
style={{
color: ANTD_GRAY[9],
}}
/>
<ContainerText>
{entityRegistry.getDisplayName(EntityType.Container, container)}
</ContainerText>
</Link>
)}
{entityCount && entityCount > 0 ? (
<>
<PlatformDivider />
<EntityCountText>{entityCount.toLocaleString()} entities</EntityCountText>
</>
) : null}
</PlatformContent>
<div style={{ display: 'flex', justifyContent: 'left', alignItems: 'center' }}>
<Link to={entityPath}>
<EntityTitle level={3}>{entityData?.name || ' '}</EntityTitle>
</Link>
)}
{entityCount && entityCount > 0 ? (
<>
<PlatformDivider />
<EntityCountText>{entityCount.toLocaleString()} entities</EntityCountText>
</>
) : null}
</PlatformContent>
<div style={{ display: 'flex', justifyContent: 'left', alignItems: 'center' }}>
<Link to={entityPath}>
<EntityTitle level={3}>{entityData?.name || ' '}</EntityTitle>
</Link>
{entityData?.health && (
<EntityHealthStatus
status={entityData?.health.status}
message={entityData?.health?.message || undefined}
/>
)}
</div>
</MainHeaderContent>
{hasExternalUrl && (
<ExternalLinkButton href={externalUrl} onClick={sendAnalytics}>
View in {platformName}
</ExternalLinkButton>
)}
<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>
</HeaderContainer>
{entityData?.deprecation?.deprecated && (
<Popover
overlayStyle={{ maxWidth: 240 }}
placement="right"
content={
hasDetails ? (
<>
{entityData?.deprecation?.note !== '' && (
<Typography.Text>{entityData?.deprecation?.note}</Typography.Text>
)}
{isDividerNeeded && <Divider />}
{entityData?.deprecation?.decommissionTime !== null && (
<Typography.Text type="secondary">
<Tooltip placement="right" title={decommissionTimeGMT}>
<LastEvaluatedAtLabel>
{decommissionTimeLocal}
</LastEvaluatedAtLabel>
</Tooltip>
</Typography.Text>
)}
</>
) : (
'No additional details'
)
}
>
<DeprecatedContainer>
<InfoCircleOutlined />
<DeprecatedText>Deprecated</DeprecatedText>
</DeprecatedContainer>
</Popover>
)}
{entityData?.health && (
<EntityHealthStatus
status={entityData?.health.status}
message={entityData?.health?.message || undefined}
/>
)}
</div>
</MainHeaderContent>
{hasExternalUrl && (
<ExternalLinkButton href={externalUrl} onClick={sendAnalytics}>
View in {platformName}
</ExternalLinkButton>
)}
<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>
<Dropdown overlay={menu} trigger={['click']}>
<MenuIcon />
</Dropdown>
</HeaderContainer>
<AddDeprecationDetailsModal
visible={showAddDeprecationDetailsModal}
urn={urn}
onClose={() => {
setShowAddDeprecationDetailsModal(false);
}}
refetch={refetch}
/>
</>
);
};

0 comments on commit e572af6

Please sign in to comment.