From 923f90f1cb7cf2d609a62adee3d34c19d50fd69a Mon Sep 17 00:00:00 2001 From: Hernan Date: Thu, 15 Oct 2026 15:16:27 -0300 Subject: [PATCH] fix(ui): ghost sentinel timestamps read as current, not 1970 The Key Values widget, the robot header "Last seen" and the fleet chip tooltip formatted the ghost sentinel (ts = 12) with moment, so ghost robots showed "57 years ago". Treat the sentinel as "now" in those three places, matching the existing handling in lib/status.js. Co-Authored-By: Claude Fable 5.1 --- .../client/oro/fleetWidgets/FleetStatusWidget/index.js | 4 ++-- .../CustomDataWidget/DataType/KeyValueComponent.js | 6 ++++-- .../RobotInfoButtons/RobotInfoButtonsComponent.js | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/imports/client/oro/fleetWidgets/FleetStatusWidget/index.js b/app/imports/client/oro/fleetWidgets/FleetStatusWidget/index.js index 68e6fa40..c8425534 100644 --- a/app/imports/client/oro/fleetWidgets/FleetStatusWidget/index.js +++ b/app/imports/client/oro/fleetWidgets/FleetStatusWidget/index.js @@ -29,7 +29,7 @@ import useRobotsWithStatus from '../../hooks/useRobotsWithStatus'; import { useMethod } from '../../util/meteorUtils'; import FleetStatusComponent from './FleetStatusComponent'; import { UI_AGG_STATUS } from '../fleetFilteringUtil'; -import { AGG_STATUSES_KEY } from '../../../../shared/constants'; +import { AGG_STATUSES_KEY, HACK_GHOST_UPDATE_STAMP_VALUE } from '../../../../shared/constants'; import { useWidgetData } from '../../contexts/WidgetDataContext'; const FleetStatusWidgetContainer = ({ @@ -91,7 +91,7 @@ const FleetStatusWidgetContainer = ({ const st = status?.[attributeId]; if (st?.message) { let { message } = st; - if (st.ts) { + if (st.ts && st.ts !== HACK_GHOST_UPDATE_STAMP_VALUE) { // ghosts: no age, see lib/status.js message += ` (Last reported ${moment(st.ts).fromNow()})`; } return message; diff --git a/app/imports/client/oro/robotWidgets/CustomDataWidget/DataType/KeyValueComponent.js b/app/imports/client/oro/robotWidgets/CustomDataWidget/DataType/KeyValueComponent.js index b0c0e771..c4979a23 100644 --- a/app/imports/client/oro/robotWidgets/CustomDataWidget/DataType/KeyValueComponent.js +++ b/app/imports/client/oro/robotWidgets/CustomDataWidget/DataType/KeyValueComponent.js @@ -29,6 +29,7 @@ import { Table, TableHead, TableSortLabel } from '@mui/material'; import { makeStyles } from 'tss-react/mui'; import { AttributeValueFormatter } from '../../../../../shared/attributes'; import { UNDEFINED_VALUE } from '../../../../../lib/util'; +import { HACK_GHOST_UPDATE_STAMP_VALUE } from '../../../../../shared/constants'; import { StyledTableBody, StyledTableCell, @@ -92,8 +93,9 @@ const KeyValueComponent = ({ customData, staleIndicator = true, now }) => { let { ts } = kv; const value = kv.value !== undefined ? formatter(kv.value) : UNDEFINED_VALUE; // HACK: Timestamps may come a few secs/mins in the future due to clock differences. - if (ts > now) ts = now; - const isStale = staleIndicator && now - kv.ts > MAX_DATA_AGE; + // Ghost robots carry the sentinel timestamp, which must read as current (see lib/status.js). + if (ts > now || ts === HACK_GHOST_UPDATE_STAMP_VALUE) ts = now; + const isStale = staleIndicator && now - ts > MAX_DATA_AGE; return ( { * Returns a string indicating the elapsed time since the robot's * last connection. */ - const getLastSeenInterval = () => 'Last seen ' + moment(updateStamp).fromNow(); + const getLastSeenInterval = () => (updateStamp === HACK_GHOST_UPDATE_STAMP_VALUE + ? 'Last seen just now' // HACK: ghost robots, see lib/status.js + : 'Last seen ' + moment(updateStamp).fromNow()); /** * Returns a string indicating the last seen time for a robot in