Skip to content

Commit 73bebdd

Browse files
committed
Replace calls to timeAgo with formatDateAsTimeAgo
1 parent 8e17251 commit 73bebdd

File tree

7 files changed

+66
-13
lines changed

7 files changed

+66
-13
lines changed

src/components/ControlPlane/FluxList.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useApiResource } from '../../lib/api/useApiResource';
55
import { FluxRequest } from '../../lib/api/types/flux/listGitRepo';
66
import { FluxKustomization, KustomizationsResponse } from '../../lib/api/types/flux/listKustomization';
77
import { useTranslation } from 'react-i18next';
8-
import { timeAgo } from '../../utils/i18n/timeAgo.ts';
8+
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo.ts';
99

1010
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1111
import { useMemo } from 'react';
@@ -148,7 +148,7 @@ export default function FluxList() {
148148
isReady: readyObject?.status === 'True',
149149
statusUpdateTime: readyObject?.lastTransitionTime,
150150
revision: shortenCommitHash(item.status.artifact?.revision ?? '-'),
151-
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
151+
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
152152
item: item,
153153
readyMessage: readyObject?.message ?? readyObject?.reason ?? '',
154154
};
@@ -161,7 +161,7 @@ export default function FluxList() {
161161
name: item.metadata.name,
162162
isReady: readyObject?.status === 'True',
163163
statusUpdateTime: readyObject?.lastTransitionTime,
164-
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
164+
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
165165
item: item,
166166
readyMessage: readyObject?.message ?? readyObject?.reason ?? '',
167167
};

src/components/ControlPlane/ManagedResources.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from '@ui5/webcomponents-react';
88
import { useApiResource } from '../../lib/api/useApiResource';
99
import { ManagedResourcesRequest } from '../../lib/api/types/crossplane/listManagedResources';
10-
import { timeAgo } from '../../utils/i18n/timeAgo';
10+
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
1111
import IllustratedError from '../Shared/IllustratedError';
1212
import '@ui5/webcomponents-icons/dist/sys-enter-2';
1313
import '@ui5/webcomponents-icons/dist/sys-cancel-2';
@@ -125,7 +125,7 @@ export function ManagedResources() {
125125
return {
126126
kind: item.kind,
127127
name: item.metadata.name,
128-
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
128+
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
129129
synced: conditionSynced?.status === 'True',
130130
syncedTransitionTime: conditionSynced?.lastTransitionTime ?? '',
131131
ready: conditionReady?.status === 'True',

src/components/ControlPlane/Providers.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useApiResource } from '../../lib/api/useApiResource';
1111
import IllustratedError from '../Shared/IllustratedError';
1212
import { ProvidersListRequest } from '../../lib/api/types/crossplane/listProviders';
1313
import { resourcesInterval } from '../../lib/shared/constants';
14-
import { timeAgo } from '../../utils/i18n/timeAgo';
14+
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
1515

1616
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1717

@@ -123,7 +123,7 @@ export function Providers() {
123123
const healthy = item.status?.conditions?.find((condition) => condition.type === 'Healthy');
124124
return {
125125
name: item.metadata.name,
126-
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
126+
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
127127
installed: installed?.status === 'True' ? 'true' : 'false',
128128
installedTransitionTime: installed?.lastTransitionTime ?? '',
129129
healthy: healthy?.status === 'True' ? 'true' : 'false',

src/components/ControlPlane/ProvidersConfig.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
import '@ui5/webcomponents-icons/dist/sys-enter-2';
99
import '@ui5/webcomponents-icons/dist/sys-cancel-2';
1010
import { useProvidersConfigResource } from '../../lib/api/useApiResource';
11-
import { timeAgo } from '../../utils/i18n/timeAgo';
11+
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
1212

1313
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1414

@@ -46,7 +46,7 @@ export function ProvidersConfig() {
4646
parent: provider.provider,
4747
name: config.metadata.name,
4848
usage: config?.status?.users ?? '0',
49-
created: timeAgo.format(new Date(config.metadata.creationTimestamp)),
49+
created: formatDateAsTimeAgo(config.metadata.creationTimestamp),
5050
resource: config,
5151
});
5252
});

