diff --git a/backend/app/services/task.py b/backend/app/services/task.py
index c7847f9..e55cbe9 100644
--- a/backend/app/services/task.py
+++ b/backend/app/services/task.py
@@ -164,6 +164,32 @@ async def _run_on_device(
tr.data = action_data or None
tr.duration_ms = duration_ms
+ # For ping action: update device status, last_seen, last_ping_ms
+ if action.name == "ping":
+ new_status = "online" if action_result_success else "offline"
+ async with AsyncSessionLocal() as db:
+ async with db.begin():
+ device_row = await db.get(Device, device.id)
+ if device_row:
+ device_row.status = new_status
+ if action_result_success:
+ device_row.last_seen = finished_at
+ ping_ms = (action_data or {}).get("ping_ms")
+ if ping_ms is not None:
+ device_row.last_ping_ms = ping_ms
+ await sse_manager.publish(SSEEvent(
+ type="device_status",
+ task_id="__monitor__",
+ data={
+ "device_id": device.id,
+ "object_id": device.object_id,
+ "ip": device.ip,
+ "status": new_status,
+ "ping_ms": (action_data or {}).get("ping_ms") if action_result_success else None,
+ "ts": finished_at.isoformat(),
+ },
+ ))
+
await sse_manager.publish(SSEEvent(
type="task_result",
task_id=task_id,
diff --git a/frontend/src/pages/DeviceDetail.tsx b/frontend/src/pages/DeviceDetail.tsx
index 39c835b..7159e53 100644
--- a/frontend/src/pages/DeviceDetail.tsx
+++ b/frontend/src/pages/DeviceDetail.tsx
@@ -31,6 +31,7 @@ import { useNavigate, useParams } from 'react-router-dom'
import { useAlerts, useAcknowledgeAlert, useResolveAlert, type AlertItem } from '../api/alerts'
import { useDevice, useDeleteDevice, useObject } from '../api/objects'
import { getDeviceWebUrl } from '../utils/deviceUrl'
+import { elapsedAgo } from '../utils/time'
import { useSnapshots, useSnapshotDiff } from '../api/snapshots'
import { useDeviceTasks, useTask, type Task } from '../api/tasks'
import { DeviceEditModal } from '../components/DeviceEditModal'
@@ -463,10 +464,11 @@ export function DeviceDetailPage() {
{device.last_seen ? (
-
-
+
+
- {effectivePing !== null ? `${effectivePing}мс` : '—'}
+ {dayjs(device.last_seen).format('DD.MM.YYYY HH:mm:ss')}
+ {effectivePing !== null && {effectivePing}мс}
) : '—'}
diff --git a/frontend/src/pages/WorkObjectDetail.tsx b/frontend/src/pages/WorkObjectDetail.tsx
index b979a04..cc3cc33 100644
--- a/frontend/src/pages/WorkObjectDetail.tsx
+++ b/frontend/src/pages/WorkObjectDetail.tsx
@@ -31,6 +31,7 @@ import { useDevices, useObject, type DeviceItem } from '../api/objects'
import { useRacks } from '../api/racks'
import { useActions, useCreateTask, useTask } from '../api/tasks'
import { CategoryTag, DeviceStatusBadge } from '../components/StatusBadge'
+import { elapsedAgo } from '../utils/time'
import dayjs from 'dayjs'
function PingButton({
@@ -256,8 +257,11 @@ export function WorkObjectDetailPage() {
if (row._type === 'group') return null
const d = row as DeviceRow
return d.last_seen ? (
-
- {d.last_ping_ms}ms
+
+
+ {dayjs(d.last_seen).format('DD.MM.YYYY HH:mm:ss')}
+ {d.last_ping_ms != null ? {d.last_ping_ms}мс : null}
+
) : '—'
},
diff --git a/frontend/src/utils/time.ts b/frontend/src/utils/time.ts
new file mode 100644
index 0000000..a4a6218
--- /dev/null
+++ b/frontend/src/utils/time.ts
@@ -0,0 +1,17 @@
+/**
+ * Возвращает строку «сколько времени прошло» с момента date до сейчас.
+ * < 60 сек → «N сек назад»
+ * < 60 мин → «N мин назад»
+ * < 24 ч → «N ч назад»
+ * иначе → «N д назад»
+ */
+export function elapsedAgo(date: string | Date): string {
+ const diffMs = Date.now() - new Date(date).getTime()
+ const secs = Math.floor(diffMs / 1000)
+ if (secs < 60) return `${secs} сек назад`
+ const mins = Math.floor(secs / 60)
+ if (mins < 60) return `${mins} мин назад`
+ const hours = Math.floor(mins / 60)
+ if (hours < 24) return `${hours} ч назад`
+ return `${Math.floor(hours / 24)} д назад`
+}