feat: add Logs tab to entity resource panel - #59
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Logs feature to the UI by introducing a LogsPanel and wiring it into the existing resource tabs, plus the supporting API dispatch + Zustand store actions + types and tests.
Changes:
- Added log domain types, API dispatch helpers, and store actions for fetching logs and reading/updating logs configuration.
- Implemented
LogsPanelUI with filtering, polling, JSON export, and a lazy-loaded configuration editor. - Integrated a new “Logs” tab into
EntityResourceTabsand added extensive Vitest coverage (including a Blob.text polyfill for jsdom).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/setup.ts | Adds a Blob.text polyfill to support download-related tests in jsdom. |
| src/lib/types.ts | Re-exports log-related types from a new local log-types.ts module. |
| src/lib/store.ts | Adds Zustand actions for fetching logs and getting/updating logs configuration. |
| src/lib/log-types.ts | Introduces local TS types mirroring the gateway /logs payloads. |
| src/lib/api-dispatch.ts | Adds per-entity-type dispatchers for logs and logs configuration endpoints. |
| src/lib/api-dispatch.test.ts | Adds tests validating the new logs dispatch functions and signal passthrough. |
| src/components/LogsPanel.tsx | Implements the Logs UI (table, filters, polling, export, and config editor). |
| src/components/LogsPanel.test.tsx | Adds comprehensive component tests for the Logs UI behavior. |
| src/components/EntityResourceTabs.tsx | Adds “Logs” as a new resource tab and renders LogsPanel when active. |
Adds a "Logs" tab to EntityResourceTabs for viewing ROS log entries per entity with severity/context filtering, auto-refresh polling, JSON download, and per-entity logs configuration editing. - Generic store actions + api-dispatch helpers for /logs endpoints - Local TypeScript interfaces mirroring the gateway JSON shape - LogsPanel component: dense table, click-to-expand rows, toolbar filters, auto-refresh with visibility gating, clear/download, lazy-loaded collapsible configuration row - Aggregation header for areas and functions - Wired as the 5th tab alongside Data/Operations/Config/Faults Closes #43
Unification: - New resource-tabs.tsx module exports RESOURCE_TABS config and renderResourceTabContent helper as the single source of truth for Data / Operations / Configurations / Faults / Logs tabs. - AppsPanel, FunctionsPanel, and EntityDetailPanel component view merge RESOURCE_TABS into their flat tab bars so the Logs tab now appears on all four entity types, not just Areas. LogsPanel polish: - Table caps at 60vh with a scrollable body and sticky column header so the page no longer grows unboundedly. - 404 responses render the same "Logs not available" state as 503, distinguished by message (entity-specific vs gateway-wide). - Windows-safe download filename (colons/dots replaced with hyphens). - Config Save button disabled until the initial GET succeeds; a "Failed to load configuration" state with Retry replaces the silent default-form bug where the user could overwrite server config with defaults. - Row expansion is keyboard-accessible (role, tabIndex, onKeyDown, aria-expanded). - Config-row state resets on entity change so navigating between entities no longer leaks cached config. Tests: +5 new LogsPanel tests covering the review findings.
- formatTime now normalizes ISO 8601 strings to millisecond precision
before parsing, so nanosecond-precision gateway timestamps render
consistently across browsers. On parse failure it returns a
placeholder ('--:--:--.---') instead of the full ISO string, which
would otherwise overflow the fixed-width Time column.
- getLogsConfiguration in the store now wraps the GET in try/catch,
matching the defensive pattern used by fetchEntityLogs, so thrown
exceptions become null returns + console.error instead of
unhandled rejections surfacing in event handlers.
- Adds a test asserting nanosecond timestamps render as HH:MM:SS.sss.
When the user navigates between entities of the same type (e.g.
Component A -> Component B), the panel components stay mounted and
their tab state persists. This caused the resource bar to show
whatever tab was active on the previous entity.
Fix:
- EntityDetailPanel: resets activeTab to 'data' on selectedEntity.id
change (covers the inline component-view tab bar).
- EntityResourceTabs: resets activeTab + loadedTabs + data/ops/faults
on entityId/entityType change (covers the Areas resource sub-bar).
- AppsPanel, FunctionsPanel, AreasPanel: now receive key={entity.id}
from the parent so React remounts them on entity change, which
resets all internal state (activeTab, loaded data, etc.).
- loadConfig now captures the entity identity at call time via a ref and discards the result if the entity changed while the GET was in-flight, preventing stale config from being applied to the wrong entity. - Renames resource-tabs.tsx to ResourceTabs.tsx to match the PascalCase naming convention used by every other component module in the project. Updates all four import sites.
mfaferek93
reviewed
Apr 12, 2026
1. Test spy leak (mfaferek93 line 646): added vi.restoreAllMocks() to afterEach so document.createElement and URL spies are cleaned up automatically between tests. 2. 10k-row rendering jank (mfaferek93 line 405): client-side display cap at 200 rows. When filteredEntries exceeds the cap, a "Show all N entries" button appears at the bottom of the table. No new dependencies. showAllEntries state resets on entity change. 3. Toolbar filters not reset on entity change (mfaferek93 line 159): the entity-change useEffect now also resets severity, contextDraft, contextFilter, messageSearch, expandedIds, and showAllEntries. Previously only config-row state was reset, so switching entities could leave stale node-FQN filters active on the new entity.
mfaferek93
self-requested a review
April 12, 2026 10:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a "Logs" tab to the entity resource panel for viewing ROS log entries per entity with severity/context filtering, auto-refresh polling, JSON export, and per-entity logs configuration editing.
Highlights:
LogsPanelcomponent with dense table (Time / Severity / Node / Message), click-to-expand rows revealing source location (context.function,context.file:line, full timestamp).severity_filterandmax_entriesfields validated against gateway bounds (1..10000), persisted viaPUT /logs/configuration.x-medkit.aggregation_leveland source count.document.visibilityState !== 'visible'.fetchEntityLogs,getLogsConfiguration,updateLogsConfigurationand api-dispatch helpers for per-entity-type paths.Issue
Type
Testing
Manual verification steps for reviewers:
max_entries, save, and verify the table reflects the new cap on the next refresh.Checklist
npm run lint)npm run build)