feat(alerts/ui): add formatRelativeTime helper
Formats ISO timestamps as `Nm ago` / `Nh ago` / `Nd ago`, falling back to an absolute locale date string for values older than 30 days. Used by the alert DataTable Age column. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
32
ui/src/pages/Alerts/time-utils.test.ts
Normal file
32
ui/src/pages/Alerts/time-utils.test.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { formatRelativeTime } from './time-utils';
|
||||||
|
|
||||||
|
const NOW = new Date('2026-04-21T12:00:00Z');
|
||||||
|
|
||||||
|
describe('formatRelativeTime', () => {
|
||||||
|
it('returns "just now" for < 30s', () => {
|
||||||
|
expect(formatRelativeTime('2026-04-21T11:59:50Z', NOW)).toBe('just now');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns minutes for < 60m', () => {
|
||||||
|
expect(formatRelativeTime('2026-04-21T11:57:00Z', NOW)).toBe('3m ago');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns hours for < 24h', () => {
|
||||||
|
expect(formatRelativeTime('2026-04-21T10:00:00Z', NOW)).toBe('2h ago');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns days for < 30d', () => {
|
||||||
|
expect(formatRelativeTime('2026-04-18T12:00:00Z', NOW)).toBe('3d ago');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns locale date string for older than 30d', () => {
|
||||||
|
const out = formatRelativeTime('2025-01-01T00:00:00Z', NOW);
|
||||||
|
expect(out).not.toMatch(/ago$/);
|
||||||
|
expect(out.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles future timestamps by clamping to "just now"', () => {
|
||||||
|
expect(formatRelativeTime('2026-04-21T12:00:30Z', NOW)).toBe('just now');
|
||||||
|
});
|
||||||
|
});
|
||||||
12
ui/src/pages/Alerts/time-utils.ts
Normal file
12
ui/src/pages/Alerts/time-utils.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export function formatRelativeTime(iso: string, now: Date = new Date()): string {
|
||||||
|
const then = new Date(iso).getTime();
|
||||||
|
const diffSec = Math.max(0, Math.floor((now.getTime() - then) / 1000));
|
||||||
|
if (diffSec < 30) return 'just now';
|
||||||
|
if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
|
||||||
|
if (diffSec < 86_400) return `${Math.floor(diffSec / 3600)}h ago`;
|
||||||
|
const diffDays = Math.floor(diffSec / 86_400);
|
||||||
|
if (diffDays < 30) return `${diffDays}d ago`;
|
||||||
|
return new Date(iso).toLocaleDateString('en-GB', {
|
||||||
|
year: 'numeric', month: 'short', day: '2-digit',
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user