fix(ui): humanize timestamps on ML entities UI (#12788)

This commit is contained in:
Hyejin Yoon 2025-04-04 15:00:25 +09:00 committed by GitHub
parent 372feeeade
commit 34928ad5f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 85 additions and 66 deletions

View File

@ -3,12 +3,12 @@ import styled from 'styled-components';
import { Space, Table, Typography } from 'antd';
import { formatDetailedDuration } from '@src/app/shared/time/timeUtils';
import { capitalize } from 'lodash';
import moment from 'moment';
import { Pill } from '@components';
import { MlHyperParam, MlMetric, DataProcessInstanceRunResultType } from '../../../../types.generated';
import { useBaseEntity } from '../../shared/EntityContext';
import { InfoItem } from '../../shared/components/styled/InfoItem';
import { GetDataProcessInstanceQuery } from '../../../../graphql/dataProcessInstance.generated';
import { Pill } from '../../../../alchemy-components/components/Pills';
import { TimestampPopover } from '../../../sharedV2/TimestampPopover';
const TabContent = styled.div`
padding: 16px;
@ -39,7 +39,7 @@ const propertyTableColumns = [
},
];
export default function MLModelSummary() {
export default function DataProcessInstanceSummary() {
const baseEntity = useBaseEntity<GetDataProcessInstanceQuery>();
const dpi = baseEntity?.dataProcessInstance;
@ -61,11 +61,7 @@ export default function MLModelSummary() {
<Typography.Title level={3}>Details</Typography.Title>
<InfoItemContainer justifyContent="left">
<InfoItem title="Created At">
<InfoItemContent>
{dpi?.properties?.created?.time
? moment(dpi.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={dpi?.properties?.created?.time} title="Created At" />
</InfoItem>
<InfoItem title="Status">
<InfoItemContent>{formatStatus(dpi?.state)}</InfoItemContent>

View File

@ -3,7 +3,6 @@ import styled from 'styled-components';
import { Space, Table, Typography } from 'antd';
import { Link } from 'react-router-dom';
import { colors } from '@src/alchemy-components/theme';
import moment from 'moment';
import { useEntityRegistry } from '../../../useEntityRegistry';
import { MlHyperParam, MlMetric, EntityType } from '../../../../types.generated';
import { useBaseEntity } from '../../shared/EntityContext';
@ -11,6 +10,7 @@ import { GetMlModelQuery } from '../../../../graphql/mlModel.generated';
import { InfoItem } from '../../shared/components/styled/InfoItem';
import { notEmpty } from '../../shared/utils';
import { Pill } from '../../../../alchemy-components/components/Pills';
import { TimestampPopover } from '../../../sharedV2/TimestampPopover';
const TabContent = styled.div`
padding: 16px;
@ -85,21 +85,13 @@ export default function MLModelSummary() {
<InfoItemContent>{model?.versionProperties?.version?.versionTag}</InfoItemContent>
</InfoItem>
<InfoItem title="Registered At">
<InfoItemContent>
{model?.properties?.created?.time
? moment(model.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={model?.properties?.created?.time} title="Registered At" />
</InfoItem>
<InfoItem title="Last Modified At">
<InfoItemContent>
{model?.properties?.lastModified?.time
? moment(model.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={model?.properties?.lastModified?.time} title="Last Modified At" />
</InfoItem>
<InfoItem title="Created By">
<InfoItemContent>{model?.properties?.created?.actor}</InfoItemContent>
<InfoItemContent>{model?.properties?.created?.actor || '-'}</InfoItemContent>
</InfoItem>
</InfoItemContainer>
<InfoItemContainer justifyContent="left">

View File

@ -3,7 +3,6 @@ import React from 'react';
import styled from 'styled-components';
import { colors } from '@src/alchemy-components/theme';
import { Pill } from '@src/alchemy-components/components/Pills';
import moment from 'moment';
import { GetMlModelGroupQuery } from '../../../../graphql/mlModelGroup.generated';
import { EntityType } from '../../../../types.generated';
import { useEntityRegistry } from '../../../useEntityRegistry';
@ -11,6 +10,7 @@ import { useBaseEntity } from '../../shared/EntityContext';
import { notEmpty } from '../../shared/utils';
import { EmptyTab } from '../../shared/components/styled/EmptyTab';
import { InfoItem } from '../../shared/components/styled/InfoItem';
import { TimestampPopover } from '../../../sharedV2/TimestampPopover';
const InfoItemContainer = styled.div<{ justifyContent }>`
display: flex;
@ -33,6 +33,7 @@ const NameLink = styled.a`
font-weight: 700;
color: inherit;
font-size: 0.9rem;
&:hover {
color: ${colors.blue[400]} !important;
}
@ -101,11 +102,7 @@ export default function MLGroupModels() {
key: 'createdAt',
width: 150,
render: (_: any, record: any) => (
<Typography.Text>
{record.properties?.createdTS?.time
? moment(record.properties.createdTS.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</Typography.Text>
<TimestampPopover timestamp={record.properties?.createdTS?.time} title="Created At" showPopover />
),
},
{
@ -159,18 +156,10 @@ export default function MLGroupModels() {
<Typography.Title level={3}>Model Group Details</Typography.Title>
<InfoItemContainer justifyContent="left">
<InfoItem title="Created At">
<InfoItemContent>
{modelGroup?.properties?.created?.time
? moment(modelGroup.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={modelGroup?.properties?.created?.time} title="Created At" />
</InfoItem>
<InfoItem title="Last Modified At">
<InfoItemContent>
{modelGroup?.properties?.lastModified?.time
? moment(modelGroup.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={modelGroup?.properties?.lastModified?.time} title="Last Modified At" />
</InfoItem>
{modelGroup?.properties?.created?.actor && (
<InfoItem title="Created By">

View File

@ -10,7 +10,7 @@ import styled from 'styled-components';
import { Space, Table, Typography } from 'antd';
import { Link } from 'react-router-dom';
import { colors } from '@src/alchemy-components/theme';
import moment from 'moment';
import { TimestampPopover } from '../../../sharedV2/TimestampPopover';
const TabContent = styled.div`
padding: 16px;
@ -87,21 +87,13 @@ export default function MLModelSummary() {
<InfoItemContent>{model?.versionProperties?.version?.versionTag}</InfoItemContent>
</InfoItem>
<InfoItem title="Registered At">
<InfoItemContent>
{model?.properties?.created?.time
? moment(model.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={model?.properties?.created?.time} title="Registered At" />
</InfoItem>
<InfoItem title="Last Modified At">
<InfoItemContent>
{model?.properties?.lastModified?.time
? moment(model.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={model?.properties?.lastModified?.time} title="Last Modified At" />
</InfoItem>
<InfoItem title="Created By">
<InfoItemContent>{model?.properties?.created?.actor}</InfoItemContent>
<InfoItemContent>{model?.properties?.created?.actor || '-'}</InfoItemContent>
</InfoItem>
</InfoItemContainer>
<InfoItemContainer justifyContent="left">

View File

@ -10,7 +10,7 @@ import React from 'react';
import styled from 'styled-components';
import { colors } from '@src/alchemy-components/theme';
import { Pill } from '@src/alchemy-components/components/Pills';
import moment from 'moment';
import { TimestampPopover } from '../../../sharedV2/TimestampPopover';
const InfoItemContainer = styled.div<{ justifyContent }>`
display: flex;
@ -104,11 +104,7 @@ export default function MLGroupModels() {
key: 'createdAt',
width: 150,
render: (_: any, record: any) => (
<Typography.Text>
{record.properties?.createdTS?.time
? moment(record.properties.createdTS.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</Typography.Text>
<TimestampPopover timestamp={record.properties?.createdTS?.time} title="Created At" showPopover />
),
},
{
@ -162,18 +158,10 @@ export default function MLGroupModels() {
<Typography.Title level={3}>Model Group Details</Typography.Title>
<InfoItemContainer justifyContent="left">
<InfoItem title="Created At">
<InfoItemContent>
{modelGroup?.properties?.created?.time
? moment(modelGroup.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={modelGroup?.properties?.created?.time} title="Created At" />
</InfoItem>
<InfoItem title="Last Modified At">
<InfoItemContent>
{modelGroup?.properties?.lastModified?.time
? moment(modelGroup.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
: '-'}
</InfoItemContent>
<TimestampPopover timestamp={modelGroup?.properties?.lastModified?.time} title="Last Modified At" />
</InfoItem>
{modelGroup?.properties?.created?.actor && (
<InfoItem title="Created By">

View File

@ -0,0 +1,63 @@
import React from 'react';
import styled from 'styled-components';
import { Popover } from '@components';
import colors from '@src/alchemy-components/theme/foundations/colors';
import { toRelativeTimeString, toLocalDateTimeString } from '@src/app/shared/time/timeUtils';
const PopoverContent = styled.div`
color: ${colors.gray[500]};
font-size: 0.8rem;
`;
const Title = styled.div`
color: ${colors.gray[500]};
border-bottom: none;
font-size: 0.8rem;
font-weight: 600;
`;
const InfoItemContent = styled.div`
padding-top: 8px;
width: 100px;
overflow-wrap: break-word;
`;
const popoverStyles = {
overlayInnerStyle: {
borderRadius: '10px',
},
overlayStyle: {
margin: '5px',
},
};
interface TimestampProps {
timestamp?: number;
title: string;
showPopover?: boolean;
}
export const TimestampPopover: React.FC<TimestampProps> = ({ timestamp, title, showPopover = true }) => {
if (!timestamp) {
return <InfoItemContent>-</InfoItemContent>;
}
const relativeTime = toRelativeTimeString(timestamp);
const absoluteTime = toLocalDateTimeString(timestamp);
if (!showPopover) {
return <InfoItemContent>{relativeTime}</InfoItemContent>;
}
return (
<Popover
content={<PopoverContent>{absoluteTime}</PopoverContent>}
title={<Title>{title}</Title>}
trigger="hover"
overlayInnerStyle={popoverStyles.overlayInnerStyle}
overlayStyle={popoverStyles.overlayStyle}
>
<InfoItemContent>{relativeTime}</InfoItemContent>
</Popover>
);
};

View File

@ -21,8 +21,7 @@ describe("models", () => {
cy.wait("@graphqlRequest");
// Ensure page has loaded by checking for specific content
cy.contains("2025-03-03").should("be.visible");
cy.contains("2025-03-04").should("be.visible");
cy.contains("ago").should("be.visible"); // relative time
cy.contains("urn:li:corpuser:datahub").should("be.visible");
// Navigate to Properties tab with verification