From 04b117f904b330e118121bceb71dd12d4b6320f8 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Tue, 2 Dec 2025 16:34:53 +0000 Subject: [PATCH 1/2] feat: Improve entity selection error handling and UI feedback - Add toast notification when fetching entity details fails (e.g. timeout) - Update \`selectEntity\` store action to set a fallback entity with error state instead of clearing selection - Enhance \`EntityDetailPanel\` to display an error card while preserving the entity header context - Prevent the UI from reverting to an empty state when a single entity request fails --- src/components/EntityDetailPanel.tsx | 275 +++++++++++++-------------- src/lib/store.ts | 21 +- 2 files changed, 151 insertions(+), 145 deletions(-) diff --git a/src/components/EntityDetailPanel.tsx b/src/components/EntityDetailPanel.tsx index f7e427d..e146df6 100644 --- a/src/components/EntityDetailPanel.tsx +++ b/src/components/EntityDetailPanel.tsx @@ -129,156 +129,151 @@ export function EntityDetailPanel({ onConnectClick }: EntityDetailPanelProps) { // Entity detail view if (selectedEntity) { - // Component with topics - if (selectedEntity.type === 'component' && selectedEntity.topics && selectedEntity.topics.length > 0) { - return ( -
-
- {/* Component Header */} + const hasTopics = selectedEntity.type === 'component' && selectedEntity.topics && selectedEntity.topics.length > 0; + const hasError = !!selectedEntity.error; + + return ( +
+
+ {/* Component Header */} + + +
+
+ {selectedEntity.name} + + {selectedEntity.type} • {selectedPath} + +
+ +
+
+
+ + {/* Content */} + {hasError ? ( - -
-
- {selectedEntity.name} - - {selectedEntity.type} • {selectedPath} - -
- + +
+

Failed to load entity details

+

The server might be unreachable or the entity might not exist.

- +
+ ) : hasTopics ? ( +
+ {/* Topics List */} + {selectedEntity.topics!.map((topic: ComponentTopic) => { + const topicName = topic.topic.split('/').pop() || topic.topic; + const isExpanded = expandedTopics.has(topic.topic); + const isPublishing = publishingTopics.has(topic.topic); + const hasNoData = topic.data === null || topic.data === undefined; - {/* Topics List */} - {selectedEntity.topics.map((topic: ComponentTopic) => { - const topicName = topic.topic.split('/').pop() || topic.topic; - const isExpanded = expandedTopics.has(topic.topic); - const isPublishing = publishingTopics.has(topic.topic); - const hasNoData = topic.data === null || topic.data === undefined; - - return ( - - -
-
- -
-
- {topic.topic} - {hasNoData && ( - - No Data - - )} + return ( + + +
+
+ +
+
+ {topic.topic} + {hasNoData && ( + + No Data + + )} +
+ + {hasNoData + ? 'No messages received (topic may be inactive)' + : `Last update: ${new Date(topic.timestamp / 1000000).toLocaleString()}` + } +
- - {hasNoData - ? 'No messages received (topic may be inactive)' - : `Last update: ${new Date(topic.timestamp / 1000000).toLocaleString()}` - } -
+
- -
- - {isExpanded && ( - - {/* Latest Data */} -
-
Latest Message
- {hasNoData ? ( -
- No data available - topic exists but is not publishing messages -
- ) : ( -
-                                                        {JSON.stringify(topic.data, null, 2)}
-                                                    
- )} -
- - {/* Publish Form - only show if we have data to infer type */} - {!hasNoData && ( -
-
Publish Message
-