Enhance status messages and layout in HostsManagerApp; improve error handling and testing for status updates
This commit is contained in:
parent
02423fe4f2
commit
d477328bea
3 changed files with 56 additions and 20 deletions
|
@ -58,6 +58,8 @@
|
||||||
- ✅ **Safe file operations**: Atomic file writing with rollback capability
|
- ✅ **Safe file operations**: Atomic file writing with rollback capability
|
||||||
- ✅ **Manager module**: Complete HostsManager class for edit operations
|
- ✅ **Manager module**: Complete HostsManager class for edit operations
|
||||||
- ✅ **Error handling**: Comprehensive error handling with user feedback
|
- ✅ **Error handling**: Comprehensive error handling with user feedback
|
||||||
|
- ✅ **Enhanced read-only error messages**: Clear, informative error messages with ❌ indicator and helpful instructions
|
||||||
|
- ✅ **Status bar positioning**: Fixed status bar layout to appear above footer for maximum visibility
|
||||||
- ✅ **Keyboard shortcuts**: All edit mode shortcuts implemented and tested
|
- ✅ **Keyboard shortcuts**: All edit mode shortcuts implemented and tested
|
||||||
- ✅ **Live testing**: Manual testing confirms all functionality works correctly
|
- ✅ **Live testing**: Manual testing confirms all functionality works correctly
|
||||||
- ✅ **Human-readable formatting**: Tab-based column alignment with proper spacing
|
- ✅ **Human-readable formatting**: Tab-based column alignment with proper spacing
|
||||||
|
@ -109,7 +111,8 @@
|
||||||
5. ✅ **Entry reordering**: Move entries up/down with Ctrl+Up/Down keyboard shortcuts
|
5. ✅ **Entry reordering**: Move entries up/down with Ctrl+Up/Down keyboard shortcuts
|
||||||
6. ✅ **Manager module**: Complete HostsManager class for all edit operations
|
6. ✅ **Manager module**: Complete HostsManager class for all edit operations
|
||||||
7. ✅ **Safe file operations**: Atomic file writing with rollback capability
|
7. ✅ **Safe file operations**: Atomic file writing with rollback capability
|
||||||
8. ✅ **Comprehensive testing**: 38 new tests for manager module (135 total tests)
|
8. ✅ **Enhanced error messages**: Professional read-only mode error messages with clear instructions
|
||||||
|
9. ✅ **Comprehensive testing**: 38 new tests for manager module (135 total tests)
|
||||||
|
|
||||||
### Recent Major Accomplishments
|
### Recent Major Accomplishments
|
||||||
- ✅ **Complete Phase 3 implementation**: Full edit mode foundation with permission management
|
- ✅ **Complete Phase 3 implementation**: Full edit mode foundation with permission management
|
||||||
|
|
|
@ -28,7 +28,7 @@ class HostsManagerApp(App):
|
||||||
|
|
||||||
CSS = """
|
CSS = """
|
||||||
.hosts-container {
|
.hosts-container {
|
||||||
height: 100%;
|
height: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-pane {
|
.left-pane {
|
||||||
|
@ -59,6 +59,16 @@ class HostsManagerApp(App):
|
||||||
color: $text;
|
color: $text;
|
||||||
height: 1;
|
height: 1;
|
||||||
padding: 0 1;
|
padding: 0 1;
|
||||||
|
dock: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-error {
|
||||||
|
background: $error;
|
||||||
|
color: $text;
|
||||||
|
height: 1;
|
||||||
|
padding: 0 1;
|
||||||
|
text-style: bold;
|
||||||
|
dock: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DataTable styling to match background */
|
/* DataTable styling to match background */
|
||||||
|
@ -115,20 +125,22 @@ class HostsManagerApp(App):
|
||||||
"""Create the application layout."""
|
"""Create the application layout."""
|
||||||
yield Header()
|
yield Header()
|
||||||
|
|
||||||
with Horizontal(classes="hosts-container"):
|
with Vertical():
|
||||||
left_pane = Vertical(classes="left-pane")
|
with Horizontal(classes="hosts-container"):
|
||||||
left_pane.border_title = "Hosts Entries"
|
left_pane = Vertical(classes="left-pane")
|
||||||
with left_pane:
|
left_pane.border_title = "Hosts Entries"
|
||||||
yield DataTable(id="entries-table")
|
with left_pane:
|
||||||
yield left_pane
|
yield DataTable(id="entries-table")
|
||||||
|
yield left_pane
|
||||||
|
|
||||||
right_pane = Vertical(classes="right-pane")
|
right_pane = Vertical(classes="right-pane")
|
||||||
right_pane.border_title = "Entry Details"
|
right_pane.border_title = "Entry Details"
|
||||||
with right_pane:
|
with right_pane:
|
||||||
yield Static("", id="entry-details")
|
yield Static("", id="entry-details")
|
||||||
yield right_pane
|
yield right_pane
|
||||||
|
|
||||||
|
yield Static("", classes="status-bar", id="status")
|
||||||
|
|
||||||
yield Static("", classes="status-bar", id="status")
|
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def on_ready(self) -> None:
|
def on_ready(self) -> None:
|
||||||
|
@ -367,8 +379,26 @@ class HostsManagerApp(App):
|
||||||
status_widget = self.query_one("#status", Static)
|
status_widget = self.query_one("#status", Static)
|
||||||
|
|
||||||
if message:
|
if message:
|
||||||
status_widget.update(message)
|
# Check if this is an error message (starts with ❌)
|
||||||
|
if message.startswith("❌"):
|
||||||
|
# Use error styling for error messages
|
||||||
|
status_widget.remove_class("status-bar")
|
||||||
|
status_widget.add_class("status-error")
|
||||||
|
status_widget.update(message)
|
||||||
|
# Auto-clear error message after 5 seconds
|
||||||
|
self.set_timer(5.0, lambda: self.update_status())
|
||||||
|
else:
|
||||||
|
# Use normal styling for regular messages
|
||||||
|
status_widget.remove_class("status-error")
|
||||||
|
status_widget.add_class("status-bar")
|
||||||
|
status_widget.update(message)
|
||||||
|
# Auto-clear regular message after 3 seconds
|
||||||
|
self.set_timer(3.0, lambda: self.update_status())
|
||||||
else:
|
else:
|
||||||
|
# Reset to normal status display
|
||||||
|
status_widget.remove_class("status-error")
|
||||||
|
status_widget.add_class("status-bar")
|
||||||
|
|
||||||
mode = "Edit mode" if self.edit_mode else "Read-only mode"
|
mode = "Edit mode" if self.edit_mode else "Read-only mode"
|
||||||
entry_count = len(self.hosts_file.entries)
|
entry_count = len(self.hosts_file.entries)
|
||||||
active_count = len(self.hosts_file.get_active_entries())
|
active_count = len(self.hosts_file.get_active_entries())
|
||||||
|
@ -485,7 +515,7 @@ class HostsManagerApp(App):
|
||||||
def action_toggle_entry(self) -> None:
|
def action_toggle_entry(self) -> None:
|
||||||
"""Toggle the active state of the selected entry."""
|
"""Toggle the active state of the selected entry."""
|
||||||
if not self.edit_mode:
|
if not self.edit_mode:
|
||||||
self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
|
self.update_status("❌ Cannot toggle entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.hosts_file.entries:
|
if not self.hosts_file.entries:
|
||||||
|
@ -513,7 +543,7 @@ class HostsManagerApp(App):
|
||||||
def action_move_entry_up(self) -> None:
|
def action_move_entry_up(self) -> None:
|
||||||
"""Move the selected entry up in the list."""
|
"""Move the selected entry up in the list."""
|
||||||
if not self.edit_mode:
|
if not self.edit_mode:
|
||||||
self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
|
self.update_status("❌ Cannot move entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.hosts_file.entries:
|
if not self.hosts_file.entries:
|
||||||
|
@ -544,7 +574,7 @@ class HostsManagerApp(App):
|
||||||
def action_move_entry_down(self) -> None:
|
def action_move_entry_down(self) -> None:
|
||||||
"""Move the selected entry down in the list."""
|
"""Move the selected entry down in the list."""
|
||||||
if not self.edit_mode:
|
if not self.edit_mode:
|
||||||
self.update_status("Not in edit mode - press 'Ctrl+E' to enable editing")
|
self.update_status("❌ Cannot move entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.hosts_file.entries:
|
if not self.hosts_file.entries:
|
||||||
|
@ -575,7 +605,7 @@ class HostsManagerApp(App):
|
||||||
def action_save_file(self) -> None:
|
def action_save_file(self) -> None:
|
||||||
"""Save the hosts file to disk."""
|
"""Save the hosts file to disk."""
|
||||||
if not self.edit_mode:
|
if not self.edit_mode:
|
||||||
self.update_status("Not in edit mode - no changes to save")
|
self.update_status("❌ Cannot save: Application is in read-only mode. No changes to save.")
|
||||||
return
|
return
|
||||||
|
|
||||||
success, message = self.manager.save_hosts_file(self.hosts_file)
|
success, message = self.manager.save_hosts_file(self.hosts_file)
|
||||||
|
|
|
@ -243,14 +243,17 @@ class TestHostsManagerApp:
|
||||||
|
|
||||||
app = HostsManagerApp()
|
app = HostsManagerApp()
|
||||||
|
|
||||||
# Mock the query_one method
|
# Mock the query_one method and set_timer to avoid event loop issues
|
||||||
mock_status = Mock()
|
mock_status = Mock()
|
||||||
app.query_one = Mock(return_value=mock_status)
|
app.query_one = Mock(return_value=mock_status)
|
||||||
|
app.set_timer = Mock() # Mock the timer to avoid event loop issues
|
||||||
|
|
||||||
app.update_status("Custom status message")
|
app.update_status("Custom status message")
|
||||||
|
|
||||||
# Verify status was updated with custom message
|
# Verify status was updated with custom message
|
||||||
mock_status.update.assert_called_once_with("Custom status message")
|
mock_status.update.assert_called_once_with("Custom status message")
|
||||||
|
# Verify timer was set for auto-clearing
|
||||||
|
app.set_timer.assert_called_once()
|
||||||
|
|
||||||
def test_action_reload(self):
|
def test_action_reload(self):
|
||||||
"""Test reload action."""
|
"""Test reload action."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue