Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/ControlPlane/FluxList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useApiResource } from '../../lib/api/useApiResource';
import { FluxRequest } from '../../lib/api/types/flux/listGitRepo';
import { FluxKustomization, KustomizationsResponse } from '../../lib/api/types/flux/listKustomization';
import { useTranslation } from 'react-i18next';
import { timeAgo } from '../../utils/i18n/timeAgo.ts';
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo.ts';

import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
import { useMemo } from 'react';
Expand Down Expand Up @@ -148,7 +148,7 @@ export default function FluxList() {
isReady: readyObject?.status === 'True',
statusUpdateTime: readyObject?.lastTransitionTime,
revision: shortenCommitHash(item.status.artifact?.revision ?? '-'),
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
item: item,
readyMessage: readyObject?.message ?? readyObject?.reason ?? '',
};
Expand All @@ -161,7 +161,7 @@ export default function FluxList() {
name: item.metadata.name,
isReady: readyObject?.status === 'True',
statusUpdateTime: readyObject?.lastTransitionTime,
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
item: item,
readyMessage: readyObject?.message ?? readyObject?.reason ?? '',
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/ControlPlane/ManagedResources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@ui5/webcomponents-react';
import { useApiResource } from '../../lib/api/useApiResource';
import { ManagedResourcesRequest } from '../../lib/api/types/crossplane/listManagedResources';
import { timeAgo } from '../../utils/i18n/timeAgo';
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
import IllustratedError from '../Shared/IllustratedError';
import '@ui5/webcomponents-icons/dist/sys-enter-2';
import '@ui5/webcomponents-icons/dist/sys-cancel-2';
Expand Down Expand Up @@ -125,7 +125,7 @@ export function ManagedResources() {
return {
kind: item.kind,
name: item.metadata.name,
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
synced: conditionSynced?.status === 'True',
syncedTransitionTime: conditionSynced?.lastTransitionTime ?? '',
ready: conditionReady?.status === 'True',
Expand Down
4 changes: 2 additions & 2 deletions src/components/ControlPlane/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useApiResource } from '../../lib/api/useApiResource';
import IllustratedError from '../Shared/IllustratedError';
import { ProvidersListRequest } from '../../lib/api/types/crossplane/listProviders';
import { resourcesInterval } from '../../lib/shared/constants';
import { timeAgo } from '../../utils/i18n/timeAgo';
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';

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

Expand Down Expand Up @@ -123,7 +123,7 @@ export function Providers() {
const healthy = item.status?.conditions?.find((condition) => condition.type === 'Healthy');
return {
name: item.metadata.name,
created: timeAgo.format(new Date(item.metadata.creationTimestamp)),
created: formatDateAsTimeAgo(item.metadata.creationTimestamp),
installed: installed?.status === 'True' ? 'true' : 'false',
installedTransitionTime: installed?.lastTransitionTime ?? '',
healthy: healthy?.status === 'True' ? 'true' : 'false',
Expand Down
4 changes: 2 additions & 2 deletions src/components/ControlPlane/ProvidersConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import '@ui5/webcomponents-icons/dist/sys-enter-2';
import '@ui5/webcomponents-icons/dist/sys-cancel-2';
import { useProvidersConfigResource } from '../../lib/api/useApiResource';
import { timeAgo } from '../../utils/i18n/timeAgo';
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';

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

Expand Down Expand Up @@ -46,7 +46,7 @@ export function ProvidersConfig() {
parent: provider.provider,
name: config.metadata.name,
usage: config?.status?.users ?? '0',
created: timeAgo.format(new Date(config.metadata.creationTimestamp)),
created: formatDateAsTimeAgo(config.metadata.creationTimestamp),
resource: config,
});
});
Expand Down
6 changes: 3 additions & 3 deletions src/components/Shared/ResourceStatusCell.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ButtonDomRef, FlexBox, Icon, ResponsivePopover, Text } from '@ui5/webcomponents-react';
import { timeAgo } from '../../utils/i18n/timeAgo';
import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
import { useRef, useState } from 'react';
import { AnimatedHoverTextButton } from '../Helper/AnimatedHoverTextButton.tsx';
import PopoverPlacement from '@ui5/webcomponents/dist/types/PopoverPlacement.js';
Expand Down Expand Up @@ -36,7 +36,7 @@ export const ResourceStatusCell = ({
design={isOk ? 'Positive' : 'Negative'}
name={isOk ? 'sys-enter-2' : 'sys-cancel-2'}
showTooltip={true}
accessibleName={transitionTime ? timeAgo.format(new Date(transitionTime)) : '-'}
accessibleName={transitionTime ? formatDateAsTimeAgo(transitionTime) : '-'}
/>
}
text={isOk ? positiveText : negativeText}
Expand Down Expand Up @@ -64,7 +64,7 @@ export const ResourceStatusCell = ({
color: isOk ? 'var(--sapPositiveTextColor)' : 'var(--sapNegativeTextColor)',
}}
>
{timeAgo.format(new Date(transitionTime))}
{formatDateAsTimeAgo(transitionTime)}
</Text>
</FlexBox>
</ResponsivePopover>
Expand Down
44 changes: 44 additions & 0 deletions src/utils/i18n/timeAgo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
import { formatDateAsTimeAgo } from './timeAgo.ts';

const MOCK_DATE = '2025-08-11T12:00:00.000Z';

describe('formatDateAsTimeAgo', () => {
beforeAll(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(MOCK_DATE));
});

afterAll(() => {
vi.useRealTimers();
});

it('should format a recent ISO date string correctly', () => {
const date = '2025-08-11T11:59:00.000Z';
expect(formatDateAsTimeAgo(date)).toBe('1 minute ago');
});

it('should format a date string from a few hours ago', () => {
const date = '2025-08-11T09:00:00.000Z';
expect(formatDateAsTimeAgo(date)).toBe('3 hours ago');
});

it('should return an empty string for an invalid date string', () => {
const invalidDate = '[INVALID DATE]';
expect(formatDateAsTimeAgo(invalidDate)).toBe('');
});

it('should return an empty string for an empty input string', () => {
expect(formatDateAsTimeAgo('')).toBe('');
});

it('should return an empty string for null input', () => {
// @ts-expect-error: Ensuring the function is robust against non-string runtime values
expect(formatDateAsTimeAgo(null)).toBe('');
});

it('should return an empty string for an undefined input', () => {
// @ts-expect-error: Ensuring the function is robust against non-string runtime values
expect(formatDateAsTimeAgo(undefined)).toBe('');
});
});
11 changes: 10 additions & 1 deletion src/utils/i18n/timeAgo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ import en from 'javascript-time-ago/locale/en';
import TimeAgo from 'javascript-time-ago';

TimeAgo.addDefaultLocale(en);
export const timeAgo = new TimeAgo('en-US');
const timeAgo = new TimeAgo('en-US');

export function formatDateAsTimeAgo(dateString: string) {
const date = new Date(dateString);

if (!dateString || isNaN(date.getTime())) {
return '';
}
return timeAgo.format(date);
}
Loading