Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate an entity #4633

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>
jjoyce0510 marked this conversation as resolved.
Show resolved Hide resolved
<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}
/>
</>
);
};