feat(web): live-данные — polling, онлайн-индикаторы, относительное время, actionable-очередь ошибок apply, часовая агрегация статистики
This commit is contained in:
@@ -34,6 +34,8 @@ const timeFmt = new Intl.DateTimeFormat('ru-RU', {
|
||||
second: '2-digit',
|
||||
})
|
||||
|
||||
const relativeFmt = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
|
||||
|
||||
/** Compact packet counter: 1,2K / 3,4M */
|
||||
export function formatPackets(n: number | undefined | null): string {
|
||||
if (n === undefined || n === null) return '—'
|
||||
@@ -72,3 +74,20 @@ export function formatStampDateTime(iso: string | null | undefined): string {
|
||||
export function formatTime(d: Date): string {
|
||||
return timeFmt.format(d)
|
||||
}
|
||||
|
||||
/** «2 минуты назад» / «5 часов назад»; старше 7 дней — полная дата. */
|
||||
export function formatRelativeTime(iso: string | null | undefined): string {
|
||||
if (!iso) return '—'
|
||||
const t = Date.parse(iso)
|
||||
if (Number.isNaN(t)) return '—'
|
||||
const diffMs = t - Date.now()
|
||||
const absMin = Math.round(Math.abs(diffMs) / 60_000)
|
||||
if (absMin < 1) return 'только что'
|
||||
const sign = diffMs < 0 ? -1 : 1
|
||||
if (absMin < 60) return relativeFmt.format(sign * absMin, 'minute')
|
||||
const absHours = Math.round(absMin / 60)
|
||||
if (absHours < 24) return relativeFmt.format(sign * absHours, 'hour')
|
||||
const absDays = Math.round(absHours / 24)
|
||||
if (absDays < 7) return relativeFmt.format(sign * absDays, 'day')
|
||||
return formatDateTime(iso)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user