feat: show inventory timestamps on Device Inventory tiles
Surface the same lifecycle timestamps on the Device Inventory cards as on the
detail panel. Tiles for devices placed on a canvas show their linked node's
created / last scan / last modified / last seen (correlated by ip or
ieee_address, aggregated across matches: created = oldest, others = newest).
Devices not yet on any canvas fall back to their discovered_at.
Rendered as compact relative times ("2d ago") with the full date on hover, in
a tight two-column footer so the tile keeps its original footprint.
Backend: PendingDeviceResponse gains node_created_at / node_last_scan /
node_last_modified / node_last_seen; the canvas correlation now also pulls node
timestamps in the same single query. Frontend: shared timeFormat util
(absolute + relative), reused by the detail panel.
ha-relevant: maybe
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
||||
import { render, screen, fireEvent, waitFor, within } from '@testing-library/react'
|
||||
import { PendingDevicesModal } from '../PendingDevicesModal'
|
||||
import { useCanvasStore } from '@/stores/canvasStore'
|
||||
|
||||
@@ -364,4 +364,37 @@ describe('PendingDevicesModal', () => {
|
||||
expect(screen.queryByTestId('pending-card-dev-b')).not.toBeInTheDocument()
|
||||
expect(screen.getByTestId('pending-card-dev-a')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
describe('tile timestamps', () => {
|
||||
it('shows the linked node lifecycle timestamps for on-canvas devices', async () => {
|
||||
mockPending.mockResolvedValue({
|
||||
data: [{
|
||||
...DEVICE_IP,
|
||||
canvas_count: 1,
|
||||
node_created_at: '2026-01-02T10:00:00Z',
|
||||
node_last_scan: '2026-06-01T08:30:00Z',
|
||||
node_last_modified: '2026-06-20T12:00:00Z',
|
||||
node_last_seen: '2026-06-25T09:15:00Z',
|
||||
}],
|
||||
})
|
||||
render(<PendingDevicesModal {...baseProps} />)
|
||||
const card = await screen.findByTestId('pending-card-dev-a')
|
||||
const inCard = within(card)
|
||||
expect(inCard.getByText('Created')).toBeInTheDocument()
|
||||
expect(inCard.getByText('Scan')).toBeInTheDocument()
|
||||
expect(inCard.getByText('Modified')).toBeInTheDocument()
|
||||
expect(inCard.getByText('Seen')).toBeInTheDocument()
|
||||
// Discovered fallback is not shown once node timestamps are present.
|
||||
expect(inCard.queryByText('Discovered')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('falls back to Discovered for devices not on any canvas', async () => {
|
||||
mockPending.mockResolvedValue({ data: [DEVICE_IP] })
|
||||
render(<PendingDevicesModal {...baseProps} />)
|
||||
const card = await screen.findByTestId('pending-card-dev-a')
|
||||
const inCard = within(card)
|
||||
expect(inCard.getByText('Discovered')).toBeInTheDocument()
|
||||
expect(inCard.queryByText('Created')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user