Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
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
1 change: 1 addition & 0 deletions web/locales/en/plugin__netobserv-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
"Navigate to alert details": "Navigate to alert details",
"State": "State",
"Severity": "Severity",
"Active since": "Active since",
"Labels": "Labels",
"Description": "Description",
"Navigate to network traffic": "Navigate to network traffic",
Expand Down
4 changes: 4 additions & 0 deletions web/src/components/health/alert-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import { Label, Text, TextContent, TextVariants } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { formatActiveSince } from '../../utils/datetime';
import { valueFormat } from '../../utils/format';
import { AlertWithRuleName, getAlertFilteredLabels, getAlertLink } from './health-helper';

Expand All @@ -25,6 +26,9 @@ export const AlertDetails: React.FC<AlertDetailsProps> = ({ resourceName, alert
</AlertDetailsValue>
<AlertDetailsValue title={t('State')}>{alert.state}</AlertDetailsValue>
<AlertDetailsValue title={t('Severity')}>{alert.labels.severity}</AlertDetailsValue>
{alert.activeAt && (
<AlertDetailsValue title={t('Active since')}>{formatActiveSince(alert.activeAt)}</AlertDetailsValue>
)}
<AlertDetailsValue title={t('Labels')}>
{labels.length === 0
? t('None')
Expand Down
2 changes: 2 additions & 0 deletions web/src/components/health/alert-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';

import { Label, Tooltip } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { formatActiveSince } from '../../utils/datetime';
import { valueFormat } from '../../utils/format';
import { HealthColorSquare } from './health-color-square';
import { AlertWithRuleName, getAlertFilteredLabels, getAlertLink, getTrafficLink } from './health-helper';
Expand Down Expand Up @@ -39,6 +40,7 @@ export const AlertRow: React.FC<AlertRowProps> = ({ kind, resourceName, alert, w
)}
<Td noPadding={!wide}>{alert.state}</Td>
<Td>{alert.labels.severity}</Td>
{alert.activeAt && <Td>{formatActiveSince(alert.activeAt)}</Td>}
<Td>
{labels.length === 0
? t('None')
Expand Down
1 change: 1 addition & 0 deletions web/src/components/health/rule-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const RuleDetails: React.FC<RuleDetailsProps> = ({ kind, info, wide }) =>
<Th>{t('Summary')}</Th>
<Th>{t('State')}</Th>
<Th>{t('Severity')}</Th>
<Th>{t('Active since')}</Th>
<Th>{t('Labels')}</Th>
<Th>{t('Value')}</Th>
<Th>{t('Description')}</Th>
Expand Down
45 changes: 45 additions & 0 deletions web/src/utils/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,48 @@ export const computeStepInterval = (range: TimeRange | number) => {
stepSeconds: step
};
};

/**
* Formats a timestamp for "Active since" display with relative or absolute time.
* - Today: Shows only time (14:23)
* - Yesterday: Shows "Yesterday, HH:MM"
* - Last 7 days: Shows day of week and time (Tue, 14:23)
* - Older: Shows full date and time (2025-11-24 14:23)
*/
export const formatActiveSince = (timestamp: string): string => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one deserves a unit test
I think there's a bug in "yesterday" : activeDate.getDate() === yesterday.getDate()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I added a unit test. It passed.

const activeDate = new Date(timestamp);
const now = new Date();

// Calculate time difference in milliseconds
const diffMs = now.getTime() - activeDate.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

// Format time as HH:MM
const timeStr = twentyFourHourTime(activeDate, false);

// Today: show only time
if (diffDays === 0 && now.getDate() === activeDate.getDate()) {
return timeStr;
}

// Yesterday: show "Yesterday, HH:MM"
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
if (
activeDate.getDate() === yesterday.getDate() &&
activeDate.getMonth() === yesterday.getMonth() &&
activeDate.getFullYear() === yesterday.getFullYear()
) {
return `Yesterday, ${timeStr}`;
}

// Last 7 days: show day of week and time
if (diffDays < 7) {
const weekdayFormatter = new Intl.DateTimeFormat(getLanguage(), { weekday: 'short' });
const weekday = weekdayFormatter.format(activeDate);
return `${weekday}, ${timeStr}`;
}

// Older: show full date and time (YYYY-MM-DD HH:MM)
return `${toISODateString(activeDate)} ${timeStr}`;
};