src/components/Shared/ResourceStatusCell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ButtonDomRef, FlexBox, Icon, ResponsivePopover, Text } from '@ui5/webcomponents-react';
2-
import { timeAgo } from '../../utils/i18n/timeAgo';
2+
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
33
import { useRef, useState } from 'react';
44
import { AnimatedHoverTextButton } from '../Helper/AnimatedHoverTextButton.tsx';
55
import PopoverPlacement from '@ui5/webcomponents/dist/types/PopoverPlacement.js';
@@ -36,7 +36,7 @@ export const ResourceStatusCell = ({
3636
design={isOk ? 'Positive' : 'Negative'}
3737
name={isOk ? 'sys-enter-2' : 'sys-cancel-2'}
3838
showTooltip={true}
39-
accessibleName={transitionTime ? timeAgo.format(new Date(transitionTime)) : '-'}
39+
accessibleName={transitionTime ? formatDateAsTimeAgo(transitionTime) : '-'}
4040
/>
4141
}
4242
text={isOk ? positiveText : negativeText}
@@ -64,7 +64,7 @@ export const ResourceStatusCell = ({
6464
color: isOk ? 'var(--sapPositiveTextColor)' : 'var(--sapNegativeTextColor)',
6565
}}
6666
>
67-
{timeAgo.format(new Date(transitionTime))}
67+
{formatDateAsTimeAgo(transitionTime)}
6868
</Text>
6969
</FlexBox>
7070
</ResponsivePopover>

src/utils/i18n/timeAgo.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
2+
import { formatDateAsTimeAgo } from './timeAgo.ts';
3+
4+
const MOCK_DATE = '2025-08-11T12:00:00.000Z';
5+
6+
describe('formatDateAsTimeAgo', () => {
7+
beforeAll(() => {
8+
vi.useFakeTimers();
9+
vi.setSystemTime(new Date(MOCK_DATE));
10+
});
11+
12+
afterAll(() => {
13+
vi.useRealTimers();
14+
});
15+
16+
it('should format a recent ISO date string correctly', () => {
17+
const date = '2025-08-11T11:59:00.000Z';
18+
expect(formatDateAsTimeAgo(date)).toBe('1 minute ago');
19+
});
20+
21+
it('should format a date string from a few hours ago', () => {
22+
const date = '2025-08-11T09:00:00.000Z';
23+
expect(formatDateAsTimeAgo(date)).toBe('3 hours ago');
24+
});
25+
26+
it('should return an empty string for an invalid date string', () => {
27+
const invalidDate = '[INVALID DATE]';
28+
expect(formatDateAsTimeAgo(invalidDate)).toBe('');
29+
});
30+
31+
it('should return an empty string for an empty input string', () => {
32+
expect(formatDateAsTimeAgo('')).toBe('');
33+
});
34+
35+
it('should return an empty string for null input', () => {
36+
// @ts-expect-error: Ensuring the function is robust against non-string runtime values
37+
expect(formatDateAsTimeAgo(null)).toBe('');
38+
});
39+
40+
it('should return an empty string for an undefined input', () => {
41+
// @ts-expect-error: Ensuring the function is robust against non-string runtime values
42+
expect(formatDateAsTimeAgo(undefined)).toBe('');
43+
});
44+
});

src/utils/i18n/timeAgo.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@ import en from 'javascript-time-ago/locale/en';
22
import TimeAgo from 'javascript-time-ago';
33

44
TimeAgo.addDefaultLocale(en);
5-
export const timeAgo = new TimeAgo('en-US');
5+
const timeAgo = new TimeAgo('en-US');
6+
7+
export function formatDateAsTimeAgo(dateString: string) {
8+
const date = new Date(dateString);
9+
10+
if (!dateString || isNaN(date.getTime())) {
11+
return '';
12+
}
13+
return timeAgo.format(date);
14+
}

0 commit comments

Comments
 (0)