diff --git a/frontend/src/pages/HomeObjectDetail.tsx b/frontend/src/pages/HomeObjectDetail.tsx
index 0535423..f306c34 100644
--- a/frontend/src/pages/HomeObjectDetail.tsx
+++ b/frontend/src/pages/HomeObjectDetail.tsx
@@ -1,6 +1,6 @@
import { ArrowLeftOutlined } from '@ant-design/icons'
import { useQueryClient } from '@tanstack/react-query'
-import { Alert, Button, Card, Col, Empty, Row, Select, Space, Table, Tag, Typography } from 'antd'
+import { Alert, Button, Card, Col, Collapse, Empty, Row, Select, Space, Table, Tag, Typography } from 'antd'
import type { ColumnsType } from 'antd/es/table'
import dayjs from 'dayjs'
import { useMemo, useState } from 'react'
@@ -56,13 +56,6 @@ function healthColor(score: number | null | undefined): string {
return 'red'
}
-function formatAge(seconds: number | null | undefined): string {
- if (seconds == null) return '-'
- if (seconds < 60) return `${seconds}с`
- if (seconds < 3600) return `${Math.floor(seconds / 60)}м`
- return `${Math.floor(seconds / 3600)}ч ${Math.floor((seconds % 3600) / 60)}м`
-}
-
function uptimeColor(point: DashboardAvailabilityPoint): string {
if (point.samples === 0) return '#d9d9d9'
if (point.offline > 0) return '#ff4d4f'
@@ -92,21 +85,6 @@ function UptimeBars({ points, compact = false }: { points: DashboardAvailability
/>
))}
-
-
- {dayjs(points[0].bucket_start).format('DD.MM HH:mm')}
-
-
- {dayjs(points[points.length - 1].bucket_end).format('DD.MM HH:mm')}
-
-
- {!compact ? (
-
- online
- offline
- нет данных
-
- ) : null}
)
}
@@ -148,15 +126,14 @@ export function HomeObjectDetailPage() {
const grouped = useMemo(() => {
const rows = devices?.devices ?? []
let online = 0
- let warn = 0
+ let offline = 0
+ let unknown = 0
const byRack = new Map()
for (const row of rows) {
if (row.ping_status === 'online') online += 1
-
- if (row.disk_status === 'warn' || row.disk_status === 'failed') {
- warn += 1
- }
+ if (row.ping_status === 'offline') offline += 1
+ if (row.ping_status === 'unknown') unknown += 1
const rackName = row.rack_name && row.rack_name.trim() ? row.rack_name : 'Без стойки'
if (!byRack.has(rackName)) byRack.set(rackName, [])
@@ -165,35 +142,35 @@ export function HomeObjectDetailPage() {
const rackGroups = Array.from(byRack.entries())
.sort((a, b) => {
- if (a[0] === 'Без стойки') return 1
- if (b[0] === 'Без стойки') return -1
+ if (a[0] === 'Без стойки') return -1
+ if (b[0] === 'Без стойки') return 1
return a[0].localeCompare(b[0])
})
.map(([rack, rowsInRack]) => ({
rack,
rows: [...rowsInRack].sort((a, b) => {
- const aProblem = a.has_problem ? 0 : 1
- const bProblem = b.has_problem ? 0 : 1
- if (aProblem !== bProblem) return aProblem - bProblem
if (a.ping_status !== b.ping_status) {
if (a.ping_status === 'offline') return -1
if (b.ping_status === 'offline') return 1
+ if (a.ping_status === 'unknown') return -1
+ if (b.ping_status === 'unknown') return 1
}
return a.ip.localeCompare(b.ip)
}),
}))
- return { online, warn, rackGroups }
+ return { online, offline, unknown, rackGroups }
}, [devices])
const columns: ColumnsType = [
{
title: 'IP / Hostname',
key: 'identity',
+ width: 190,
render: (_, row) => (
-
+
{row.ip}
-
+
{row.hostname ?? '—'}
@@ -203,18 +180,24 @@ export function HomeObjectDetailPage() {
title: 'Категория',
dataIndex: 'category',
key: 'category',
- width: 130,
+ width: 100,
+ ellipsis: true,
+ },
+ {
+ title: 'Роль',
+ key: 'role',
+ width: 90,
+ render: (_, row) => row.role ?? '—',
},
{
title: 'Ping',
key: 'ping',
- width: 190,
+ width: 140,
render: (_, row) => (
{row.ping_status}
- {row.ping_checked_at ? dayjs(row.ping_checked_at).format('DD.MM HH:mm:ss') : '—'}
- {row.last_ping_ms != null ? ` • ${row.last_ping_ms}ms` : ''}
+ {row.last_ping_ms != null ? `${row.last_ping_ms}ms` : '—'}
),
@@ -222,7 +205,7 @@ export function HomeObjectDetailPage() {
{
title: 'Uptime',
key: 'uptime',
- width: 280,
+ width: 220,
render: (_, row) => {
const availability = availabilityByDevice.get(Number(row.device_id))
@@ -243,32 +226,6 @@ export function HomeObjectDetailPage() {
)
},
},
- {
- title: 'Свежесть',
- key: 'freshness',
- width: 110,
- render: (_, row) => {formatAge(row.ping_age_sec)},
- },
- {
- title: 'Disk',
- key: 'disk',
- width: 140,
- render: (_, row) =>
- row.disk_status ? (
-
- {row.disk_status}
- {row.disk_max_pct != null ? ` (${row.disk_max_pct}%)` : ''}
-
- ) : (
- '—'
- ),
- },
- {
- title: 'Проблема',
- key: 'problem',
- width: 100,
- render: (_, row) => (row.has_problem ? Да : Нет),
- },
]
return (
@@ -319,9 +276,17 @@ export function HomeObjectDetailPage() {
- Проблемных
+ Unknown
- {summary?.totals.problem_devices ?? 0}
+ {grouped.unknown}
+
+
+
+
+
+ Offline
+
+ {grouped.offline}
@@ -333,14 +298,6 @@ export function HomeObjectDetailPage() {
-
-
- Disk warn/failed
-
- {grouped.warn}
-
-
-
) : (
-
- {grouped.rackGroups.map((rackGroup) => (
-
+ ({
+ key: rackGroup.rack,
+ label: `${rackGroup.rack} (${rackGroup.rows.length})`,
+ children: (
loading={devicesLoading}
dataSource={rackGroup.rows}
rowKey="device_id"
size="small"
pagination={false}
- scroll={{ x: 1300 }}
columns={columns}
/>
-
- ))}
-
+ ),
+ }))}
+ />
)}
)
}
-